package edu.harvard.iq.dataverse.settings;
import edu.harvard.iq.dataverse.actionlogging.ActionLogRecord;
import edu.harvard.iq.dataverse.actionlogging.ActionLogServiceBean;
//import edu.harvard.iq.dataverse.api.ApiBlockingFilter;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
/**
* Service bean accessing a persistent hash map, used as settings in the application.
* @author michael
*/
@Stateless
@Named
public class SettingsServiceBean {
private static final Logger logger = Logger.getLogger(SettingsServiceBean.class.getCanonicalName());
/**
* Some convenient keys for the settings. Note that the setting's
* name is really a {@code String}, but it's good to have the compiler look
* over your shoulder when typing strings in various places of a large app.
* So there.
*/
public enum Key {
FooterCopyright,
FileFixityChecksumAlgorithm,
MinutesUntilConfirmEmailTokenExpires,
/**
* Override Solr highlighting "fragsize"
* https://wiki.apache.org/solr/HighlightingParameters#hl.fragsize
*/
SearchHighlightFragmentSize,
/**
* Domain name specific code for Google Analytics
*//**
* Domain name specific code for Google Analytics
*/
GoogleAnalyticsCode,
/**
* Revert to MyData *not* using the Solr "permission documents" which
* was the behavior in Dataverse 4.2. Starting to use Solr permission
* documents in MyData has been introduced in 4.2.1 as a fix for
* https://github.com/IQSS/dataverse/issues/2649 where the "File
* Downloader" role was exposing cards for unpublished datasets when it
* shouldn't.
*/
MyDataDoesNotUseSolrPermissionDocs,
/**
* Experimental: Allow non-public search with a key/token using the
* Search API. See also https://github.com/IQSS/dataverse/issues/1299
*/
SearchApiNonPublicAllowed,
/**
* Experimental: Use Solr to power the file listing on the dataset page.
*/
FilesOnDatasetPageFromSolr,
/**
* API endpoints that are not accessible. Comma separated list.
*/
BlockedApiEndpoints,
/**
* A key that, with the right {@link ApiBlockingFilter.BlockPolicy},
* allows calling blocked APIs.
*/
BlockedApiKey,
/**
* How to treat blocked APIs. One of drop, localhost-only, unblock-key
*/
BlockedApiPolicy,
/**
* For development only (see dev guide for details). Backed by an enum
* of possible account types.
*/
DebugShibAccountType,
/** Application-wide Terms of Use per installation. */
ApplicationTermsOfUse,
/** Terms of Use specific to API per installation. */
ApiTermsOfUse,
/**
* URL for the application-wide Privacy Policy per installation, linked
* to from the footer.
*/
ApplicationPrivacyPolicyUrl,
/**
* A boolean defining if indexing and search should respect the concept
* of "permission root".
*
* <p>
*
* If we ignore permissionRoot at index time, we should blindly give
* search ("discoverability") access to people and group who have access
* defined in a parent dataverse, all the way back to the root.
*
* <p>
*
* If we respect permissionRoot, this means that the dataverse being
* indexed is an island of permissions all by itself. We should not look
* to its parent to see if more people and groups might be able to
* search the DvObjects within it. We would assume no implicit
* inheritance of permissions. In this mode, all permissions must be
* explicitly defined on DvObjects. No implied inheritance.
*
*/
SearchRespectPermissionRoot,
/** Solr hostname and port, such as "localhost:8983". */
SolrHostColonPort,
/** Key for limiting the number of bytes uploaded via the Data Deposit API, UI (web site and . */
MaxFileUploadSizeInBytes,
/**
* Experimental: Key for if DDI export is enabled or disabled.
*/
DdiExportEnabled,
/** Key for if Shibboleth is enabled or disabled. */
ShibEnabled,
/** Key for if ScrubMigrationData is enabled or disabled. */
ScrubMigrationData,
/** Key for the url to send users who want to sign up to. */
SignUpUrl,
/** Key for whether we allow users to sign up */
AllowSignUp,
/** protocol for global id */
Protocol,
/** authority for global id */
Authority,
/** DoiProvider for global id */
DoiProvider,
DoiSeparator,
/* Removed for now - tried to add here but DOI Service Bean didn't like it at start-up
DoiUsername,
DoiPassword,
DoiBaseurlstring,
*/
/* TwoRavens location */
TwoRavensUrl,
/** Optionally override http://guides.dataverse.org . */
GuidesBaseUrl,
/* zip download size limit */
ZipDownloadLimit,
/* zip upload number of files limit */
ZipUploadFilesLimit,
/* the number of files the GUI user is allowed to upload in one batch,
via drag-and-drop, or through the file select dialog */
MultipleUploadFilesLimit,
/* Size limits for generating thumbnails on the fly */
/* (i.e., we'll attempt to generate a thumbnail on the fly if the
* size of the file is less than this)
*/
ThumbnailSizeLimitImage,
ThumbnailSizeLimitPDF,
/* status message that will appear on the home page */
StatusMessageHeader,
/* full text of status message, to appear in popup */
StatusMessageText,
/* return email address for system emails such as notifications */
SystemEmail,
/* whether file landing page is available
for 4.2 development */
ShowFileLandingPage,
/* size limit for Tabular data file ingests */
/* (can be set separately for specific ingestable formats; in which
case the actual stored option will be TabularIngestSizeLimit:{FORMAT_NAME}
where {FORMAT_NAME} is the format identification tag returned by the
getFormatName() method in the format-specific plugin; "sav" for the
SPSS/sav format, "RData" for R, etc.
for example: :TabularIngestSizeLimit:RData */
TabularIngestSizeLimit,
/**
Whether to allow user to create GeoConnect Maps
This boolean effects whether the user sees the map button on
the dataset page and if the ingest will create a shape file
Default is false
*/
GeoconnectCreateEditMaps,
/**
Whether to allow a user to view existing maps
This boolean effects whether a user may see the
Explore World Map Button
Default is false;
*/
GeoconnectViewMaps,
/**
For DEVELOPMENT ONLY. Generate SQL statements for populating
MapLayerMetadata objects when Geoconnect is not available.
When files have related MapLayerMetadata objects, the "Explore button
will be available to users.
*/
GeoconnectDebug,
/**
Whether to allow a user to view tabular files
using the TwoRavens application
This boolean effects whether a user may see the
Explore Button that links to TwoRavens
Default is false;
*/
TwoRavensTabularView,
/**
The message added to a popup upon dataset publish
*
*/
DatasetPublishPopupCustomText,
/*
Whether to display the publish text for every published version
*/
DatasetPublishPopupCustomTextOnAllVersions,
/*
Whether Harvesting (OAI) service is enabled
*/
OAIServerEnabled;
@Override
public String toString() {
return ":" + name();
}
}
@PersistenceContext
EntityManager em;
@EJB
ActionLogServiceBean actionLogSvc;
/**
* Values that are considered as "true".
* @see #isTrue(java.lang.String, boolean)
*/
private static final Set<String> TRUE_VALUES = Collections.unmodifiableSet(
new TreeSet<>( Arrays.asList("1","yes", "true","allow")));
/**
* Basic functionality - get the name, return the setting, or {@code null}.
* @param name of the setting
* @return the actual setting, or {@code null}.
*/
public String get( String name ) {
Setting s = em.find( Setting.class, name );
return (s!=null) ? s.getContent() : null;
}
/**
* Same as {@link #get(java.lang.String)}, but with static checking.
* @param key Enum value of the name.
* @return The setting, or {@code null}.
*/
public String getValueForKey( Key key ) {
return get(key.toString());
}
/**
* Attempt to convert the value to an integer
* - Applicable for keys such as MaxFileUploadSizeInBytes
*
* On failure (key not found or string not convertible to a long), returns null
* @param key
* @return
*/
public Long getValueForKeyAsLong(Key key){
String val = this.getValueForKey(key);
if (val == null){
return null;
}
try {
long valAsInt = Long.parseLong(val);
return valAsInt;
} catch (NumberFormatException ex) {
logger.log(Level.WARNING, "Incorrect setting. Could not convert \"{0}\" from setting {1} to long.", new Object[]{val, key.toString()});
return null;
}
}
/**
* Return the value stored, or the default value, in case no setting by that
* name exists. The main difference between this method and the other {@code get()}s
* is that is never returns null (unless {@code defaultValue} is {@code null}.
*
* @param name Name of the setting.
* @param defaultValue The value to return if no setting is found in the DB.
* @return Either the stored value, or the default value.
*/
public String get( String name, String defaultValue ) {
String val = get(name);
return (val!=null) ? val : defaultValue;
}
public String getValueForKey( Key key, String defaultValue ) {
return get( key.toString(), defaultValue );
}
public Setting set( String name, String content ) {
Setting s = new Setting( name, content );
s = em.merge(s);
actionLogSvc.log( new ActionLogRecord(ActionLogRecord.ActionType.Setting, "set")
.setInfo(name + ": " + content));
return s;
}
public Setting setValueForKey( Key key, String content ) {
return set( key.toString(), content );
}
/**
* The correct way to decide whether a string value in the
* settings table should be considered as {@code true}.
* @param name name of the setting.
* @param defaultValue logical value of {@code null}.
* @return boolean value of the setting.
*/
public boolean isTrue( String name, boolean defaultValue ) {
String val = get(name);
return ( val==null ) ? defaultValue : TRUE_VALUES.contains(val.trim().toLowerCase() );
}
public boolean isTrueForKey( Key key, boolean defaultValue ) {
return isTrue( key.toString(), defaultValue );
}
public void deleteValueForKey( Key name ) {
delete( name.toString() );
}
public void delete( String name ) {
actionLogSvc.log( new ActionLogRecord(ActionLogRecord.ActionType.Setting, "delete")
.setInfo(name));
em.createNamedQuery("Setting.deleteByName")
.setParameter("name", name)
.executeUpdate();
}
public Set<Setting> listAll() {
return new HashSet<>(em.createNamedQuery("Setting.findAll", Setting.class).getResultList());
}
}