getDisplayAdditionalMetadataTranslateFields() {
public boolean isAdvancedSearchFieldHierarchical(String field) {
return isAdvancedSearchFieldHasAttribute(field, "hierarchical");
}
-
+
/**
*
* isAdvancedSearchFieldRange.
@@ -1972,6 +1997,15 @@ public String getSmtpSenderName() {
public String getSmtpSecurity() {
return getLocalString("user.smtpSecurity", "none");
}
+
+ /**
+ *
+ * @return Configured SMTP port number; -1 if not configured
+ * @should return correct value
+ */
+ public int getSmtpPort() {
+ return getLocalInt("user.smtpPort", -1);
+ }
/**
*
@@ -2922,30 +2956,6 @@ public String getRssCopyrightText() {
return getLocalString("rss.copyright");
}
- /**
- *
- * getRulesetFilePath.
- *
- *
- * @should return correct value
- * @return a {@link java.lang.String} object.
- */
- public String getRulesetFilePath() {
- return getLocalString("content.ruleset");
- }
-
- /**
- *
- * getDefaultCollection.
- *
- *
- * @should return correct value
- * @return a {@link java.lang.String} object.
- */
- public String getDefaultCollection() {
- return getLocalString("content.defaultCollection");
- }
-
/**
*
* getThumbnailsWidth.
@@ -3420,18 +3430,6 @@ public String getWatermarkFormat() {
return getLocalString("viewer.watermarkFormat", "jpg");
}
- /**
- *
- * isOriginalContentDownload.
- *
- *
- * @should return correct value
- * @return a boolean.
- */
- public boolean isOriginalContentDownload() {
- return getLocalBoolean("content.originalContentDownload", false);
- }
-
/**
*
* getStopwordsFilePath.
@@ -4815,4 +4813,25 @@ public List getLicenseDescriptions() {
return licenses;
}
+ /**
+ * @return
+ */
+ public String getBaseXUrl() {
+ return getLocalString("urls.basex.url");
+ }
+
+ /**
+ * @return
+ */
+ public String getBaseXDatabase() {
+ return getLocalString("urls.basex.defaultDatabase");
+
+ }
+
+ /**
+ * @return
+ */
+ public HierarchicalConfiguration getBaseXMetadataConfig() {
+ return getLocalConfigurationAt("metadata.basexMetadataList");
+ }
}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/DataManager.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/DataManager.java
index 86c48e70c17..79885e5c4f3 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/DataManager.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/DataManager.java
@@ -24,6 +24,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import de.intranda.monitoring.timer.TimeAnalysis;
import io.goobi.viewer.controller.language.LanguageHelper;
import io.goobi.viewer.dao.IDAO;
import io.goobi.viewer.dao.impl.JPADAO;
@@ -77,6 +78,8 @@ public final class DataManager {
private String indexerVersion = "";
private RestApiManager restApiManager;
+
+ private TimeAnalysis timing = new TimeAnalysis();
/**
*
@@ -445,4 +448,19 @@ public void setRestApiManager(RestApiManager restApiManager) {
public RecordLockManager getRecordLockManager() {
return recordLockManager;
}
+
+ /**
+ * @return the timing
+ */
+ public TimeAnalysis getTiming() {
+ return timing;
+ }
+
+ /**
+ *
+ */
+ public void resetTiming() {
+ this.timing = new TimeAnalysis();
+
+ }
}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/JsonTools.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/JsonTools.java
index 6e5a82e82f4..fb5c3c90dd9 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/JsonTools.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/JsonTools.java
@@ -36,6 +36,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import io.goobi.viewer.controller.SolrConstants.DocType;
+import io.goobi.viewer.controller.imaging.ThumbnailHandler;
import io.goobi.viewer.exceptions.DAOException;
import io.goobi.viewer.exceptions.IndexUnreachableException;
import io.goobi.viewer.exceptions.PresentationException;
@@ -77,6 +78,7 @@ public static JSONArray getRecordJsonArray(SolrDocumentList result, Map recipients, String subject, String b
return postMail(recipients, subject, body, DataManager.getInstance().getConfiguration().getSmtpServer(),
DataManager.getInstance().getConfiguration().getSmtpUser(), DataManager.getInstance().getConfiguration().getSmtpPassword(),
DataManager.getInstance().getConfiguration().getSmtpSenderAddress(), DataManager.getInstance().getConfiguration().getSmtpSenderName(),
- DataManager.getInstance().getConfiguration().getSmtpSecurity());
+ DataManager.getInstance().getConfiguration().getSmtpSecurity(), DataManager.getInstance().getConfiguration().getSmtpPort());
}
/**
@@ -245,11 +245,12 @@ public static boolean postMail(List recipients, String subject, String b
* @param smtpSenderAddress
* @param smtpSenderName
* @param smtpSecurity
+ * @param smtpPort
* @throws MessagingException
* @throws UnsupportedEncodingException
*/
private static boolean postMail(List recipients, String subject, String body, String smtpServer, final String smtpUser,
- final String smtpPassword, String smtpSenderAddress, String smtpSenderName, String smtpSecurity)
+ final String smtpPassword, String smtpSenderAddress, String smtpSenderName, String smtpSecurity, Integer smtpPort)
throws MessagingException, UnsupportedEncodingException {
if (recipients == null) {
throw new IllegalArgumentException("recipients may not be null");
@@ -282,13 +283,15 @@ private static boolean postMail(List recipients, String subject, String
if (StringUtils.isEmpty(smtpUser)) {
auth = false;
}
- String security = DataManager.getInstance().getConfiguration().getSmtpSecurity();
Properties props = new Properties();
- switch (security.toUpperCase()) {
+ switch (smtpSecurity.toUpperCase()) {
case "STARTTLS":
logger.debug("Using STARTTLS");
+ if (smtpPort == -1) {
+ smtpPort = 25;
+ }
props.setProperty("mail.transport.protocol", "smtp");
- props.setProperty("mail.smtp.port", "25");
+ props.setProperty("mail.smtp.port", String.valueOf(smtpPort));
props.setProperty("mail.smtp.host", smtpServer);
props.setProperty("mail.smtp.ssl.trust", "*");
props.setProperty("mail.smtp.starttls.enable", "true");
@@ -296,21 +299,28 @@ private static boolean postMail(List recipients, String subject, String
break;
case "SSL":
logger.debug("Using SSL");
+ if (smtpPort == -1) {
+ smtpPort = 465;
+ }
props.setProperty("mail.transport.protocol", "smtp");
props.setProperty("mail.smtp.host", smtpServer);
- props.setProperty("mail.smtp.port", "465");
+ props.setProperty("mail.smtp.port", String.valueOf(smtpPort));
props.setProperty("mail.smtp.ssl.enable", "true");
props.setProperty("mail.smtp.ssl.trust", "*");
break;
default:
logger.debug("Using no SMTP security");
+ if (smtpPort == -1) {
+ smtpPort = 25;
+ }
props.setProperty("mail.transport.protocol", "smtp");
- props.setProperty("mail.smtp.port", "25");
+ props.setProperty("mail.smtp.port", String.valueOf(smtpPort));
props.setProperty("mail.smtp.host", smtpServer);
}
props.setProperty("mail.smtp.connectiontimeout", "15000");
props.setProperty("mail.smtp.timeout", "15000");
props.setProperty("mail.smtp.auth", String.valueOf(auth));
+ logger.debug("Connecting to email server " + smtpServer + " on port " + String.valueOf(smtpPort) + " via SMTP security " + smtpSecurity.toUpperCase());
// logger.trace(props.toString());
Session session;
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/RestApiManager.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/RestApiManager.java
index 0c4b6679693..1fded8cc3c2 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/RestApiManager.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/RestApiManager.java
@@ -86,7 +86,9 @@ public RestApiManager(Configuration config) {
* null is returned
*/
public AbstractApiUrlManager getDataApiManager() {
- this.updateDataUrlManager();
+ if(this.dataApiManager == null) {
+ this.updateDataUrlManager();
+ }
return dataApiManager;
}
@@ -121,7 +123,9 @@ public String getContentApiUrl() {
* Otherwise null is returned
*/
public AbstractApiUrlManager getContentApiManager() {
- this.updateContentUrlManager();
+ if(this.contentApiManager == null) {
+ this.updateContentUrlManager();
+ }
return contentApiManager;
}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/SolrConstants.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/SolrConstants.java
index f852e143771..3f0a7097f78 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/SolrConstants.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/SolrConstants.java
@@ -241,6 +241,8 @@ public static MetadataGroupType getByName(String name) {
public static final String SUPERFULLTEXT = "SUPERFULLTEXT";
/** Constant SUPERUGCTERMS="SUPERUGCTERMS"
*/
public static final String SUPERUGCTERMS = "SUPERUGCTERMS";
+ /** Constant TECTONICS_ID="MD_TECTONICS_ID"
*/
+ public static final String ARCHIVE_ENTRY_ID = "MD_ARCHIVE_ENTRY_ID";
/** Constant TITLE="MD_TITLE"
*/
public static final String TITLE = "MD_TITLE";
/** Constant THUMBNAIL="THUMBNAIL"
*/
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/SolrSearchIndex.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/SolrSearchIndex.java
index e5649907c82..50745623b2d 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/SolrSearchIndex.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/SolrSearchIndex.java
@@ -29,6 +29,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Random;
import java.util.Set;
import java.util.function.BinaryOperator;
import java.util.regex.Matcher;
@@ -51,8 +52,10 @@
import org.apache.solr.client.solrj.response.LukeResponse;
import org.apache.solr.client.solrj.response.LukeResponse.FieldInfo;
import org.apache.solr.client.solrj.response.QueryResponse;
+import org.apache.solr.client.solrj.response.SolrPingResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
+import org.apache.solr.common.SolrException;
import org.apache.solr.common.luke.FieldFlag;
import org.jdom2.Document;
import org.jdom2.Element;
@@ -64,6 +67,7 @@
import de.intranda.metadata.multilanguage.IMetadataValue;
import de.intranda.metadata.multilanguage.MultiLanguageMetadataValue;
import io.goobi.viewer.controller.SolrConstants.DocType;
+import io.goobi.viewer.exceptions.DAOException;
import io.goobi.viewer.exceptions.HTTPException;
import io.goobi.viewer.exceptions.IndexUnreachableException;
import io.goobi.viewer.exceptions.PresentationException;
@@ -99,6 +103,8 @@ public final class SolrSearchIndex {
Map dataRepositoryNames = new HashMap<>();
private SolrClient client;
+
+ private List solrFields = null;
/**
*
@@ -227,6 +233,10 @@ public QueryResponse search(String query, int first, int rows, List
for (int i = 0; i < sortFields.size(); ++i) {
StringPair sortField = sortFields.get(i);
if (StringUtils.isNotEmpty(sortField.getOne())) {
+ // If RANDOM is used, generate a randomized sort field
+ if ("RANDOM".equals(sortField.getOne())) {
+ sortField.setOne(generateRandomSortField());
+ }
solrQuery.addSort(sortField.getOne(), "desc".equals(sortField.getTwo()) ? ORDER.desc : ORDER.asc);
}
}
@@ -1190,6 +1200,20 @@ public QueryResponse searchFacetsAndStatistics(String query, List filter
throws PresentationException, IndexUnreachableException {
return searchFacetsAndStatistics(query, filterQueries, facetFields, facetMinCount, null, getFieldStatistics);
}
+
+ public boolean pingSolrIndex() {
+ if(client != null) {
+ try {
+ SolrPingResponse ping = client.ping();
+ return ping.getStatus() < 400;
+ } catch (SolrException | SolrServerException | IOException e) {
+ logger.trace("Ping to solr failed " + e.toString());
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
/**
* Returns facets for the given facet field list. No actual docs are returned since they aren't necessary.
@@ -1378,25 +1402,33 @@ public static boolean isQuerySyntaxError(Exception e) {
*
*
* @return a {@link java.util.List} object.
+ * @throws DAOException
* @throws org.apache.solr.client.solrj.SolrServerException if any.
* @throws java.io.IOException if any.
*/
- public List getAllFieldNames() throws SolrServerException, IOException {
- LukeRequest lukeRequest = new LukeRequest();
- lukeRequest.setNumTerms(0);
- LukeResponse lukeResponse = lukeRequest.process(client);
- Map fieldInfoMap = lukeResponse.getFieldInfo();
-
- List list = new ArrayList<>();
- for (String name : fieldInfoMap.keySet()) {
- FieldInfo info = fieldInfoMap.get(name);
- if (info != null && info.getType() != null && (info.getType().toLowerCase().contains("string")
- || info.getType().toLowerCase().contains("text") || info.getType().toLowerCase().contains("tlong"))) {
- list.add(name);
+ public List getAllFieldNames() throws DAOException {
+ try {
+ if(this.solrFields == null) {
+ LukeRequest lukeRequest = new LukeRequest();
+ lukeRequest.setNumTerms(0);
+ LukeResponse lukeResponse = lukeRequest.process(client);
+ Map fieldInfoMap = lukeResponse.getFieldInfo();
+
+ List list = new ArrayList<>();
+ for (String name : fieldInfoMap.keySet()) {
+ FieldInfo info = fieldInfoMap.get(name);
+ if (info != null && info.getType() != null && (info.getType().toLowerCase().contains("string")
+ || info.getType().toLowerCase().contains("text") || info.getType().toLowerCase().contains("tlong"))) {
+ list.add(name);
+ }
+ }
+ this.solrFields = list;
}
+ } catch(IllegalStateException | SolrServerException | IOException e) {
+ throw new DAOException("Failed to load SOLR field names: " + e.toString());
}
- return list;
+ return this.solrFields;
}
/**
@@ -1750,6 +1782,15 @@ public static String getProcessedConditions(String conditions) {
return conditions.trim();
}
+
+ /**
+ * Solr supports dynamic random_* sorting fields. Each value represents one particular order, so a random number is required.
+ *
+ * @return Randomized sorting field
+ */
+ public static String generateRandomSortField() {
+ return "random_" + new Random().nextInt(Integer.MAX_VALUE);
+ }
/**
*
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/imaging/ThumbnailHandler.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/imaging/ThumbnailHandler.java
index 719da43882d..aa6097a06c0 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/imaging/ThumbnailHandler.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/controller/imaging/ThumbnailHandler.java
@@ -27,6 +27,7 @@
import org.slf4j.LoggerFactory;
import de.intranda.api.iiif.IIIFUrlResolver;
+import de.intranda.monitoring.timer.Time;
import de.unigoettingen.sub.commons.contentlib.imagelib.ImageFileFormat;
import de.unigoettingen.sub.commons.contentlib.imagelib.ImageType.Colortype;
import de.unigoettingen.sub.commons.contentlib.imagelib.transform.Region;
@@ -142,8 +143,8 @@ public String getThumbnailUrl(PhysicalElement page) {
* @throws io.goobi.viewer.exceptions.ViewerConfigurationException if any.
*/
public String getThumbnailUrl(String pi) throws IndexUnreachableException, PresentationException, ViewerConfigurationException {
- return getThumbnailUrl(pi, DataManager.getInstance().getConfiguration().getThumbnailsWidth(),
- DataManager.getInstance().getConfiguration().getThumbnailsHeight());
+ return getThumbnailUrl(pi, DataManager.getInstance().getConfiguration().getThumbnailsWidth(),
+ DataManager.getInstance().getConfiguration().getThumbnailsHeight());
}
/**
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/dao/converter/StringListConverter.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/dao/converter/StringListConverter.java
new file mode 100644
index 00000000000..266a7b52560
--- /dev/null
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/dao/converter/StringListConverter.java
@@ -0,0 +1,61 @@
+/**
+ * This file is part of the Goobi viewer - a content presentation and management application for digitized objects.
+ *
+ * Visit these websites for more information.
+ * - http://www.intranda.com
+ * - http://digiverso.com
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If not, see .
+ */
+package io.goobi.viewer.dao.converter;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.persistence.AttributeConverter;
+import javax.persistence.Converter;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * Store simple strings in single database field
+ *
+ * @author florian
+ *
+ */
+@Converter
+public class StringListConverter implements AttributeConverter, String> {
+
+ /* (non-Javadoc)
+ * @see javax.persistence.AttributeConverter#convertToDatabaseColumn(java.lang.Object)
+ */
+ @Override
+ public String convertToDatabaseColumn(List attribute) {
+ if(attribute == null || attribute.isEmpty()) {
+ return null;
+ } else {
+ return attribute.stream().map(s -> s.replace(",", ",")).collect(Collectors.joining(","));
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see javax.persistence.AttributeConverter#convertToEntityAttribute(java.lang.Object)
+ */
+ @Override
+ public List convertToEntityAttribute(String dbData) {
+ if(StringUtils.isBlank(dbData)) {
+ return new ArrayList<>();
+ } else {
+ return Arrays.asList(dbData.split(",")).stream().map(s -> s.replace(",", ",")).collect(Collectors.toList());
+ }
+ }
+
+}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/dao/impl/JPADAO.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/dao/impl/JPADAO.java
index fadfb2d6827..5be6153d93d 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/dao/impl/JPADAO.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/dao/impl/JPADAO.java
@@ -3508,7 +3508,7 @@ public Campaign getCampaign(Long id) throws DAOException {
try {
Campaign o = em.getReference(Campaign.class, id);
if (o != null) {
- // updateFromDatabase(id, Campaign.class);
+// em.refresh(o);
}
return o;
} catch (EntityNotFoundException e) {
@@ -3597,7 +3597,7 @@ public boolean updateCampaign(Campaign campaign) throws DAOException {
c.resetSolrQueryResults();
return true;
} catch (RollbackException e) {
- return false;
+ throw new PersistenceException("Failed to persist campaign " + campaign, e);
}
}
}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/exceptions/BaseXException.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/exceptions/BaseXException.java
new file mode 100644
index 00000000000..3b577eea799
--- /dev/null
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/exceptions/BaseXException.java
@@ -0,0 +1,40 @@
+/**
+ * This file is part of the Goobi viewer - a content presentation and management application for digitized objects.
+ *
+ * Visit these websites for more information.
+ * - http://www.intranda.com
+ * - http://digiverso.com
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If not, see .
+ */
+package io.goobi.viewer.exceptions;
+
+import java.io.Serializable;
+
+/**
+ *
+ * IndexUnreachableException class.
+ *
+ */
+public class BaseXException extends Exception implements Serializable {
+
+ private static final long serialVersionUID = -5840484445206784670L;
+
+ /**
+ *
+ * Constructor for IndexUnreachableException.
+ *
+ *
+ * @param string {@link java.lang.String}
+ */
+ public BaseXException(String string) {
+ super(string);
+ }
+
+}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/exceptions/MyExceptionHandler.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/exceptions/MyExceptionHandler.java
index a1af8c8fde5..6825b063602 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/exceptions/MyExceptionHandler.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/exceptions/MyExceptionHandler.java
@@ -208,7 +208,7 @@ public void handle() throws FacesException {
i.remove();
}
} else if (t instanceof DAOException || isCausedByExceptionType(t, DAOException.class.getName())
- || (t instanceof PrettyException && t.getMessage().contains(IndexUnreachableException.class.getSimpleName()))) {
+ || (t instanceof PrettyException && t.getMessage().contains(DAOException.class.getSimpleName()))) {
logger.trace("Caused by DAOException");
try {
requestMap.put("errorType", "dao");
@@ -218,7 +218,18 @@ public void handle() throws FacesException {
} finally {
i.remove();
}
- } else if (t instanceof ViewerConfigurationException || isCausedByExceptionType(t, ViewerConfigurationException.class.getName())
+ } else if (t instanceof BaseXException || isCausedByExceptionType(t, BaseXException.class.getName())
+ || (t instanceof PrettyException && t.getMessage().contains(BaseXException.class.getSimpleName()))) {
+ logger.trace("Caused by BaseXException");
+ try {
+ requestMap.put("errorType", "basex");
+ flash.put("errorType", "basex");
+ nav.handleNavigation(fc, null, "pretty:error");
+ fc.renderResponse();
+ } finally {
+ i.remove();
+ }
+ }else if (t instanceof ViewerConfigurationException || isCausedByExceptionType(t, ViewerConfigurationException.class.getName())
|| (t instanceof PrettyException && t.getMessage().contains(ViewerConfigurationException.class.getSimpleName()))) {
logger.trace("Caused by ViewerConfigurationException");
String msg = getRootCause(t).getMessage();
@@ -267,7 +278,7 @@ public void handle() throws FacesException {
requestMap.put("errMsg", msg);
requestMap.put("errorType", "general");
flash.put("errorType", "general");
-// nav.handleNavigation(fc, null, "pretty:error");
+ nav.handleNavigation(fc, null, "pretty:error");
fc.renderResponse();
} finally {
i.remove();
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/ActiveDocumentBean.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/ActiveDocumentBean.java
index f25fe65259d..b1722511ebe 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/ActiveDocumentBean.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/ActiveDocumentBean.java
@@ -145,7 +145,7 @@ public class ActiveDocumentBean implements Serializable {
private String selectedRecordLanguage;
private Boolean deleteRecordKeepTrace;
-
+
private CMSSidebarElement mapWidget = null;
private int reloads = 0;
@@ -1387,7 +1387,7 @@ public String getTitleBarLabel(String language)
if (StringUtils.isNotEmpty(label)) {
return label;
}
- } else if (cmsBean != null) {
+ } else if (cmsBean != null && navigationHelper.isCmsPage()) {
CMSPage cmsPage = cmsBean.getCurrentPage();
if (cmsPage != null) {
String cmsPageName = StringUtils.isNotBlank(cmsPage.getMenuTitle()) ? cmsPage.getMenuTitle() : cmsPage.getTitle();
@@ -1396,8 +1396,13 @@ public String getTitleBarLabel(String language)
}
}
}
-
- return null;
+
+ if(navigationHelper.getCurrentPageType() != null) {
+ PageType pageType = navigationHelper.getCurrentPageType();
+ return Messages.translate(pageType.getName(), Locale.forLanguageTag(language));
+ } else {
+ return null;
+ }
}
/**
@@ -1888,18 +1893,20 @@ public void setDeleteRecordKeepTrace(Boolean deleteRecordKeepTrace) {
}
public CMSSidebarElement getMapWidget() throws PresentationException, DAOException {
- if(this.mapWidget == null) {
+ if (this.mapWidget == null) {
this.mapWidget = generateMapWidget();
}
return this.mapWidget;
}
-
public CMSSidebarElement generateMapWidget() throws PresentationException, DAOException {
-
CMSSidebarElement widget = new CMSSidebarElement();
widget.setType("widgetGeoMap");
try {
+ if ("-".equals(getPersistentIdentifier())) {
+ return null;
+ }
+
GeoMap map = new GeoMap();
map.setId(Long.MAX_VALUE);
map.setType(GeoMapType.SOLR_QUERY);
@@ -1907,7 +1914,7 @@ public CMSSidebarElement generateMapWidget() throws PresentationException, DAOEx
map.setMarkerTitleField(null);
map.setMarker("default");
map.setSolrQuery(String.format("PI:%s OR PI_TOPSTRUCT:%s", getPersistentIdentifier(), getPersistentIdentifier()));
-
+
if (!map.getFeaturesAsString().equals("[]") || contentBean.hasGeoCoordinateAnnotations(getPersistentIdentifier())) {
widget.setGeoMap(map);
}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/AdminBean.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/AdminBean.java
index f3d71f92b24..d8f6f56a086 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/AdminBean.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/AdminBean.java
@@ -547,7 +547,7 @@ public void saveUserRoleAction() throws DAOException {
Messages.error("userGroup_memberAddFailure");
}
}
- setCurrentUserRole(null);
+ resetCurrentUserRoleAction();
}
/**
@@ -564,7 +564,7 @@ public void deleteUserRoleAction(UserRole userRole) throws DAOException {
} else {
Messages.error("deleteFailure");
}
- setCurrentUserRole(null);
+ resetCurrentUserRoleAction();
}
// LicenseType
@@ -1688,7 +1688,7 @@ public List getPossibleAccessConditions() throws IndexUnreachableExcepti
Collections.sort(accessConditions);
return accessConditions;
}
-
+
/**
*
* @return List of access condition values that have no corresponding license type in the database
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/AnnotationBean.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/AnnotationBean.java
new file mode 100644
index 00000000000..1d408ff5071
--- /dev/null
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/AnnotationBean.java
@@ -0,0 +1,295 @@
+/**
+ * This file is part of the Goobi viewer - a content presentation and management application for digitized objects.
+ *
+ * Visit these websites for more information.
+ * - http://www.intranda.com
+ * - http://digiverso.com
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If not, see .
+ */
+package io.goobi.viewer.managedbeans;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.SessionScoped;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.view.ViewScoped;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.goobi.viewer.controller.DataManager;
+import io.goobi.viewer.dao.IDAO;
+import io.goobi.viewer.exceptions.DAOException;
+import io.goobi.viewer.exceptions.IndexUnreachableException;
+import io.goobi.viewer.exceptions.PresentationException;
+import io.goobi.viewer.exceptions.ViewerConfigurationException;
+import io.goobi.viewer.managedbeans.tabledata.TableDataProvider;
+import io.goobi.viewer.managedbeans.tabledata.TableDataSource;
+import io.goobi.viewer.managedbeans.tabledata.TableDataProvider.SortOrder;
+import io.goobi.viewer.messages.Messages;
+import io.goobi.viewer.model.annotation.PersistentAnnotation;
+import io.goobi.viewer.model.annotation.export.AnnotationSheetWriter;
+import io.goobi.viewer.model.crowdsourcing.campaigns.Campaign;
+import io.goobi.viewer.model.crowdsourcing.questions.Question;
+import io.goobi.viewer.model.misc.SelectionManager;
+import io.goobi.viewer.model.toc.export.pdf.TocWriter;
+import io.goobi.viewer.model.toc.export.pdf.WriteTocException;
+
+/**
+ * @author florian
+ *
+ */
+@Named
+@ViewScoped
+public class AnnotationBean implements Serializable {
+
+ private static final long serialVersionUID = 8377250065305331020L;
+
+ private static final Logger logger = LoggerFactory.getLogger(AnnotationBean.class);
+
+ private static final int DEFAULT_ROWS_PER_PAGE = 15;
+
+ @Inject
+ protected CrowdsourcingBean crowdsourcingBean;
+
+ private TableDataProvider lazyModelAnnotations;
+
+ private SelectionManager exportSelection = new SelectionManager<>();
+
+ private String ownerCampaignId = "";
+
+ private String targetRecordPI = "";
+
+
+ @PostConstruct
+ public void init() {
+ if (lazyModelAnnotations == null) {
+ lazyModelAnnotations = new TableDataProvider(new TableDataSource() {
+
+ private Optional numCreatedPages = Optional.empty();
+
+ @Override
+ public List getEntries(int first, int pageSize, String sortField, SortOrder sortOrder,
+ Map filters) {
+ try {
+ if (StringUtils.isBlank(sortField)) {
+ sortField = "id";
+ sortOrder = SortOrder.DESCENDING;
+ }
+ filters.putAll(getFilters());
+ List ret =
+ DataManager.getInstance().getDao().getAnnotations(first, pageSize, sortField, sortOrder.asBoolean(), filters);
+ exportSelection = new SelectionManager(ret.stream().map(PersistentAnnotation::getId).collect(Collectors.toList()));
+ return ret;
+ } catch (DAOException e) {
+ logger.error("Could not initialize lazy model: {}", e.getMessage());
+ }
+
+ return Collections.emptyList();
+ }
+
+ /**
+ * @param filters
+ */
+ public Map getFilters() {
+ Map filters = new HashMap<>();
+ if (StringUtils.isNotEmpty(getOwnerCampaignId())) {
+ filters.put("generatorId", getOwnerCampaignId());
+ }
+ if (StringUtils.isNotEmpty(getTargetRecordPI())) {
+ filters.put("targetPI", getTargetRecordPI());
+ }
+
+ return filters;
+ }
+
+ @Override
+ public long getTotalNumberOfRecords(Map filters) {
+ if (!numCreatedPages.isPresent()) {
+ try {
+ filters.putAll(getFilters());
+ numCreatedPages = Optional.ofNullable(DataManager.getInstance().getDao().getAnnotationCount(filters));
+ } catch (DAOException e) {
+ logger.error("Unable to retrieve total number of campaigns", e);
+ }
+ }
+ return numCreatedPages.orElse(0l);
+ }
+
+ @Override
+ public void resetTotalNumberOfRecords() {
+ numCreatedPages = Optional.empty();
+ }
+
+
+ });
+ lazyModelAnnotations.setEntriesPerPage(DEFAULT_ROWS_PER_PAGE);
+ lazyModelAnnotations.setFilters("targetPI_body");
+ }
+ }
+
+ /**
+ *
+ * Getter for the field lazyModelAnnotations
.
+ *
+ *
+ * @return the lazyModelAnnotations
+ */
+ public TableDataProvider getLazyModelAnnotations() {
+ return lazyModelAnnotations;
+ }
+
+ /**
+ * @return the ownerCampaignId
+ */
+ public String getOwnerCampaignId() {
+ return ownerCampaignId;
+ }
+
+ /**
+ * @param ownerCampaignId the ownerCampaignId to set
+ */
+ public void setOwnerCampaignId(String ownerCampaignId) {
+ this.ownerCampaignId = ownerCampaignId;
+ }
+
+ /**
+ * @return the ownerRecordPI
+ */
+ public String getTargetRecordPI() {
+ return targetRecordPI;
+ }
+
+ /**
+ * @param ownerRecordPI the ownerRecordPI to set
+ */
+ public void setTargetRecordPI(String targetRecordPI) {
+ this.targetRecordPI = targetRecordPI;
+ }
+
+ /**
+ * @return the exportSelection
+ */
+ public SelectionManager getExportSelection() {
+ return exportSelection;
+ }
+
+
+ /**
+ * Deletes given annotation.
+ *
+ * @param annotation a {@link io.goobi.viewer.model.annotation.PersistentAnnotation} object.
+ * @return empty string
+ * @throws io.goobi.viewer.exceptions.DAOException if any.
+ */
+ public String deleteAnnotationAction(PersistentAnnotation annotation) throws DAOException {
+ if (annotation == null) {
+ return "";
+ }
+
+ try {
+ if (annotation.delete()) {
+ Messages.info("admin__crowdsoucing_annotation_deleteSuccess");
+ crowdsourcingBean.getLazyModelCampaigns().update();
+ }
+ } catch (ViewerConfigurationException e) {
+ logger.error(e.getMessage());
+ Messages.error(e.getMessage());
+ }
+
+ return "";
+ }
+
+
+ public Optional getOwningCampaign(PersistentAnnotation anno) {
+ try {
+ IDAO dao = DataManager.getInstance().getDao();
+ if(anno.getGeneratorId() != null) {
+ Question question = dao.getQuestion(anno.getGeneratorId());
+ if(question != null) {
+ return Optional.ofNullable(question.getOwner());
+ }
+ }
+ } catch(DAOException e) {
+ logger.error(e.toString(), e);
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Setter for {@link SelectionManager#setSelectAll(boolean) exportSelection#setSelectAll(boolean)}
+ * is placed here to avoid jsf confusing it with setting a value of the map
+ * @param select
+ */
+ public void setSelectAll(boolean select) {
+ this.exportSelection.setSelectAll(select);
+ }
+
+ /**
+ * Getter for {@link SelectionManager#isSelectAll() exportSelection#isSelectAll()}
+ * is placed here to avoid jsf confusing it with getting a value of the map
+ * @param select
+ * @return always false to deselect the select all button when loading the page
+ */
+ public boolean isSelectAll() {
+ return false;//this.exportSelection.isSelectAll();
+ }
+
+ /**
+ * Create an excel sheet and write it to download stream
+ * @throws IOException
+ */
+ public void downloadSelectedAnnotations() throws IOException {
+ List selectedAnnos = this.exportSelection.getAllSelected().stream()
+ .map(this::getAnnotationById)
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .collect(Collectors.toList());
+ logger.debug("Selected " + selectedAnnos.size() + " annotations for excel download");
+ downloadAnnotations(selectedAnnos);
+ }
+
+ public void downloadAnnotations(List annotations) throws IOException {
+ try {
+ String fileName = "annotations.xlsx";
+
+ FacesContext fc = FacesContext.getCurrentInstance();
+ ExternalContext ec = fc.getExternalContext();
+ ec.responseReset(); // Some JSF component library or some Filter might have set some headers in the buffer beforehand. We want to get rid of them, else it may collide.
+ ec.setResponseContentType("application/msexcel");
+ ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
+ OutputStream os = ec.getResponseOutputStream();
+ AnnotationSheetWriter writer = new AnnotationSheetWriter();
+ writer.createExcelSheet(os, annotations);
+ fc.responseComplete(); // Important! Otherwise JSF will attempt to render the response which obviously will fail since it's already written with a file and closed.
+ } finally {
+
+ }
+ }
+
+ public Optional getAnnotationById(Long id) {
+ return this.lazyModelAnnotations.getPaginatorList().stream().filter(anno -> id.equals(anno.getId())).findFirst();
+ }
+
+}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/ArchiveBean.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/ArchiveBean.java
new file mode 100644
index 00000000000..9a9672c68f1
--- /dev/null
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/ArchiveBean.java
@@ -0,0 +1,477 @@
+/**
+ * This file is part of the Goobi viewer - a content presentation and management application for digitized objects.
+ *
+ * Visit these websites for more information.
+ * - http://www.intranda.com
+ * - http://digiverso.com
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If not, see .
+ */
+package io.goobi.viewer.managedbeans;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import javax.annotation.PostConstruct;
+import javax.faces.view.ViewScoped;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.SolrDocumentList;
+import org.jdom2.Document;
+import org.jdom2.JDOMException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import de.intranda.monitoring.timer.Time;
+import io.goobi.viewer.controller.DataManager;
+import io.goobi.viewer.controller.SolrConstants;
+import io.goobi.viewer.controller.SolrSearchIndex;
+import io.goobi.viewer.exceptions.BaseXException;
+import io.goobi.viewer.exceptions.HTTPException;
+import io.goobi.viewer.exceptions.IndexUnreachableException;
+import io.goobi.viewer.exceptions.PresentationException;
+import io.goobi.viewer.model.archives.ArchiveEntry;
+import io.goobi.viewer.model.archives.ArchiveResource;
+import io.goobi.viewer.model.archives.ArchiveTree;
+import io.goobi.viewer.model.archives.BasexEADParser;
+import io.goobi.viewer.model.viewer.StringPair;
+
+@Named
+@ViewScoped
+public class ArchiveBean implements Serializable {
+
+ private static final long serialVersionUID = -1755934299534933504L;
+
+ private static final Logger logger = LoggerFactory.getLogger(ArchiveBean.class);
+
+ private ArchiveTree archiveTree;
+
+ private String searchString;
+
+ private DatabaseState databaseState = DatabaseState.NOT_INITIALIZED;
+
+ @Deprecated
+ @Inject
+ private PersistentStorageBean storage;
+
+ // @Inject
+ // private FacesContext context;
+
+ private static enum DatabaseState {
+ NOT_INITIALIZED,
+ VALID,
+ ERROR_NOT_CONFIGURED,
+ ERROR_NOT_REACHABLE,
+ ERROR_INVALID_FORMAT
+ }
+
+ /**
+ * Empty constructor.
+ */
+ public ArchiveBean() {
+ // the emptiness inside
+ }
+
+ /**
+ *
+ */
+ @PostConstruct
+ public void init() {
+ String basexUrl = DataManager.getInstance().getConfiguration().getBaseXUrl();
+ String databaseName = DataManager.getInstance().getConfiguration().getBaseXDatabase();
+ if (StringUtils.isNoneBlank(basexUrl, databaseName)) {
+ BasexEADParser eadParser = new BasexEADParser(basexUrl);
+ this.databaseState = loadDatabase(eadParser, databaseName);
+ } else {
+ this.databaseState = DatabaseState.ERROR_NOT_CONFIGURED;
+ }
+ }
+
+ DatabaseState loadDatabase(BasexEADParser eadParser, String databaseName) {
+ if (eadParser == null) {
+ return DatabaseState.NOT_INITIALIZED;
+ }
+
+ // String storageKey = databaseName + "@" + eadParser.getBasexUrl();
+ // if(context.getExternalContext().getSessionMap().containsKey(storageKey)) {
+ // eadParser = new BasexEADParser((BasexEADParser)context.getExternalContext().getSessionMap().containsKey(storageKey));
+ // } else {
+ HierarchicalConfiguration baseXMetadataConfig = DataManager.getInstance().getConfiguration().getBaseXMetadataConfig();
+ try {
+ eadParser.readConfiguration(baseXMetadataConfig);
+ Document databaseDoc = eadParser.retrieveDatabaseDocument(databaseName);
+ ArchiveEntry rootElement = eadParser.loadDatabase(databaseName, databaseDoc);
+ this.archiveTree = loadTree(rootElement);
+ logger.info("Loaded EAD database: {}", databaseName);
+ return DatabaseState.VALID;
+ } catch (IOException | HTTPException e) {
+ logger.error("Error retrieving database {} from {}", databaseName, eadParser.getBasexUrl());
+ return DatabaseState.ERROR_NOT_REACHABLE;
+ } catch (JDOMException | ConfigurationException e) {
+ logger.error("Error reading database {} from {}", databaseName, eadParser.getBasexUrl());
+ return DatabaseState.ERROR_INVALID_FORMAT;
+ }
+ // context.getExternalContext().getSessionMap().put(storageKey, eadParser);
+ // }
+
+ }
+
+ /**
+ * @param eadParser
+ * @param databaseName
+ * @throws ClientProtocolException
+ * @throws IOException
+ * @throws HTTPException
+ * @throws IllegalStateException
+ * @throws JDOMException
+ * @deprecated Storing database in application seems ineffective since verifying that database is current takes about as much time as retriving
+ * the complete database
+ */
+ @Deprecated
+ public void loadDatabaseAndStoreInApplicationScope(BasexEADParser eadParser, String databaseName)
+ throws ClientProtocolException, IOException, HTTPException, IllegalStateException, JDOMException {
+ try (Time t = DataManager.getInstance().getTiming().takeTime("loadDatabase")) {
+ Document databaseDoc = null;
+ String storageKey = databaseName + "@" + eadParser.getBasexUrl();
+ List dbs;
+ try (Time t1 = DataManager.getInstance().getTiming().takeTime("getPossibleDatabases")) {
+ dbs = eadParser.getPossibleDatabases();
+ }
+ ArchiveResource db = dbs.stream().filter(res -> res.getCombinedName().equals(databaseName)).findAny().orElse(null);
+ if (db == null) {
+ throw new IllegalStateException("Configured default database not found in " + eadParser.getBasexUrl());
+ } else if (storage.contains(storageKey) && !storage.olderThan(storageKey, db.lastModified)) {
+ databaseDoc = (Document) storage.get(storageKey);
+ } else {
+ try (Time t2 = DataManager.getInstance().getTiming().takeTime("retrieveDatabaseDocument")) {
+ databaseDoc = eadParser.retrieveDatabaseDocument(databaseName);
+ storage.put(storageKey, databaseDoc);
+ }
+ }
+ HierarchicalConfiguration baseXMetadataConfig = DataManager.getInstance().getConfiguration().getBaseXMetadataConfig();
+ try (Time t3 = DataManager.getInstance().getTiming().takeTime("eadParser.loadDatabase")) {
+ eadParser.readConfiguration(baseXMetadataConfig).loadDatabase(databaseName, databaseDoc);
+ } catch (ConfigurationException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ *
+ * @return actual root element of the document, even if it's not in the displayed tree
+ */
+ public ArchiveEntry getTrueRoot() {
+ if (archiveTree == null) {
+ return null;
+ }
+
+ return archiveTree.getRootElement();
+ }
+
+ /**
+ *
+ * @return
+ * @throws BaseXException
+ */
+ public ArchiveTree getArchiveTree() throws BaseXException {
+ return archiveTree;
+ }
+
+ /**
+ * @param rootElement
+ * @return
+ */
+ ArchiveTree loadTree(ArchiveEntry rootElement) {
+ if (rootElement == null) {
+ logger.trace("Root not found, cannot load tree.");
+ return null;
+ }
+
+ ArchiveTree ret = new ArchiveTree();
+ ret.generate(rootElement);
+ if (ret.getSelectedEntry() == null) {
+ ret.setSelectedEntry(ret.getRootElement());
+ }
+ // This should happen before the tree is expanded to the selected entry, otherwise the collapse level will be reset
+ ret.getTreeView();
+
+ return ret;
+ }
+
+ /**
+ *
+ * expandEntry.
+ *
+ *
+ * @param entry a {@link io.goobi.viewer.model.toc.TOCElement} object.
+ */
+ public void expandEntry(ArchiveEntry entry) {
+ logger.trace("expandEntry: {}", entry);
+ if (archiveTree == null) {
+ return;
+ }
+ synchronized (archiveTree) {
+ entry.expand();
+ }
+ }
+
+ /**
+ *
+ * collapseEntry.
+ *
+ *
+ * @param entry a {@link io.goobi.viewer.model.toc.TOCElement} object.
+ */
+ public void collapseEntry(ArchiveEntry entry) {
+ logger.trace("collapseEntry: {}", entry);
+ if (archiveTree == null) {
+ return;
+ }
+
+ synchronized (archiveTree) {
+ entry.collapse();
+ }
+ }
+
+ /**
+ * Returns the entry hierarchy from the root down to the entry with the given identifier.
+ *
+ * @param identifier Entry identifier
+ * @param List of entries An empty list if the identified node has no ancestors or doesn't exist
+ */
+ public List getArchiveHierarchyForIdentifier(String identifier) {
+ if (StringUtils.isEmpty(identifier)) {
+ return Collections.emptyList();
+ }
+
+ if (archiveTree == null) {
+ logger.error("Archive not loaded");
+ return Collections.emptyList();
+ }
+
+ ArchiveEntry entry = archiveTree.getEntryById(identifier);
+ if (entry == null) {
+ // return Collections.emptyList();
+ return Collections.singletonList(getTrueRoot());
+ } else if (getTrueRoot().equals(entry) || getTrueRoot().equals(entry.getParentNode())) {
+ return Collections.singletonList(entry);
+ } else {
+ return entry.getAncestors(false).stream().skip(1).collect(Collectors.toList());
+ }
+ }
+
+ /**
+ *
+ * @param entry
+ * @return
+ */
+ public String selectEntryAction(ArchiveEntry entry) {
+ if (entry == null || archiveTree == null) {
+ return "";
+ }
+
+ archiveTree.setSelectedEntry(entry);
+
+ return "";
+ }
+
+ /**
+ *
+ * @return
+ * @throws BaseXException
+ */
+ public String searchAction() throws BaseXException {
+ logger.trace("searchAction: {}", searchString);
+ search(true, true);
+
+ return "";
+ }
+
+ /**
+ * Executes search for searchString.
+ *
+ * @param resetSelectedEntry If true, selected entry will be set to null
+ * @param collapseAll If true, all elements will be collapsed before expanding path to search hits
+ * @throws BaseXException
+ */
+ void search(boolean resetSelectedEntry, boolean collapseAll) throws BaseXException {
+ if (!isDatabaseValid()) {
+ logger.warn("Archive not loaded, cannot search.");
+ return;
+ }
+
+ if (StringUtils.isEmpty(searchString)) {
+ archiveTree.resetSearch();
+ archiveTree.resetCollapseLevel(archiveTree.getRootElement(), ArchiveTree.defaultCollapseLevel);
+ return;
+ }
+
+ archiveTree.search(searchString);
+ List results = archiveTree.getFlatEntryList();
+ if (results == null || results.isEmpty()) {
+ return;
+ }
+ logger.trace("result entries: {}", results.size());
+
+ if (resetSelectedEntry) {
+ setSelectedEntryId(null);
+ }
+ archiveTree.collapseAll(collapseAll);
+ for (ArchiveEntry entry : results) {
+ if (entry.isSearchHit()) {
+ entry.expandUp();
+ }
+ }
+ }
+
+ /**
+ * @return the searchString
+ */
+ public String getSearchString() {
+ return searchString;
+ }
+
+ /**
+ * @param searchString the searchString to set
+ */
+ public void setSearchString(String searchString) {
+ logger.trace("setSearchString: {}", searchString);
+ this.searchString = searchString;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public String getSelectedEntryId() {
+ if (archiveTree == null || archiveTree.getSelectedEntry() == null) {
+ return "-";
+ }
+
+ return archiveTree.getSelectedEntry().getId();
+ }
+
+ /**
+ * Setter for the URL parameter. Loads the entry that has the given ID. Loads the tree, if this is a new sessions.
+ *
+ * @param id Entry ID
+ * @throws BaseXException
+ */
+ public void setSelectedEntryId(String id) throws BaseXException {
+ logger.trace("setSelectedEntryId: {}", id);
+ if (!isDatabaseValid()) {
+ return;
+ }
+
+ // Select root element if no ID given
+ if (StringUtils.isBlank(id)) {
+ id = "-";
+ }
+ if ("-".equals(id)) {
+ archiveTree.setSelectedEntry(null);
+ return;
+ }
+ // Requested entry is already selected
+ if (archiveTree.getSelectedEntry() != null && archiveTree.getSelectedEntry().getId().equals(id)) {
+ return;
+ }
+
+ // Find entry with given ID in the tree
+ ArchiveEntry result = archiveTree.getEntryById(id);
+ if (result != null) {
+ archiveTree.setSelectedEntry(result);
+ result.expandUp();
+ } else {
+ logger.debug("Entry not found: {}", id);
+ archiveTree.setSelectedEntry(archiveTree.getRootElement());
+ }
+
+ }
+
+ public boolean isSearchActive() {
+ return StringUtils.isNotBlank(searchString);
+ }
+
+ /**
+ *
+ * @return the {@link ArchiveEntry} to display in the metadata section of the archives view. Either {@link ArchiveTree#getSelectedEntry()} or
+ * {@link ArchiveTree#getRootElement()} if the former is null
+ */
+ public ArchiveEntry getDisplayEntry() {
+ if (archiveTree == null) {
+ return null;
+ }
+
+ return Optional.ofNullable(archiveTree.getSelectedEntry()).orElse(archiveTree.getRootElement());
+ }
+
+ /**
+ * In the list of archive document search hits, find the id of the entry just before the given one
+ *
+ * @param entry
+ * @param sortOrder 'asc' to get the preceding entry, 'desc' to get the succeeding one
+ * @return the neighboring entry id if it exists
+ * @throws PresentationException
+ * @throws IndexUnreachableException
+ */
+ public Pair, Optional> findIndexedNeighbours(String entryId)
+ throws PresentationException, IndexUnreachableException {
+ String query = SolrConstants.ARCHIVE_ENTRY_ID + ":*";
+ List sortFields = Collections.singletonList(new StringPair(SolrConstants.PI, "asc"));
+ List fieldList = Arrays.asList(SolrConstants.PI, SolrConstants.ARCHIVE_ENTRY_ID);
+
+ SolrDocumentList docs = DataManager.getInstance()
+ .getSearchIndex()
+ .search(query, SolrSearchIndex.MAX_HITS, sortFields, fieldList);
+ Optional prev = Optional.empty();
+ Optional next = Optional.empty();
+
+ ListIterator iter = docs.listIterator();
+ while (iter.hasNext()) {
+ SolrDocument doc = iter.next();
+ String id = SolrSearchIndex.getSingleFieldStringValue(doc, SolrConstants.ARCHIVE_ENTRY_ID);
+ if (id.equals(entryId)) {
+ if (iter.hasNext()) {
+ String nextId = SolrSearchIndex.getSingleFieldStringValue(iter.next(), SolrConstants.ARCHIVE_ENTRY_ID);
+ next = Optional.of(nextId);
+ }
+ break;
+ }
+ prev = Optional.of(id);
+ }
+ return Pair.of(prev, next);
+ }
+
+ /**
+ * @return the databaseState
+ */
+ public DatabaseState getDatabaseState() {
+ return databaseState;
+ }
+
+ public boolean isDatabaseValid() {
+ return DatabaseState.VALID.equals(this.databaseState);
+ }
+
+}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/BreadcrumbBean.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/BreadcrumbBean.java
index c2065dd6a83..0ccbce518e1 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/BreadcrumbBean.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/BreadcrumbBean.java
@@ -463,7 +463,7 @@ public List getBreadcrumbs() {
flattenedLinks.add(labeledLink);
}
}
- logger.trace("getBreadcrumbs: {}", flattenedLinks.toString());
+ // logger.trace("getBreadcrumbs: {}", flattenedLinks.toString());
return flattenedLinks;
}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/CmsBean.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/CmsBean.java
index 919f2d0b5ee..f7dbcac3a0b 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/CmsBean.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/CmsBean.java
@@ -31,6 +31,7 @@
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
@@ -150,6 +151,8 @@ public class CmsBean implements Serializable {
private List solrSortFields = null;
private List solrGroupFields = null;
+ private List luceneFields = null;
+
/**
*
* init.
@@ -1592,7 +1595,7 @@ public String cmsContextAction(boolean resetSearch)
if (StringUtils.isNotBlank(searchBean.getExactSearchString().replace("-", ""))) {
searchBean.setShowReducedSearchOptions(true);
return searchAction(item);
- } else if (item.isDisplayEmptySearchResults()) {
+ } else if (item.isDisplayEmptySearchResults() || StringUtils.isNotBlank(searchBean.getFacets().getCurrentFacetString())) {
String searchString = StringUtils.isNotBlank(item.getSolrQuery().replace("-", "")) ? item.getSolrQuery() : "";
// searchBean.setSearchString(item.getSolrQuery());
searchBean.setExactSearchString(searchString);
@@ -1771,7 +1774,7 @@ public String searchAction(CMSContentItem item)
}
boolean aggregateHits = DataManager.getInstance().getConfiguration().isAggregateHits();
if (item != null && CMSContentItemType.SEARCH.equals(item.getType())) {
- ((SearchFunctionality) item.getFunctionality()).search();
+ ((SearchFunctionality) item.getFunctionality()).search(item.getOwnerPageLanguageVersion().getOwnerPage().getSubThemeDiscriminatorValue());
} else if (item != null && StringUtils.isNotBlank(item.getSolrQuery())) {
Search search = new Search(SearchHelper.SEARCH_TYPE_REGULAR, SearchHelper.SEARCH_FILTER_ALL);
search.setQuery(item.getSolrQuery());
@@ -2066,7 +2069,7 @@ public CollectionView getCollection(CMSPage page) throws PresentationException,
*
* @return a {@link java.util.List} object.
*/
- public static List getLuceneFields() {
+ public List getLuceneFields() {
return getLuceneFields(false, false);
}
@@ -2078,7 +2081,7 @@ public static List getLuceneFields() {
* @param includeUntokenized a boolean.
* @return a {@link java.util.List} object.
*/
- public static List getLuceneFields(boolean includeUntokenized) {
+ public List getLuceneFields(boolean includeUntokenized) {
return getLuceneFields(includeUntokenized, false);
}
@@ -2091,22 +2094,24 @@ public static List getLuceneFields(boolean includeUntokenized) {
* @param excludeTokenizedMetadataFields a boolean.
* @return a {@link java.util.List} object.
*/
- public static List getLuceneFields(boolean includeUntokenized, boolean excludeTokenizedMetadataFields) {
- List constants;
+ public List getLuceneFields(boolean includeUntokenized, boolean excludeTokenizedMetadataFields) {
try {
- constants = DataManager.getInstance().getSearchIndex().getAllFieldNames();
- Iterator iterator = constants.iterator();
- while (iterator.hasNext()) {
- String name = iterator.next();
- if (name.startsWith("_") || name.startsWith("FACET_") || name.startsWith("NORM_")
- || (!includeUntokenized && name.endsWith(SolrConstants._UNTOKENIZED))
- || (excludeTokenizedMetadataFields && name.startsWith("MD_") && !name.endsWith(SolrConstants._UNTOKENIZED))) {
- iterator.remove();
- }
+ if(this.luceneFields == null) {
+ this.luceneFields = DataManager.getInstance().getSearchIndex().getAllFieldNames();
+ }
+
+ Stream filteredLuceneFields = this.luceneFields.stream()
+ .filter(name -> !(name.startsWith("_") || name.startsWith("FACET_") || name.startsWith("NORM_")));
+ if(!includeUntokenized) {
+ filteredLuceneFields = filteredLuceneFields.filter(name -> !name.endsWith(SolrConstants._UNTOKENIZED));
}
- Collections.sort(constants);
- return constants;
- } catch (SolrServerException | IOException e) {
+ if(excludeTokenizedMetadataFields) {
+ filteredLuceneFields = filteredLuceneFields.filter(name -> !(name.startsWith("MD_") && !name.endsWith(SolrConstants._UNTOKENIZED)));
+ }
+ filteredLuceneFields = filteredLuceneFields.sorted();
+ return filteredLuceneFields.collect(Collectors.toList());
+
+ } catch (DAOException e) {
logger.error("Error retrieving solr fields", e);
return Collections.singletonList("");
}
@@ -2577,20 +2582,13 @@ public List getPossibleSortFields() throws SolrServerException, IOExcept
* @throws java.io.IOException if any.
*/
public List getPossibleGroupFields() throws SolrServerException, IOException {
+
+ if (this.solrGroupFields == null) {
+ this.solrGroupFields = DataManager.getInstance().getSearchIndex().getAllGroupFieldNames();
+ Collections.sort(solrGroupFields);
+ }
+ return this.solrGroupFields;
- if (this.solrGroupFields == null) {
- this.solrGroupFields = DataManager.getInstance().getSearchIndex().getAllGroupFieldNames();
- Collections.sort(solrGroupFields);
- }
- return this.solrGroupFields;
-
- // List fields = new ArrayList<>();
- // fields.add(SolrConstants.SORTNUM_YEAR);
- // fields.add(SolrConstants.DOCSTRCT);
- // fields.add(SolrConstants.DC);
- // fields.add(SolrConstants.PI_ANCHOR);
- // fields.add(DataManager.getInstance().getConfiguration().getSubthemeDiscriminatorField());
- // return fields;
}
/**
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/ConfigurationBean.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/ConfigurationBean.java
index a77e9e942b1..e8946d60795 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/ConfigurationBean.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/ConfigurationBean.java
@@ -894,6 +894,7 @@ public boolean isDisplayTimeMatrix() {
public boolean isDisplayCrowdsourcingModuleLinks() {
return DataManager.getInstance().getConfiguration().isDisplayCrowdsourcingModuleLinks();
}
+
/**
*
@@ -921,6 +922,7 @@ public int getTimeMatrixStartYear(String subTheme) throws PresentationException,
}
}
+
/**
*
* getTimeMatrixEndYear.
@@ -1280,7 +1282,7 @@ public String getRestApiUrl() throws ViewerConfigurationException {
* @throws io.goobi.viewer.exceptions.ViewerConfigurationException if any.
*/
public String getIiifApiUrl() throws ViewerConfigurationException {
- return DataManager.getInstance().getConfiguration().getRestApiUrl();
+ return DataManager.getInstance().getConfiguration().getIIIFApiUrl();
}
/**
@@ -1492,4 +1494,12 @@ public List getSearchHitsPerPageValues() {
public boolean isAnonymousUserEmailAddressValid() {
return EmailValidator.validateEmailAddress(DataManager.getInstance().getConfiguration().getAnonymousUserEmailAddress());
}
+
+ /**
+ *
+ * @return true if default sorting field is 'RANDOM'; false otherwise
+ */
+ public boolean isDefaultSortFieldRandom() {
+ return "RANDOM".equals(DataManager.getInstance().getConfiguration().getDefaultSortField());
+ }
}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/CrowdsourcingBean.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/CrowdsourcingBean.java
index d573574b5ff..9aabe79edad 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/CrowdsourcingBean.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/CrowdsourcingBean.java
@@ -19,21 +19,22 @@
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
+import java.util.EnumSet;
import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
-import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
+import javax.persistence.PersistenceException;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
@@ -65,8 +66,10 @@
import io.goobi.viewer.model.crowdsourcing.CrowdsourcingTools;
import io.goobi.viewer.model.crowdsourcing.campaigns.Campaign;
import io.goobi.viewer.model.crowdsourcing.campaigns.Campaign.CampaignVisibility;
+import io.goobi.viewer.model.crowdsourcing.campaigns.Campaign.ReviewMode;
import io.goobi.viewer.model.crowdsourcing.campaigns.CampaignRecordStatistic.CampaignRecordStatus;
import io.goobi.viewer.model.crowdsourcing.questions.Question;
+import io.goobi.viewer.model.misc.IPolyglott;
import io.goobi.viewer.model.security.License;
import io.goobi.viewer.model.security.LicenseType;
import io.goobi.viewer.model.security.user.User;
@@ -93,7 +96,6 @@ public class CrowdsourcingBean implements Serializable {
protected UserBean userBean;
private TableDataProvider lazyModelCampaigns;
- private TableDataProvider lazyModelAnnotations;
/**
* The campaign selected in backend
@@ -162,57 +164,6 @@ public void resetTotalNumberOfRecords() {
lazyModelCampaigns.setFilters("name");
}
- if (lazyModelAnnotations == null) {
- lazyModelAnnotations = new TableDataProvider<>(new TableDataSource() {
-
- private Optional numCreatedPages = Optional.empty();
-
- @Override
- public List getEntries(int first, int pageSize, String sortField, SortOrder sortOrder,
- Map filters) {
- try {
- if (StringUtils.isBlank(sortField)) {
- sortField = "id";
- sortOrder = SortOrder.DESCENDING;
- }
- // Permanent filtering for annotations for a specific campaign
- if (StringUtils.isNotEmpty(getTargetCampaignId())) {
- filters.put("generatorId", getTargetCampaignId());
- }
- List ret =
- DataManager.getInstance().getDao().getAnnotations(first, pageSize, sortField, sortOrder.asBoolean(), filters);
- return ret;
- } catch (DAOException e) {
- logger.error("Could not initialize lazy model: {}", e.getMessage());
- }
-
- return Collections.emptyList();
- }
-
- @Override
- public long getTotalNumberOfRecords(Map filters) {
- if (!numCreatedPages.isPresent()) {
- try {
- // Permanent filtering for annotations for a specific campaign
- if (StringUtils.isNotEmpty(getTargetCampaignId())) {
- filters.put("generatorId", getTargetCampaignId());
- }
- numCreatedPages = Optional.ofNullable(DataManager.getInstance().getDao().getAnnotationCount(filters));
- } catch (DAOException e) {
- logger.error("Unable to retrieve total number of campaigns", e);
- }
- }
- return numCreatedPages.orElse(0l);
- }
-
- @Override
- public void resetTotalNumberOfRecords() {
- numCreatedPages = Optional.empty();
- }
- });
- lazyModelAnnotations.setEntriesPerPage(DEFAULT_ROWS_PER_PAGE);
- lazyModelAnnotations.setFilters("targetPI_body");
- }
}
/**
@@ -257,19 +208,8 @@ public String filterCampaignsAction(CampaignVisibility visibility) {
*
* @return A list of all locales supported by this viewer application
*/
- public static List getAllLocales() {
- List list = new LinkedList<>();
- list.add(ViewerResourceBundle.getDefaultLocale());
- if (FacesContext.getCurrentInstance() != null && FacesContext.getCurrentInstance().getApplication() != null) {
- Iterator iter = FacesContext.getCurrentInstance().getApplication().getSupportedLocales();
- while (iter.hasNext()) {
- Locale locale = iter.next();
- if (!list.contains(locale)) {
- list.add(locale);
- }
- }
- }
- return list;
+ public static Collection getAllLocales() {
+ return IPolyglott.getLocalesStatic();
}
/**
@@ -456,16 +396,14 @@ public boolean isUserOwnsAnyCampaigns(User user) throws DAOException {
public static boolean isAllowed(User user, Campaign campaign) throws DAOException {
if (campaign == null) {
return false;
- }
-
- if (CampaignVisibility.PUBLIC.equals(campaign.getVisibility())) {
- return true;
- }
-
+ }
// Skip inactive campaigns
if (!campaign.isHasStarted() || campaign.isHasEnded()) {
return false;
}
+ if (CampaignVisibility.PUBLIC.equals(campaign.getVisibility())) {
+ return true;
+ }
// Allow campaigns with a set time frame, but no user group
if (campaign.isTimePeriodEnabled() && campaign.getDateStart() != null && campaign.getDateEnd() != null
@@ -475,15 +413,15 @@ public static boolean isAllowed(User user, Campaign campaign) throws DAOExceptio
switch (campaign.getVisibility()) {
case PRIVATE:
- // Only logged in members may access campaigns limited to a user group
- if (!campaign.isLimitToGroup() || campaign.getUserGroup() == null) {
- return false;
- }
if (user == null) {
return false;
}
- if (user.isSuperuser()) {
+ if ( user.isSuperuser()) {
return true;
+ }
+ // Only logged in members may access campaigns limited to a user group
+ if (!campaign.isLimitToGroup() || campaign.getUserGroup() == null) {
+ return false;
}
try {
return campaign.getUserGroup().getMembersAndOwner().contains(user);
@@ -553,7 +491,12 @@ public String saveSelectedCampaignAction() throws DAOException, PresentationExce
}
selectedCampaign.setDateUpdated(now);
if (selectedCampaign.getId() != null) {
- success = DataManager.getInstance().getDao().updateCampaign(selectedCampaign);
+ try {
+ success = DataManager.getInstance().getDao().updateCampaign(selectedCampaign);
+ } catch(PersistenceException e) {
+ logger.error("Updating campaign " + selectedCampaign + " in database failed ", e);
+ success = false;
+ }
} else {
success = DataManager.getInstance().getDao().addCampaign(selectedCampaign);
}
@@ -592,42 +535,6 @@ public TableDataProvider getLazyModelCampaigns() {
return lazyModelCampaigns;
}
- /**
- *
- * Getter for the field lazyModelAnnotations
.
- *
- *
- * @return the lazyModelAnnotations
- */
- public TableDataProvider getLazyModelAnnotations() {
- return lazyModelAnnotations;
- }
-
- /**
- * Deletes given annotation.
- *
- * @param annotation a {@link io.goobi.viewer.model.annotation.PersistentAnnotation} object.
- * @return empty string
- * @throws io.goobi.viewer.exceptions.DAOException if any.
- */
- public String deleteAnnotationAction(PersistentAnnotation annotation) throws DAOException {
- if (annotation == null) {
- return "";
- }
-
- try {
- if (annotation.delete()) {
- Messages.info("admin__crowdsoucing_annotation_deleteSuccess");
- lazyModelCampaigns.update();
- }
- } catch (ViewerConfigurationException e) {
- logger.error(e.getMessage());
- Messages.error(e.getMessage());
- }
-
- return "";
- }
-
/**
*
* Getter for the field selectedCampaign
.
@@ -1060,4 +967,10 @@ public void updateActiveCampaigns() throws DAOException, PresentationException,
}
logger.trace("Added {} identifiers to the map.", DataManager.getInstance().getRecordCampaignMap().size());
}
+
+ public Set getPossibleReviewModes() {
+ return EnumSet.allOf(ReviewMode.class);
+ }
+
+
}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/ImageDeliveryBean.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/ImageDeliveryBean.java
index 728f8e1ab26..3b3fbd12f71 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/ImageDeliveryBean.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/ImageDeliveryBean.java
@@ -99,15 +99,15 @@ public class ImageDeliveryBean implements Serializable {
@PostConstruct
private void init() {
+ logger.trace("init");
try {
Configuration config = DataManager.getInstance().getConfiguration();
- this.servletPath = getServletPathFromContext();
init(config);
} catch (NullPointerException e) {
logger.error("Failed to initialize ImageDeliveryBean: Resources misssing");
}
}
-
+
private String getServletPathFromContext() {
String path;
if (servletRequest != null) {
@@ -128,9 +128,10 @@ private String getServletPathFromContext() {
* @param apiUrls a {@link java.lang.String} object.
*/
public void init(Configuration config) {
+ this.servletPath = getServletPathFromContext();
AbstractApiUrlManager dataUrlManager = DataManager.getInstance().getRestApiManager().getDataApiManager();
AbstractApiUrlManager contentUrlManager = DataManager.getInstance().getRestApiManager().getContentApiManager();
-
+
this.staticImagesURI = getStaticImagesPath(this.servletPath, config.getTheme());
this.cmsMediaPath =
config.getViewerHome() + config.getCmsMediaFolder();
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/NavigationHelper.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/NavigationHelper.java
index fee1fa30a1c..5d7c0236ded 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/NavigationHelper.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/NavigationHelper.java
@@ -115,7 +115,7 @@ public class NavigationHelper implements Serializable {
/** Map for setting any navigation status variables. Replaces currentView, etc. */
protected Map statusMap = new HashMap<>();
- private String theme = "";
+ private final String theme;
/** Currently selected page from the main navigation bar. */
private String currentPage = "index";
@@ -126,7 +126,7 @@ public class NavigationHelper implements Serializable {
* Empty constructor.
*/
public NavigationHelper() {
- // the emptiness inside
+ theme = DataManager.getInstance().getConfiguration().getTheme();
}
/**
@@ -142,7 +142,6 @@ public void init() {
locale = Locale.GERMAN;
logger.warn("Could not access FacesContext, locale set to DE.");
}
- theme = DataManager.getInstance().getConfiguration().getTheme();
statusMap.put(KEY_CURRENT_PARTNER_PAGE, "");
statusMap.put(KEY_SELECTED_NEWS_ARTICLE, "");
statusMap.put(KEY_MENU_PAGE, "user");
@@ -248,7 +247,7 @@ public void setCurrentPage(String currentPage) {
* @param resetCurrentDocument a boolean.
*/
public void setCurrentPage(String currentPage, boolean resetBreadcrubs, boolean resetCurrentDocument) {
- logger.trace("setCurrentPage: {}", currentPage);
+ // logger.trace("setCurrentPage: {}", currentPage);
setCurrentPage(currentPage, resetBreadcrubs, resetCurrentDocument, false);
}
@@ -1033,21 +1032,10 @@ public void resetTheme() {
logger.trace("resetTheme");
// Resetting the current page here would result in the current record being flushed, which is bad for CMS overview pages
// resetCurrentPage();
- theme = DataManager.getInstance().getConfiguration().getTheme();
setCmsPage(false);
setSubThemeDiscriminatorValue("");
}
- /**
- *
- * Setter for the field theme
.
- *
- *
- * @param theme a {@link java.lang.String} object.
- */
- public void setTheme(String theme) {
- this.theme = theme;
- }
/**
*
@@ -1091,6 +1079,11 @@ public String getObjectUrl() {
public String getImageUrl() {
return BeanUtils.getServletPathWithHostAsUrlFromJsfContext() + "/" + PageType.viewImage.getName();
}
+
+ public String getCurrentPageTypeUrl() {
+ return BeanUtils.getServletPathWithHostAsUrlFromJsfContext() + "/" + getCurrentPageType().getName();
+
+ }
/**
*
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/PersistentStorageBean.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/PersistentStorageBean.java
new file mode 100644
index 00000000000..eba3e1c4cf3
--- /dev/null
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/PersistentStorageBean.java
@@ -0,0 +1,58 @@
+/**
+ * This file is part of the Goobi viewer - a content presentation and management application for digitized objects.
+ *
+ * Visit these websites for more information.
+ * - http://www.intranda.com
+ * - http://digiverso.com
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If not, see .
+ */
+package io.goobi.viewer.managedbeans;
+
+import java.io.Serializable;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Named;
+
+import org.apache.commons.lang3.tuple.Pair;
+
+/**
+ * Used for application wide storage of objects accessible to other managed objects
+ *
+ * @author florian
+ *
+ */
+@Named
+@ApplicationScoped
+public class PersistentStorageBean implements Serializable {
+
+ private static final long serialVersionUID = -5127431137772735598L;
+
+ private Map> map = new HashMap<>();
+
+
+ public synchronized Object get(String key) {
+ return map.get(key).getLeft();
+ }
+
+ public synchronized boolean olderThan(String key, Instant now) {
+ return map.get(key).getRight().isBefore(now);
+ }
+
+ public synchronized Object put(String key, Object object) {
+ return map.put(key, Pair.of(object, Instant.now()));
+ }
+
+ public boolean contains(String key) {
+ return map.containsKey(key);
+ }
+}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/SearchBean.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/SearchBean.java
index 2b327c74907..1de7ecacf43 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/SearchBean.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/SearchBean.java
@@ -2510,5 +2510,23 @@ public String getBookmarkListSharedKey() {
.orElse("");
return value.replace("KEY::", "");
}
+
+ public String searchInRecord(String queryField, String queryValue) {
+
+ this.getAdvancedQueryGroups().get(0).getQueryItems().get(0).setField(queryField);
+ if(StringUtils.isNotBlank(queryValue)) {
+ this.getAdvancedQueryGroups().get(0).getQueryItems().get(0).setValue(queryValue);
+ }
+ this.getAdvancedQueryGroups().get(0).getQueryItems().get(0).setOperator(SearchItemOperator.IS);
+ this.getAdvancedQueryGroups().get(0).getQueryItems().get(1).setField("searchAdvanced_allFields");
+ this.getAdvancedQueryGroups().get(0).getQueryItems().get(1).setOperator(SearchItemOperator.AUTO);
+ this.setActiveSearchType(1);
+
+ return this.searchAdvanced();
+ }
+
+ public boolean isSolrIndexReachable() {
+ return DataManager.getInstance().getSearchIndex().pingSolrIndex();
+ }
}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/TermsOfUseBean.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/TermsOfUseBean.java
index 49aa0b0043f..907d96d8957 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/TermsOfUseBean.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/TermsOfUseBean.java
@@ -95,6 +95,9 @@ public void setActivated(boolean active) {
}
public boolean isActivated() {
+ if (termsOfUse == null) {
+ return false;
+ }
return this.termsOfUse.isActive();
}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/UserBean.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/UserBean.java
index 32ad11234d5..50cdaaba5b7 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/UserBean.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/UserBean.java
@@ -51,7 +51,6 @@
import io.goobi.viewer.controller.DataManager;
import io.goobi.viewer.controller.NetTools;
import io.goobi.viewer.controller.StringTools;
-import io.goobi.viewer.controller.TranskribusUtils;
import io.goobi.viewer.exceptions.DAOException;
import io.goobi.viewer.exceptions.HTTPException;
import io.goobi.viewer.exceptions.IndexUnreachableException;
@@ -70,6 +69,7 @@
import io.goobi.viewer.model.security.authentication.LoginResult;
import io.goobi.viewer.model.security.user.User;
import io.goobi.viewer.model.security.user.UserGroup;
+import io.goobi.viewer.model.transkribus.TranskribusUtils;
import io.goobi.viewer.model.urlresolution.ViewHistory;
import io.goobi.viewer.model.urlresolution.ViewerPath;
import io.goobi.viewer.model.viewer.Feedback;
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/tabledata/TableDataProvider.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/tabledata/TableDataProvider.java
index 0c1039e2e88..e709a9ab316 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/tabledata/TableDataProvider.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/tabledata/TableDataProvider.java
@@ -604,6 +604,7 @@ public void setFilters(String... columns) {
*/
void resetTotalNumberOfRecords() {
source.resetTotalNumberOfRecords();
+ resetCurrentList();
}
/**
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/utils/BeanUtils.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/utils/BeanUtils.java
index 0a771017111..fda2f16b8f3 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/utils/BeanUtils.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/managedbeans/utils/BeanUtils.java
@@ -50,6 +50,7 @@
import io.goobi.viewer.managedbeans.MetadataBean;
import io.goobi.viewer.managedbeans.NavigationHelper;
import io.goobi.viewer.managedbeans.SearchBean;
+import io.goobi.viewer.managedbeans.ArchiveBean;
import io.goobi.viewer.managedbeans.UserBean;
import io.goobi.viewer.model.security.user.User;
import io.goobi.viewer.servlets.utils.ServletUtils;
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/annotation/PersistentAnnotation.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/annotation/PersistentAnnotation.java
index 9bbf6819e1e..0ec5bf81171 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/annotation/PersistentAnnotation.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/annotation/PersistentAnnotation.java
@@ -21,6 +21,7 @@
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.HashSet;
+import java.util.Optional;
import java.util.Set;
import javax.persistence.Column;
@@ -50,6 +51,8 @@
import io.goobi.viewer.exceptions.PresentationException;
import io.goobi.viewer.exceptions.RecordNotFoundException;
import io.goobi.viewer.exceptions.ViewerConfigurationException;
+import io.goobi.viewer.model.crowdsourcing.campaigns.Campaign;
+import io.goobi.viewer.model.crowdsourcing.campaigns.CampaignRecordStatistic.CampaignRecordStatus;
import io.goobi.viewer.model.crowdsourcing.questions.Question;
import io.goobi.viewer.model.security.user.User;
@@ -69,10 +72,10 @@ public class PersistentAnnotation {
@Column(name = "annotation_id")
private Long id;
- @Column(name = "date_created")
+ @Column(name = "date_created", columnDefinition = "TIMESTAMP")
private LocalDateTime dateCreated;
- @Column(name = "date_modified")
+ @Column(name = "date_modified", columnDefinition = "TIMESTAMP")
private LocalDateTime dateModified;
@Column(name = "motivation")
@@ -636,4 +639,30 @@ public String getAccessCondition() {
public void setAccessCondition(String accessCondition) {
this.accessCondition = accessCondition;
}
+
+ /**
+ * Find the record status of the generator campaign and pi. If the annotation does not belong to a campaign, return {@link CampaignRecordStatus.FINISHED}
+ *
+ * @return The review status for this annotation
+ * @throws DAOException
+ */
+ public CampaignRecordStatus getReviewStatus() throws DAOException {
+ return Optional.ofNullable(getGenerator()).map(Question::getOwner).map(c -> c.getRecordStatus(getTargetPI())).orElse(CampaignRecordStatus.FINISHED);
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("Crowdsourcing Annotation");
+ sb.append("\n\t").append("Body:").append(getBody());
+ sb.append("\n\t").append("Target:").append(getTarget());
+ sb.append("\n\t").append("GeneratorId:").append(getGeneratorId());
+ sb.append("\n\t").append("CreatorId:").append(getCreatorId());
+ sb.append("\n\t").append("ReviewerId:").append(getReviewerId());
+
+ return sb.toString();
+
+ }
}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/annotation/export/AnnotationSheetWriter.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/annotation/export/AnnotationSheetWriter.java
new file mode 100644
index 00000000000..6ee30ac9b7d
--- /dev/null
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/annotation/export/AnnotationSheetWriter.java
@@ -0,0 +1,73 @@
+/**
+ * This file is part of the Goobi viewer - a content presentation and management application for digitized objects.
+ *
+ * Visit these websites for more information.
+ * - http://www.intranda.com
+ * - http://digiverso.com
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If not, see .
+ */
+package io.goobi.viewer.model.annotation.export;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import de.intranda.api.annotation.IResource;
+import de.intranda.api.annotation.wa.TypedResource;
+import io.goobi.viewer.api.rest.resourcebuilders.AnnotationsResourceBuilder;
+import io.goobi.viewer.controller.DataManager;
+import io.goobi.viewer.model.annotation.PersistentAnnotation;
+
+/**
+ * @author florian
+ *
+ */
+public class AnnotationSheetWriter {
+
+ public static final String UNKNOWN_RESOURCE_TYPE = "Unknown";
+
+ private final ExcelRenderer excelRenderer;
+ private final AnnotationsResourceBuilder annotationBuilder = new AnnotationsResourceBuilder(DataManager.getInstance().getRestApiManager().getDataApiManager(), null);
+
+ public AnnotationSheetWriter() {
+ this.excelRenderer = new ExcelRenderer(annotationBuilder);
+ }
+
+ /**
+ * @param os
+ * @param annotations
+ * @throws IOException
+ */
+ public void createExcelSheet(OutputStream os, List annotations) throws IOException {
+
+ Map> annoMap = annotations.stream().collect(Collectors.groupingBy(this::getBodyType));
+
+ HSSFWorkbook wb = excelRenderer.render(annoMap);
+ wb.write(os);
+ os.flush();
+ }
+
+ private String getBodyType(PersistentAnnotation anno) {
+ try {
+ IResource body = annotationBuilder.getBodyAsResource(anno);
+ if(body instanceof TypedResource) {
+ return ((TypedResource) body).getType();
+ }
+ } catch (IOException e) {
+ }
+ return UNKNOWN_RESOURCE_TYPE;
+ }
+
+}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/annotation/export/ExcelRenderer.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/annotation/export/ExcelRenderer.java
new file mode 100644
index 00000000000..a2211353209
--- /dev/null
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/annotation/export/ExcelRenderer.java
@@ -0,0 +1,225 @@
+/**
+ * This file is part of the Goobi viewer - a content presentation and management application for digitized objects.
+ *
+ * Visit these websites for more information.
+ * - http://www.intranda.com
+ * - http://digiverso.com
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If not, see .
+ */
+package io.goobi.viewer.model.annotation.export;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+import java.util.Optional;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFCellStyle;
+import org.apache.poi.hssf.usermodel.HSSFFont;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.Row;
+import org.bouncycastle.asn1.cmc.BodyPartID;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+
+import de.intranda.api.annotation.IResource;
+import de.intranda.api.annotation.wa.Dataset;
+import de.intranda.api.annotation.wa.TextualResource;
+import de.intranda.api.annotation.wa.TypedResource;
+import io.goobi.viewer.api.rest.resourcebuilders.AnnotationsResourceBuilder;
+import io.goobi.viewer.controller.DataManager;
+import io.goobi.viewer.exceptions.DAOException;
+import io.goobi.viewer.model.annotation.PersistentAnnotation;
+import io.goobi.viewer.model.crowdsourcing.campaigns.Campaign;
+import io.goobi.viewer.model.crowdsourcing.questions.Question;
+import io.goobi.viewer.model.security.user.User;
+
+public class ExcelRenderer {
+
+ private static final Logger logger = LoggerFactory.getLogger(ExcelRenderer.class);
+
+ private final AnnotationsResourceBuilder annotationBuilder;
+
+ /**
+ * @param annotationBuilder
+ */
+ public ExcelRenderer(AnnotationsResourceBuilder annotationBuilder) {
+ this.annotationBuilder = annotationBuilder;
+ }
+
+ public HSSFWorkbook render(Map> annotationMap) {
+ if (annotationMap == null) {
+ throw new IllegalArgumentException("No annotations given");
+ }
+
+ if (annotationMap.size() == 0) {
+ throw new IllegalArgumentException("Empty annotations map");
+ }
+
+ HSSFWorkbook wb = new HSSFWorkbook();
+
+ for (String type : annotationMap.keySet()) {
+ HSSFSheet sheet = wb.createSheet(type);
+ sheet.setDefaultColumnWidth(30);
+ short height = 300;
+ sheet.setDefaultRowHeight(height);
+ createHeaderRow(sheet);
+ List annotations = annotationMap.get(type);
+ createDataRows(sheet, annotations);
+ }
+
+ return wb;
+ }
+
+ /**
+ * @param sheet
+ * @param annotations
+ */
+ public void createDataRows(HSSFSheet sheet, List annotations) {
+ int rowCounter = 1;
+ for (PersistentAnnotation annotation : annotations) {
+ try {
+ createDataRow(annotation, sheet, rowCounter);
+ } catch (DAOException e) {
+ logger.error("Error creating data row for annotation " + annotation);
+ }
+ rowCounter++;
+ }
+ }
+
+ /**
+ * @param annotation
+ * @param sheet
+ * @param rowCounter
+ * @throws DAOException
+ */
+ public void createDataRow(PersistentAnnotation annotation, HSSFSheet sheet, int rowCounter) throws DAOException {
+ HSSFRow row = sheet.createRow(rowCounter);
+ row.setRowStyle(getDataCellStyle(sheet.getWorkbook()));
+ HSSFCell idCell = row.createCell(0, CellType.STRING);
+ idCell.setCellValue(annotation.getId().toString());
+ HSSFCell recordCell = row.createCell(1, CellType.STRING);
+ recordCell.setCellValue(annotation.getTargetPI());
+ HSSFCell pageCell = row.createCell(2, CellType.STRING);
+ String pageOrder = Optional.ofNullable(annotation.getTargetPageOrder()).map(i -> i.toString()).orElse("");
+ pageCell.setCellValue(pageOrder);
+ HSSFCell campaignCell = row.createCell(3, CellType.STRING);
+ campaignCell.setCellValue(Optional.ofNullable(annotation.getGenerator()).map(Question::getOwner).map(Campaign::getTitle).orElse(""));
+ HSSFCell authorCell = row.createCell(4, CellType.STRING);
+ authorCell.setCellValue(Optional.ofNullable(annotation.getCreator()).map(User::getDisplayName).orElse(""));
+ HSSFCell bodyCell = row.createCell(5, CellType.STRING);
+ bodyCell.setCellValue(getBodyValues(annotation).get(0));
+
+ setCellStyles(row, getDataCellStyle(sheet.getWorkbook()));
+
+ }
+
+
+
+ /**
+ * @param sheet
+ */
+ public void createHeaderRow(HSSFSheet sheet) {
+ HSSFRow titleRow = sheet.createRow(0);
+ titleRow.setRowStyle(getHeaderCellStyle(sheet.getWorkbook()));
+ HSSFCell idCell = titleRow.createCell(0, CellType.STRING);
+ idCell.setCellStyle(getHeaderCellStyle(sheet.getWorkbook()));
+ idCell.setCellValue("ID");
+ HSSFCell recordCell = titleRow.createCell(1, CellType.STRING);
+ recordCell.setCellValue("in");
+ HSSFCell pageCell = titleRow.createCell(2, CellType.STRING);
+ pageCell.setCellValue("on page");
+ HSSFCell campaignCell = titleRow.createCell(3, CellType.STRING);
+ campaignCell.setCellValue("Campaign");
+ HSSFCell authorCell = titleRow.createCell(4, CellType.STRING);
+ authorCell.setCellValue("Author");
+ HSSFCell bodyCell = titleRow.createCell(5, CellType.STRING);
+ bodyCell.setCellValue("values");
+
+ setCellStyles(titleRow, getHeaderCellStyle(sheet.getWorkbook()));
+ }
+
+ private List getBodyValues(PersistentAnnotation anno) {
+ try {
+ IResource bodyResource = annotationBuilder.getBodyAsResource(anno);
+ String type = "unknown";
+ if(bodyResource instanceof TypedResource) {
+ type = ((TypedResource) bodyResource).getType();
+ }
+ switch(type) {
+ case "TextualBody":
+ TextualResource res = (TextualResource)bodyResource;
+ return Collections.singletonList(res.getText());
+ case "AuthorityResource":
+ return Collections.singletonList(bodyResource.getId().toString());
+ case "Dataset":
+ Dataset dataset = (Dataset)bodyResource;
+ StringBuilder sb = new StringBuilder();
+ for (Entry> entry : dataset.getData().entrySet()) {
+ String label = entry.getKey();
+ String value = entry.getValue().stream().collect(Collectors.joining(", "));
+ sb.append(label).append(": ").append(value).append("\t");
+ }
+ return Collections.singletonList(sb.toString().trim());
+ default:
+ return Collections.singletonList(anno.getBody());
+
+ }
+ } catch(IOException e) {
+ logger.error("Error writing body fields", e);
+ return Collections.singletonList(anno.getBody());
+ }
+ }
+
+ /**
+ *
+ */
+ private void setCellStyles(Row row, CellStyle style) {
+ Iterator cells = row.cellIterator();
+ while(cells.hasNext()) {
+ Cell cell = cells.next();
+ cell.setCellStyle(style);
+ }
+ }
+
+ private CellStyle getHeaderCellStyle(HSSFWorkbook wb) {
+ HSSFCellStyle style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.LEFT);
+ HSSFFont font = wb.createFont();
+ font.setBold(true);
+ style.setFont(font);
+ return style;
+ }
+
+ /**
+ * @param workbook
+ * @return
+ */
+ private HSSFCellStyle getDataCellStyle(HSSFWorkbook wb) {
+ HSSFCellStyle style = wb.createCellStyle();
+// style.setAlignment(HorizontalAlignment.RIGHT);
+ HSSFFont font = wb.createFont();
+ style.setFont(font);
+ return style;
+ }
+}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/archives/ArchiveEntry.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/archives/ArchiveEntry.java
new file mode 100644
index 00000000000..0ace07eefbb
--- /dev/null
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/archives/ArchiveEntry.java
@@ -0,0 +1,737 @@
+package io.goobi.viewer.model.archives;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.SolrException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.goobi.viewer.controller.DataManager;
+import io.goobi.viewer.controller.SolrConstants;
+import io.goobi.viewer.exceptions.IndexUnreachableException;
+import io.goobi.viewer.exceptions.PresentationException;
+
+public class ArchiveEntry {
+
+ private static final Logger logger = LoggerFactory.getLogger(ArchiveEntry.class);
+
+ // parent node
+ private ArchiveEntry parentNode;
+ // list contains all child elements
+ private List subEntryList = new ArrayList<>();
+ // order number of the current element within the current hierarchy
+ private Integer orderNumber;
+ // hierarchy level
+ private Integer hierarchyLevel;
+ // c@id
+ private String id;
+ // display label
+ private String label;
+ // node is open/closed
+ private boolean displayChildren;
+ // node is search hit
+ private boolean searchHit;
+ // node type - @level
+ private String nodeType;
+ // display node in a search result
+ private boolean displaySearch;
+ // true if the validation of all metadata fields was successful
+ private boolean valid = true;
+
+ private String descriptionLevel;
+
+ private boolean visible = true;
+
+ private boolean expanded = false;
+
+ private String associatedRecordPi;
+
+ /* 1. metadata for Identity Statement Area */
+ // Reference code(s)
+ // Title
+ // private String unittitle; // did/unittitle
+ // Date(s)
+ // Level of description
+ // Extent and medium of the unit of description (quantity, bulk, or size)
+ private List identityStatementAreaList = new ArrayList<>();
+
+ /* 2. Context Area */
+ // Name of creator(s)
+ // Administrative | Biographical history
+ // Archival history
+ // Immediate source of acquisition or transfer
+ private List contextAreaList = new ArrayList<>();
+
+ /* 3. Content and Structure Area */
+ // Scope and content
+ // Appraisal, destruction and scheduling information
+ // Accruals
+ // System of arrangement
+ private List contentAndStructureAreaAreaList = new ArrayList<>();
+
+ /* 4. Condition of Access and Use Area */
+ // Conditions governing access
+ // Conditions governing reproduction
+ // Language | Scripts of material
+ // Physical characteristics and technical requirements
+ // Finding aids
+ private List accessAndUseAreaList = new ArrayList<>();
+
+ /* 5. Allied Materials Area */
+ // Existence and location of originals
+ // Existence and location of copies
+ // Related units of description
+ // Publication note
+ private List alliedMaterialsAreaList = new ArrayList<>();
+
+ /* 6. Note Area */
+ // Note
+ private List notesAreaList = new ArrayList<>();
+
+ /* 7. Description Control Area */
+ // Archivist's Note
+ // Rules or Conventions
+ // Date(s) of descriptions
+ private List descriptionControlAreaList = new ArrayList<>();
+
+ public ArchiveEntry(Integer order, Integer hierarchy) {
+ this.orderNumber = order;
+ this.hierarchyLevel = hierarchy;
+ }
+
+ public void addSubEntry(ArchiveEntry other) {
+ subEntryList.add(other);
+ other.setParentNode(this);
+ }
+
+ public void removeSubEntry(ArchiveEntry other) {
+ subEntryList.remove(other);
+ reOrderElements();
+ }
+
+ public void reOrderElements() {
+ int order = 0;
+ for (ArchiveEntry entry : subEntryList) {
+ entry.setOrderNumber(order++);
+ }
+ }
+
+ /**
+ *
+ * @param ignoreDisplayChildren
+ * @return
+ */
+ public List getAsFlatList(boolean ignoreDisplayChildren) {
+ List list = new LinkedList<>();
+ list.add(this);
+ if (displayChildren || ignoreDisplayChildren) {
+ if (subEntryList != null && !subEntryList.isEmpty()) {
+ for (ArchiveEntry ds : subEntryList) {
+ list.addAll(ds.getAsFlatList(ignoreDisplayChildren));
+ // logger.trace("ID: {}, level {}", ds.getId(), ds.getHierarchy());
+ }
+ }
+ }
+ return list;
+ }
+
+ public void findAssociatedRecordPi() throws PresentationException {
+ if (id == null) {
+ associatedRecordPi = "";
+ return;
+ }
+ try {
+ SolrDocument doc = DataManager.getInstance()
+ .getSearchIndex()
+ .getFirstDoc("+" + SolrConstants.ARCHIVE_ENTRY_ID + ":" + id, Collections.singletonList(SolrConstants.PI));
+ if (doc != null) {
+ associatedRecordPi = (String) doc.getFieldValue(SolrConstants.PI);
+ }
+ if (associatedRecordPi == null) {
+ associatedRecordPi = "";
+ }
+ } catch (SolrException | IndexUnreachableException e) {
+ logger.error("Error reading solr database:" + e);
+ }
+ }
+
+ public boolean isHasChildren() {
+ return !subEntryList.isEmpty();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ ArchiveEntry other = (ArchiveEntry) obj;
+ if (hierarchyLevel == null) {
+ if (other.hierarchyLevel != null) {
+ return false;
+ }
+ } else if (!hierarchyLevel.equals(other.hierarchyLevel)) {
+ return false;
+ }
+ if (orderNumber == null) {
+ if (other.orderNumber != null) {
+ return false;
+ }
+ } else if (!orderNumber.equals(other.orderNumber)) {
+ return false;
+ }
+ if (parentNode == null && other.parentNode == null) {
+ return true;
+ }
+ if (parentNode == null && other.parentNode != null) {
+ return false;
+ }
+ if (parentNode != null && other.parentNode == null) {
+ return false;
+ }
+
+ if (!parentNode.getOrderNumber().equals(other.parentNode.getOrderNumber())) {
+ return false;
+ }
+ if (!parentNode.getHierarchyLevel().equals(other.parentNode.getHierarchyLevel())) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((hierarchyLevel == null) ? 0 : hierarchyLevel.hashCode());
+ result = prime * result + ((orderNumber == null) ? 0 : orderNumber.hashCode());
+ result = prime * result + ((parentNode == null) ? 0 : parentNode.getHierarchyLevel().hashCode());
+ result = prime * result + ((parentNode == null) ? 0 : parentNode.getOrderNumber().hashCode());
+ return result;
+ }
+
+ public void updateHierarchy() {
+ // root node
+ if (parentNode == null) {
+ hierarchyLevel = 0;
+ } else {
+ hierarchyLevel = parentNode.getHierarchyLevel() + 1;
+ }
+
+ for (ArchiveEntry child : subEntryList) {
+ child.updateHierarchy();
+ }
+ }
+
+ public void markAsFound(boolean keepChildrenVisible) {
+ displaySearch = true;
+ searchHit = true;
+
+ if (parentNode != null) {
+ ArchiveEntry node = parentNode;
+ while (!node.isDisplaySearch()) {
+ node.setDisplaySearch(true);
+ if (node.parentNode != null) {
+ node = node.parentNode;
+ }
+ }
+ }
+ if (keepChildrenVisible) {
+ for (ArchiveEntry child : this.subEntryList) {
+ child.setDisplaySearch(true, true);
+ }
+ }
+ }
+
+ public void resetFoundList() {
+ // logger.trace("resetFoundList: {}", id);
+ displaySearch = false;
+ searchHit = false;
+ if (subEntryList != null) {
+ for (ArchiveEntry ds : subEntryList) {
+ ds.resetFoundList();
+ }
+ }
+ }
+
+ public List getSearchList() {
+ List list = new LinkedList<>();
+ if (displaySearch) {
+ list.add(this);
+ if (subEntryList != null) {
+ for (ArchiveEntry child : subEntryList) {
+ list.addAll(child.getSearchList());
+ }
+ }
+ }
+ return list;
+ }
+
+ /**
+ *
+ * @param offset
+ */
+ public void shiftHierarchy(int offset) {
+ this.hierarchyLevel += offset;
+ if (isHasChildren()) {
+ for (ArchiveEntry sub : subEntryList) {
+ sub.shiftHierarchy(offset);
+ }
+ }
+ }
+
+ /**
+ * Expands and sets visible all ancestors of this node and expands siblings of this node.
+ */
+ public void expandUp() {
+ if (parentNode == null) {
+ return;
+ }
+
+ parentNode.setVisible(visible);
+ parentNode.expand();
+ parentNode.expandUp();
+ }
+
+ /**
+ * Expands this entry and sets all sub-entries visible if their immediate parent is expanded.
+ */
+ public void expand() {
+ // logger.trace("expand: {}", id);
+ if (!isHasChildren()) {
+ return;
+ }
+
+ setExpanded(true);
+ setChildrenVisibility(true);
+ }
+
+ /**
+ * Collapses this entry and hides all sub-entries.
+ */
+ public void collapse() {
+ // logger.trace("collapse: {}", id);
+ if (!isHasChildren()) {
+ return;
+ }
+
+ setExpanded(false);
+ setChildrenVisibility(false);
+ }
+
+ /**
+ *
+ * @param visible
+ */
+ void setChildrenVisibility(boolean visible) {
+ if (!isHasChildren()) {
+ return;
+ }
+
+ for (ArchiveEntry sub : subEntryList) {
+ sub.setVisible(visible);
+ if (sub.isExpanded() && sub.isHasChildren()) {
+ sub.setChildrenVisibility(visible);
+ }
+ }
+ }
+
+ /**
+ * @return the parentNode
+ */
+ public ArchiveEntry getParentNode() {
+ return parentNode;
+ }
+
+ /**
+ * @param parentNode the parentNode to set
+ */
+ public void setParentNode(ArchiveEntry parentNode) {
+ this.parentNode = parentNode;
+ }
+
+ /**
+ * @return the subEntryList
+ */
+ public List getSubEntryList() {
+ return subEntryList;
+ }
+
+ /**
+ * @param subEntryList the subEntryList to set
+ */
+ public void setSubEntryList(List subEntryList) {
+ this.subEntryList = subEntryList;
+ }
+
+ /**
+ * @return the orderNumber
+ */
+ public Integer getOrderNumber() {
+ return orderNumber;
+ }
+
+ /**
+ * @param orderNumber the orderNumber to set
+ */
+ public void setOrderNumber(Integer orderNumber) {
+ this.orderNumber = orderNumber;
+ }
+
+ /**
+ * @return the hierarchyLevel
+ */
+ public Integer getHierarchyLevel() {
+ return hierarchyLevel;
+ }
+
+ /**
+ * @param hierarchyLevel the hierarchyLevel to set
+ */
+ public void setHierarchyLevel(Integer hierarchyLevel) {
+ this.hierarchyLevel = hierarchyLevel;
+ }
+
+ /**
+ * @return the id
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * @param id the id to set
+ */
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ /**
+ * @return the label
+ */
+ public String getLabel() {
+ return label;
+ }
+
+ /**
+ * @param label the label to set
+ */
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ /**
+ * @return the displayChildren
+ */
+ public boolean isDisplayChildren() {
+ return displayChildren;
+ }
+
+ /**
+ * @param displayChildren the displayChildren to set
+ */
+ public void setDisplayChildren(boolean displayChildren) {
+ this.displayChildren = displayChildren;
+ }
+
+ /**
+ * @return the searchHit
+ */
+ public boolean isSearchHit() {
+ return searchHit;
+ }
+
+ /**
+ * @param searchHit the searchHit to set
+ */
+ public void setSearchHit(boolean searchHit) {
+ this.searchHit = searchHit;
+ }
+
+ /**
+ * @return the nodeType
+ */
+ public String getNodeType() {
+ return nodeType;
+ }
+
+ /**
+ * @param nodeType the nodeType to set
+ */
+ public void setNodeType(String nodeType) {
+ this.nodeType = nodeType;
+ }
+
+ /**
+ * @return the displaySearch
+ */
+ public boolean isDisplaySearch() {
+ return displaySearch;
+ }
+
+ /**
+ * @param displaySearch the displaySearch to set
+ */
+ public void setDisplaySearch(boolean displaySearch) {
+ this.setDisplaySearch(displaySearch, false);
+ }
+
+ public void setDisplaySearch(boolean displaySearch, boolean recursive) {
+ this.displaySearch = displaySearch;
+ if (recursive) {
+ for (ArchiveEntry child : this.subEntryList) {
+ child.setDisplaySearch(displaySearch, recursive);
+ }
+ }
+ }
+
+ public ArchiveMetadataField getIdentityStatementAreaField(String name) {
+ for (ArchiveMetadataField field : identityStatementAreaList) {
+ if (field.getLabel().equals(name)) {
+ return field;
+ }
+ }
+
+ return null;
+ }
+
+ public List getAllAreaLists() {
+ logger.trace("getAllAreaLists ({})", id);
+ List ret = new ArrayList<>(getIdentityStatementAreaList().size()
+ + getContextAreaList().size()
+ + getContentAndStructureAreaAreaList().size()
+ + getAccessAndUseAreaList().size()
+ + getAlliedMaterialsAreaList().size()
+ + getNotesAreaList().size()
+ + getDescriptionControlAreaList().size());
+ ret.addAll(getIdentityStatementAreaList());
+ ret.addAll(getContextAreaList());
+ ret.addAll(getContentAndStructureAreaAreaList());
+ ret.addAll(getAccessAndUseAreaList());
+ ret.addAll(getAlliedMaterialsAreaList());
+ ret.addAll(getNotesAreaList());
+ ret.addAll(getDescriptionControlAreaList());
+
+ logger.trace("getAllAreaLists END");
+ return ret;
+ }
+
+ /**
+ * @return the identityStatementAreaList
+ */
+ public List getIdentityStatementAreaList() {
+ // logger.trace("getIdentityStatementAreaList ({})", id);
+ return identityStatementAreaList;
+ }
+
+ /**
+ * @param identityStatementAreaList the identityStatementAreaList to set
+ */
+ public void setIdentityStatementAreaList(List identityStatementAreaList) {
+ this.identityStatementAreaList = identityStatementAreaList;
+ }
+
+ /**
+ * @return the contextAreaList
+ */
+ public List getContextAreaList() {
+ // logger.trace("getContextAreaList ({})", id);
+ return contextAreaList;
+ }
+
+ /**
+ * @param contextAreaList the contextAreaList to set
+ */
+ public void setContextAreaList(List contextAreaList) {
+ this.contextAreaList = contextAreaList;
+ }
+
+ /**
+ * @return the contentAndStructureAreaAreaList
+ */
+ public List getContentAndStructureAreaAreaList() {
+ // logger.trace("getContentAndStructureAreaAreaList ({})", id);
+ return contentAndStructureAreaAreaList;
+ }
+
+ /**
+ * @param contentAndStructureAreaAreaList the contentAndStructureAreaAreaList to set
+ */
+ public void setContentAndStructureAreaAreaList(List contentAndStructureAreaAreaList) {
+ this.contentAndStructureAreaAreaList = contentAndStructureAreaAreaList;
+ }
+
+ /**
+ * @return the accessAndUseAreaList
+ */
+ public List getAccessAndUseAreaList() {
+ // logger.trace("getAccessAndUseAreaList ({})", id);
+ return accessAndUseAreaList;
+ }
+
+ /**
+ * @param accessAndUseAreaList the accessAndUseAreaList to set
+ */
+ public void setAccessAndUseAreaList(List accessAndUseAreaList) {
+ this.accessAndUseAreaList = accessAndUseAreaList;
+ }
+
+ /**
+ * @return the alliedMaterialsAreaList
+ */
+ public List getAlliedMaterialsAreaList() {
+ // logger.trace("getAlliedMaterialsAreaList ({})", id);
+ return alliedMaterialsAreaList;
+ }
+
+ /**
+ * @param alliedMaterialsAreaList the alliedMaterialsAreaList to set
+ */
+ public void setAlliedMaterialsAreaList(List alliedMaterialsAreaList) {
+ this.alliedMaterialsAreaList = alliedMaterialsAreaList;
+ }
+
+ /**
+ * @return the notesAreaList
+ */
+ public List getNotesAreaList() {
+ // logger.trace("getNotesAreaList ({})", id);
+ return notesAreaList;
+ }
+
+ /**
+ * @param notesAreaList the notesAreaList to set
+ */
+ public void setNotesAreaList(List notesAreaList) {
+ this.notesAreaList = notesAreaList;
+ }
+
+ /**
+ * @return the descriptionControlAreaList
+ */
+ public List getDescriptionControlAreaList() {
+ // logger.trace("getDescriptionControlAreaList ({})", id);
+ return descriptionControlAreaList;
+ }
+
+ /**
+ * @param descriptionControlAreaList the descriptionControlAreaList to set
+ */
+ public void setDescriptionControlAreaList(List descriptionControlAreaList) {
+ this.descriptionControlAreaList = descriptionControlAreaList;
+ }
+
+ /**
+ * @return the valid
+ */
+ public boolean isValid() {
+ return valid;
+ }
+
+ /**
+ * @param valid the valid to set
+ */
+ public void setValid(boolean valid) {
+ this.valid = valid;
+ }
+
+ /**
+ * @return the descriptionLevel
+ */
+ public String getDescriptionLevel() {
+ return descriptionLevel;
+ }
+
+ /**
+ * @param descriptionLevel the descriptionLevel to set
+ */
+ public void setDescriptionLevel(String descriptionLevel) {
+ this.descriptionLevel = descriptionLevel;
+ }
+
+
+ /**
+ * @return the visible
+ */
+ public boolean isVisible() {
+ return visible;
+ }
+
+ /**
+ * @param visible the visible to set
+ */
+ public void setVisible(boolean visible) {
+ this.visible = visible;
+ }
+
+ /**
+ * @return the expanded
+ */
+ public boolean isExpanded() {
+ return expanded;
+ }
+
+ /**
+ * @param expanded the expanded to set
+ */
+ public void setExpanded(boolean expanded) {
+ this.expanded = expanded;
+ }
+
+ /**
+ * @return the hasChild
+ */
+ public boolean isHasChild() {
+ return !subEntryList.isEmpty();
+ }
+
+ /**
+ * @return the associatedRecordPi
+ * @throws IndexUnreachableException
+ * @throws PresentationException
+ */
+ public String getAssociatedRecordPi() throws PresentationException, IndexUnreachableException {
+ if (associatedRecordPi == null) {
+ findAssociatedRecordPi();
+ }
+
+ return associatedRecordPi;
+ }
+
+ /**
+ * Get the parent node hierarchy of this node, optionally including the node itself The list is sorted with hightest hierarchy level first, so the
+ * node itself will always be the last element, if included
+ *
+ * @param includeSelf
+ * @return
+ */
+ public List getAncestors(boolean includeSelf) {
+ List ancestors = new ArrayList<>();
+ if (includeSelf) {
+ ancestors.add(this);
+ }
+ ArchiveEntry parent = this.parentNode;
+ while (parent != null) {
+ ancestors.add(parent);
+ parent = parent.parentNode;
+ }
+ Collections.reverse(ancestors);
+ return ancestors;
+
+ }
+
+ @Override
+ public String toString() {
+ return id;
+ }
+}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/archives/ArchiveMetadataField.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/archives/ArchiveMetadataField.java
new file mode 100644
index 00000000000..d807ddf2c4c
--- /dev/null
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/archives/ArchiveMetadataField.java
@@ -0,0 +1,167 @@
+package io.goobi.viewer.model.archives;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang.StringUtils;
+
+public class ArchiveMetadataField {
+
+ /** contains the internal name of the field. The value can be used to translate the field in the messages files */
+ private String label;
+
+ /**
+ * metadata level, allowed values are 1-7:
+ *
+ * - 1: metadata for Identity Statement Area
+ * - 2: Context Area
+ * - 3: Content and Structure Area
+ * - 4: Condition of Access and Use Area
+ * - 5: Allied Materials Area
+ * - 6: Note Area
+ * - 7: Description Control Area
+ *
+ */
+ private Integer type;
+
+ /** contains a relative path to the ead value. The root of the xpath is either the {@code} element or the {@code} element */
+ private String xpath;
+
+ /** type of the xpath return value, can be text, attribute, element (default) */
+ private String xpathType;
+
+ /** contains the metadata values */
+ private List values;
+
+ /** links to the ead node. Required to set the title field for the entry while parsing metadata */
+ // @ToString.Exclude
+ private ArchiveEntry eadEntry;
+
+
+
+
+ public ArchiveMetadataField(String label, Integer type, String xpath, String xpathType) {
+ this.label = label;
+ this.type = type;
+ this.xpath = xpath;
+ this.xpathType = xpathType;
+ }
+
+ public boolean isFilled() {
+ if (values == null || values.isEmpty()) {
+ return false;
+ }
+ for (FieldValue val : values) {
+ if (StringUtils.isNotBlank(val.getValue())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void addFieldValue(FieldValue value) {
+ if (values == null) {
+ values = new ArrayList<>();
+ }
+ values.add(value);
+ }
+
+ public void addValue() {
+ if (values == null) {
+ values = new ArrayList<>();
+ }
+ values.add(new FieldValue(this));
+ }
+
+ /**
+ * @return the name
+ */
+ public String getLabel() {
+ return label;
+ }
+
+ /**
+ * @param name the name to set
+ */
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ /**
+ * @return the level
+ */
+ public Integer getType() {
+ return type;
+ }
+
+ /**
+ * @param level the level to set
+ */
+ public void setType(Integer type) {
+ this.type = type;
+ }
+
+ /**
+ * @return the xpathType
+ */
+ public String getXpathType() {
+ return xpathType;
+ }
+
+ /**
+ * @param xpathType the xpathType to set
+ */
+ public void setXpathType(String xpathType) {
+ this.xpathType = xpathType;
+ }
+
+ public String getValue() {
+ if (values == null || values.isEmpty()) {
+ return null;
+ }
+
+ return values.get(0).getValue();
+ }
+
+ /**
+ * @return the values
+ */
+ public List getValues() {
+ return values;
+ }
+
+ /**
+ * @param values the values to set
+ */
+ public void setValues(List values) {
+ this.values = values;
+ }
+
+ /**
+ * @return the xpath
+ */
+ public String getXpath() {
+ return xpath;
+ }
+
+ /**
+ * @param xpath the xpath to set
+ */
+ public void setXpath(String xpath) {
+ this.xpath = xpath;
+ }
+
+ /**
+ * @return the eadEntry
+ */
+ public ArchiveEntry getEadEntry() {
+ return eadEntry;
+ }
+
+ /**
+ * @param eadEntry the eadEntry to set
+ */
+ public void setEadEntry(ArchiveEntry eadEntry) {
+ this.eadEntry = eadEntry;
+ }
+}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/archives/ArchiveResource.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/archives/ArchiveResource.java
new file mode 100644
index 00000000000..738ea30eda5
--- /dev/null
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/archives/ArchiveResource.java
@@ -0,0 +1,50 @@
+/**
+ * This file is part of the Goobi viewer - a content presentation and management application for digitized objects.
+ *
+ * Visit these websites for more information.
+ * - http://www.intranda.com
+ * - http://digiverso.com
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If not, see .
+ */
+package io.goobi.viewer.model.archives;
+
+import java.time.Instant;
+
+/**
+ * @author florian
+ *
+ */
+public class ArchiveResource {
+
+ public final String databaseName;
+ public final String resourceName;
+ public final Instant lastModified;
+
+ /**
+ *
+ */
+ public ArchiveResource(String database, String resource, String modifiedDate) {
+ this.databaseName = database;
+ this.resourceName = resource;
+ this.lastModified = Instant.parse(modifiedDate);
+ }
+
+ public String getCombinedName() {
+ return databaseName + " - " + resourceName;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return getCombinedName();
+ }
+}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/archives/ArchiveTree.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/archives/ArchiveTree.java
new file mode 100644
index 00000000000..48a043615d2
--- /dev/null
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/archives/ArchiveTree.java
@@ -0,0 +1,432 @@
+/**
+ * This file is part of the Goobi viewer - a content presentation and management application for digitized objects.
+ *
+ * Visit these websites for more information.
+ * - http://www.intranda.com
+ * - http://digiverso.com
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If not, see .
+ */
+package io.goobi.viewer.model.archives;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Table of contents and associated functionality for a record.
+ */
+public class ArchiveTree implements Serializable {
+
+ private static final long serialVersionUID = 1798213211987072214L;
+
+ private static final Logger logger = LoggerFactory.getLogger(ArchiveTree.class);
+
+ /** Constant DEFAULT_GROUP="_DEFAULT" */
+ public static final String DEFAULT_GROUP = "_DEFAULT";
+
+ public static int defaultCollapseLevel = 1;
+
+ /** Actual root of the document (even if it's not part of the displayed tree) */
+ private ArchiveEntry trueRootElement = null;
+
+ /** Currently displayed tree. Can be partial after a search, etc. */
+ private List flatEntryList;
+
+ /** TOC element map. */
+ private Map> entryMap = new HashMap<>(1);
+
+ /** Actively selected entry */
+ private ArchiveEntry selectedEntry;
+
+ private boolean treeBuilt = false;
+
+ /**
+ *
+ * Constructor for TOC.
+ *
+ */
+ public ArchiveTree() {
+ logger.trace("new EADTree()");
+ }
+
+ public void generate(ArchiveEntry root) {
+ if (root == null) {
+ throw new IllegalArgumentException("root may not be null");
+ }
+
+ setTrueRootElement(root);
+
+ // If root has just one child, use it as new root
+ if (root.getSubEntryList().size() == 1) {
+ root = root.getSubEntryList().get(0);
+ root.shiftHierarchy(-1);
+ }
+
+ List tree = root.getAsFlatList(true);
+ entryMap.put(DEFAULT_GROUP, tree);
+ }
+
+ /**
+ *
+ * getViewForGroup.
+ *
+ *
+ * @param group a {@link java.lang.String} object.
+ * @return a {@link java.util.List} object.
+ */
+ public List getViewForGroup(String group) {
+ if (entryMap != null) {
+ return entryMap.get(group);
+ }
+
+ return null;
+ }
+
+ /**
+ *
+ * getTreeViewForGroup.
+ *
+ *
+ * @param group a {@link java.lang.String} object.
+ * @should call buildTree and set maxTocDepth correctly
+ * @return a {@link java.util.List} object.
+ */
+ public List getTreeViewForGroup(String group) {
+ if (!treeBuilt) {
+ buildTree(group, defaultCollapseLevel);
+ }
+ return getViewForGroup(group);
+ }
+
+ /**
+ *
+ * getFlatView.
+ *
+ *
+ * @return a {@link java.util.List} object.
+ */
+ public List getFlatView() {
+ // logger.trace("getFlatView");
+ return getViewForGroup(DEFAULT_GROUP);
+ }
+
+ /**
+ *
+ * getTreeView.
+ *
+ *
+ * @return a {@link java.util.List} object.
+ */
+ public List getTreeView() {
+ return getTreeViewForGroup(DEFAULT_GROUP);
+ }
+
+ /**
+ *
+ * @param group
+ * @param collapseLevel
+ */
+ private void buildTree(String group, int collapseLevel) {
+ logger.trace("buildTree");
+ if (group == null) {
+ throw new IllegalArgumentException("group may not be null");
+ }
+
+ synchronized (this) {
+ if (entryMap == null) {
+ return;
+ }
+ int lastLevel = 0;
+ int lastParent = 0;
+ for (ArchiveEntry entry : entryMap.get(group)) {
+ // Current element index
+ int index = entryMap.get(group).indexOf(entry);
+ if (lastLevel < entry.getHierarchyLevel() && index > 0) {
+ if (entry.getHierarchyLevel() > collapseLevel) {
+ entryMap.get(group).get(index - 1).setExpanded(false);
+ entry.setVisible(false);
+ } else {
+ entryMap.get(group).get(index - 1).setExpanded(true);
+ }
+
+ for (int i = index + 1; i < entryMap.get(group).size(); i++) {
+ ArchiveEntry tc = entryMap.get(group).get(i);
+ if (tc.getHierarchyLevel() > collapseLevel) {
+ tc.setVisible(false);
+ }
+ }
+
+ }
+ lastParent = index;
+ lastLevel = entry.getHierarchyLevel();
+ }
+ treeBuilt = true;
+ resetCollapseLevel(getRootElement(), collapseLevel);
+ }
+ }
+
+ /**
+ *
+ * @param entry
+ * @param maxDepth
+ */
+ public void resetCollapseLevel(ArchiveEntry entry, int maxDepth) {
+ if (entry == null) {
+ return;
+ }
+
+ if (entry.getHierarchyLevel() <= maxDepth) {
+ entry.setVisible(true);
+ entry.setExpanded(entry.getHierarchyLevel() != maxDepth);
+ } else {
+ entry.setVisible(false);
+ entry.setExpanded(false);
+ }
+
+ if (entry.getSubEntryList() != null && !entry.getSubEntryList().isEmpty()) {
+ for (ArchiveEntry child : entry.getSubEntryList()) {
+ resetCollapseLevel(child, maxDepth);
+ }
+ }
+ }
+
+ /**
+ * @return the selectedEntry
+ */
+ public ArchiveEntry getSelectedEntry() {
+ return selectedEntry;
+ }
+
+ /**
+ * @param selectedEntry the selectedEntry to set
+ */
+ public void setSelectedEntry(ArchiveEntry selectedEntry) {
+ logger.trace("setSelectedEntry: {}", selectedEntry != null ? selectedEntry.getId() : null);
+ this.selectedEntry = selectedEntry;
+ }
+
+ /**
+ * @return the trueRootElement
+ */
+ public ArchiveEntry getTrueRootElement() {
+ return trueRootElement;
+ }
+
+ /**
+ * @param trueRootElement the trueRootElement to set
+ */
+ public void setTrueRootElement(ArchiveEntry trueRootElement) {
+ this.trueRootElement = trueRootElement;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public ArchiveEntry getRootElement() {
+ return getRootElement(DEFAULT_GROUP);
+ }
+
+ /**
+ *
+ * @param group
+ * @return
+ */
+ public ArchiveEntry getRootElement(String group) {
+ if (group == null || entryMap == null || entryMap.isEmpty()) {
+ return null;
+ }
+
+ return entryMap.get(group).get(0);
+ }
+
+ /**
+ *
+ * expandAll.
+ *
+ */
+ public void expandAll() {
+ logger.trace("expandAll");
+ if (entryMap == null) {
+ return;
+ }
+
+ for (ArchiveEntry tcElem : entryMap.get(DEFAULT_GROUP)) {
+ tcElem.setVisible(true);
+ if (tcElem.isHasChild()) {
+ tcElem.setExpanded(true);
+ }
+ }
+ }
+
+ /**
+ *
+ * collapseAll.
+ *
+ */
+ public void collapseAll() {
+ if (entryMap == null) {
+ return;
+ }
+
+ collapseAll(false);
+ }
+
+ /**
+ *
+ * @param collapseAllEntries If true, all invisible child children will also be collapsed
+ */
+ public void collapseAll(boolean collapseAllEntries) {
+ logger.trace("collapseAll");
+ if (entryMap == null) {
+ return;
+ }
+
+ for (ArchiveEntry tcElem : entryMap.get(DEFAULT_GROUP)) {
+ if (tcElem.getHierarchyLevel() == 0) {
+ tcElem.setExpanded(false);
+ } else {
+ if (collapseAllEntries) {
+ tcElem.setExpanded(false);
+ }
+ tcElem.setVisible(false);
+ }
+ }
+ }
+
+ /**
+ * @return the entryMap
+ */
+ Map> getEntryMap() {
+ return entryMap;
+ }
+
+ /**
+ *
+ * getTocElements.
+ *
+ *
+ * @return a {@link java.util.List} object.
+ */
+ public List getTocElements() {
+ if (entryMap != null) {
+ return entryMap.get(DEFAULT_GROUP);
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the hierarchical tree as a flat list
+ *
+ * @return
+ */
+
+ public List getFlatEntryList() {
+ if (flatEntryList == null) {
+ if (trueRootElement != null) {
+ flatEntryList = new LinkedList<>();
+ flatEntryList.addAll(trueRootElement.getAsFlatList(false));
+ }
+ }
+ return flatEntryList;
+ }
+
+ /**
+ *
+ * @return the {@link ArchiveEntry} with the given identifier if it exists in the tree; null otherwise
+ * @param identifier
+ */
+ public ArchiveEntry getEntryById(String identifier) {
+ return findEntry(identifier, getRootElement()).orElse(null);
+ }
+
+ /**
+ *
+ * @param searchValue
+ */
+ public void search(String searchValue) {
+ if (getRootElement() == null) {
+ logger.error("Database not loaded");
+ return;
+ }
+
+ if (StringUtils.isNotBlank(searchValue)) {
+ // hide all elements
+ getRootElement().resetFoundList();
+ // search in all/some metadata fields of all elements?
+
+ // for now: search only labels
+ searchInNode(getRootElement(), searchValue);
+
+ // fill flatList with displayable fields
+ flatEntryList = getRootElement().getSearchList();
+ } else {
+ resetSearch();
+ }
+ }
+
+ /**
+ *
+ * @param node
+ * @param searchValue
+ */
+ static void searchInNode(ArchiveEntry node, String searchValue) {
+ if (node.getId() != null && node.getId().equals(searchValue)) {
+ // ID match
+ node.markAsFound(true);
+ } else if (node.getLabel() != null && node.getLabel().toLowerCase().contains(searchValue.toLowerCase())) {
+ // mark element + all parents as displayable
+ node.markAsFound(true);
+ }
+ if (node.getSubEntryList() != null) {
+ for (ArchiveEntry child : node.getSubEntryList()) {
+ searchInNode(child, searchValue);
+ }
+ }
+ }
+
+ /**
+ * Return this node if it has the given identifier or the first of its descendents with the identifier
+ *
+ * @param identifier
+ * @param topNode
+ * @return
+ */
+ private Optional findEntry(String identifier, ArchiveEntry node) {
+ if (StringUtils.isNotBlank(identifier)) {
+ if (identifier.equals(node.getId())) {
+ return Optional.of(node);
+ }
+ if (node.getSubEntryList() != null) {
+ for (ArchiveEntry child : node.getSubEntryList()) {
+ Optional find = findEntry(identifier, child);
+ if (find.isPresent()) {
+ return find;
+ }
+ }
+ }
+ }
+
+ return Optional.empty();
+ }
+
+ public void resetSearch() {
+ trueRootElement.resetFoundList();
+ flatEntryList = null;
+ }
+}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/archives/BasexEADParser.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/archives/BasexEADParser.java
new file mode 100644
index 00000000000..e91215fafe0
--- /dev/null
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/archives/BasexEADParser.java
@@ -0,0 +1,468 @@
+/**
+ * This file is part of the Goobi viewer - a content presentation and management application for digitized objects.
+ *
+ * Visit these websites for more information.
+ * - http://www.intranda.com
+ * - http://digiverso.com
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If not, see .
+ */
+package io.goobi.viewer.model.archives;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
+import org.apache.commons.lang.StringUtils;
+import org.apache.http.client.ClientProtocolException;
+import org.jdom2.Attribute;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
+import org.jdom2.Namespace;
+import org.jdom2.Text;
+import org.jdom2.filter.Filters;
+import org.jdom2.input.SAXBuilder;
+import org.jdom2.input.sax.XMLReaders;
+import org.jdom2.xpath.XPathExpression;
+import org.jdom2.xpath.XPathFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.goobi.viewer.controller.NetTools;
+import io.goobi.viewer.exceptions.HTTPException;
+
+/**
+ * Loads and parses EAD documents from BaseX databases.
+ */
+public class BasexEADParser {
+
+ private static final Logger logger = LoggerFactory.getLogger(BasexEADParser.class);
+
+ public static final Namespace NAMESPACE_EAD = Namespace.getNamespace("ead", "urn:isbn:1-931666-22-9");
+
+ private static final XPathFactory xFactory = XPathFactory.instance();
+
+ private final String basexUrl;
+
+ private String selectedDatabase;
+
+ private List configuredFields;
+
+ // private List eventList;
+ // private List editorList;
+
+ /**
+ *
+ * @param configFilePath
+ * @throws ConfigurationException
+ */
+ public BasexEADParser(String basexUrl) {
+ this.basexUrl = basexUrl;
+ }
+
+ /**
+ * Get the database names and file names from the basex databases
+ *
+ * @return
+ * @throws HTTPException
+ * @throws IOException
+ * @throws ClientProtocolException
+ */
+ public List getPossibleDatabases() throws ClientProtocolException, IOException, HTTPException {
+ String response = NetTools.getWebContentGET(basexUrl + "databases");
+ if (StringUtils.isBlank(response)) {
+ return Collections.emptyList();
+ }
+
+ Document document;
+ try {
+ document = openDocument(response);
+
+ Element root = document.getRootElement();
+ List databaseList = root.getChildren("database");
+ List ret = new ArrayList<>();
+ for (Element db : databaseList) {
+ String dbName = db.getChildText("name");
+
+ Element details = db.getChild("details");
+ for (Element resource : details.getChildren()) {
+ String resourceName = resource.getText();
+ String lastUpdated = resource.getAttributeValue("modified-date");
+ ArchiveResource eadResource = new ArchiveResource(dbName, resourceName, lastUpdated);
+ ret.add(eadResource);
+ }
+
+ }
+
+ return ret;
+ } catch (JDOMException e) {
+ logger.error("Failed to parse response from " + (basexUrl + "databases"), e);
+ return Collections.emptyList();
+ }
+ }
+
+ /**
+ *
+ * @param database
+ * @return
+ * @throws IOException
+ * @throws IllegalStateException
+ * @throws HTTPException
+ * @throws JDOMException
+ */
+ public Document retrieveDatabaseDocument(String database) throws IOException, IllegalStateException, HTTPException, JDOMException {
+ if (StringUtils.isNotBlank(database)) {
+ String[] parts = database.split(" - ");
+ String url = basexUrl + "db/" + parts[0] + "/" + parts[1];
+ logger.trace("URL: {}", url);
+ String response;
+ response = NetTools.getWebContentGET(url);
+
+ // get xml root element
+ Document document = openDocument(response);
+ return document;
+ }
+ throw new IllegalStateException("Must provide database name before loading database");
+ }
+
+ /**
+ * Loads the given database and parses the EAD document.
+ *
+ * @param database
+ * @param document
+ * @return Root element of the loaded tree
+ * @throws IllegalStateException
+ * @throws IOException
+ * @throws HTTPException
+ * @throws JDOMException
+ * @throws ConfigurationException
+ */
+ public ArchiveEntry loadDatabase(String database, Document document)
+ throws IllegalStateException, IOException, HTTPException, JDOMException, ConfigurationException {
+
+ if (document == null) {
+ document = retrieveDatabaseDocument(database);
+ }
+
+ // parse ead file
+ return parseEadFile(document);
+ }
+
+ public List getDistinctDatabaseNames() throws ClientProtocolException, IOException, HTTPException {
+ List answer = new ArrayList<>();
+ List completeList = getPossibleDatabases();
+ for (ArchiveResource resource : completeList) {
+ String dbName = resource.databaseName;
+ if (!answer.contains(dbName)) {
+ answer.add(dbName);
+ }
+ }
+
+ return answer;
+ }
+
+ /**
+ * Reads the hierarchy from the given EAD document.
+ *
+ * @param document
+ * @return Root element of the tree
+ * @should parse document correctly
+ */
+ ArchiveEntry parseEadFile(Document document) {
+ if (document == null) {
+ throw new IllegalArgumentException("document may not be null");
+ }
+
+ Element collection = document.getRootElement();
+ Element eadElement = collection.getChild("ead", NAMESPACE_EAD);
+ ArchiveEntry rootElement = parseElement(1, 0, eadElement, configuredFields);
+ rootElement.setDisplayChildren(true);
+
+ // Element archdesc = eadElement.getChild("archdesc", NAMESPACE_EAD);
+ // if (archdesc != null) {
+ // Element processinfoElement = archdesc.getChild("processinfo", NAMESPACE_EAD);
+ // if (processinfoElement != null) {
+ // Element list = processinfoElement.getChild("list", NAMESPACE_EAD);
+ // List entries = list.getChildren("item", NAMESPACE_EAD);
+ // eventList = new ArrayList<>(entries.size());
+ // for (Element item : entries) {
+ // editorList.add(item.getText());
+ // }
+ // }
+ // }
+ // Element control = eadElement.getChild("control", NAMESPACE_EAD);
+ // if (control != null) {
+ // Element maintenancehistory = control.getChild("maintenancehistory", NAMESPACE_EAD);
+ // if (maintenancehistory != null) {
+ // List events = maintenancehistory.getChildren("maintenancehistory", NAMESPACE_EAD);
+ // eventList = new ArrayList<>(events.size());
+ // for (Element event : events) {
+ // String type = event.getChildText("eventtype", NAMESPACE_EAD);
+ // String date = event.getChildText("eventdatetime", NAMESPACE_EAD);
+ // eventList.add(new StringPair(type, date));
+ // }
+ // }
+ // }
+
+ return rootElement;
+ }
+
+ /**
+ * read the metadata for the current xml node. - create an {@link ArchiveEntry} - execute the configured xpaths on the current node - add the
+ * metadata to one of the 7 levels - check if the node has sub nodes - call the method recursively for all sub nodes
+ *
+ * @param order
+ * @param hierarchy
+ * @param element
+ * @param configuredFields
+ * @return
+ */
+ private static ArchiveEntry parseElement(int order, int hierarchy, Element element, List configuredFields) {
+ if (element == null) {
+ throw new IllegalArgumentException("element may not be null");
+ }
+ if (configuredFields == null) {
+ throw new IllegalArgumentException("configuredFields may not be null");
+ }
+
+ ArchiveEntry entry = new ArchiveEntry(order, hierarchy);
+
+ for (ArchiveMetadataField emf : configuredFields) {
+
+ List stringValues = new ArrayList<>();
+ if ("text".equalsIgnoreCase(emf.getXpathType())) {
+ XPathExpression engine = xFactory.compile(emf.getXpath(), Filters.text(), null, NAMESPACE_EAD);
+ List values = engine.evaluate(element);
+ for (Text value : values) {
+ String stringValue = value.getValue();
+ stringValues.add(stringValue);
+ }
+ } else if ("attribute".equalsIgnoreCase(emf.getXpathType())) {
+ XPathExpression engine = xFactory.compile(emf.getXpath(), Filters.attribute(), null, NAMESPACE_EAD);
+ List values = engine.evaluate(element);
+
+ for (Attribute value : values) {
+ String stringValue = value.getValue();
+ stringValues.add(stringValue);
+ }
+ } else {
+ XPathExpression engine = xFactory.compile(emf.getXpath(), Filters.element(), null, NAMESPACE_EAD);
+ List values = engine.evaluate(element);
+ for (Element value : values) {
+ String stringValue = value.getValue();
+ stringValues.add(stringValue);
+ }
+ }
+ addFieldToEntry(entry, emf, stringValues);
+ }
+
+ Element eadheader = element.getChild("eadheader", NAMESPACE_EAD);
+
+ entry.setId(element.getAttributeValue("id"));
+
+ if (eadheader != null) {
+ entry.setLabel(
+ eadheader.getChild("filedesc", NAMESPACE_EAD).getChild("titlestmt", NAMESPACE_EAD).getChildText("titleproper", NAMESPACE_EAD));
+ }
+
+ // nodeType
+ // get child elements
+ List clist = null;
+ Element archdesc = element.getChild("archdesc", NAMESPACE_EAD);
+ if (archdesc != null) {
+ String type = archdesc.getAttributeValue("localtype");
+ entry.setNodeType(type);
+ Element dsc = archdesc.getChild("dsc", NAMESPACE_EAD);
+ if (dsc != null) {
+ clist = dsc.getChildren("c", NAMESPACE_EAD);
+ }
+
+ } else {
+ String type = element.getAttributeValue("otherlevel");
+ entry.setNodeType(type);
+
+ }
+
+ if (StringUtils.isBlank(entry.getNodeType())) {
+ entry.setNodeType("folder");
+ }
+
+ // Set description level value
+ entry.setDescriptionLevel(element.getAttributeValue("level"));
+
+ if (clist == null) {
+ clist = element.getChildren("c", NAMESPACE_EAD);
+ }
+ if (clist != null) {
+ int subOrder = 0;
+ int subHierarchy = hierarchy + 1;
+ for (Element c : clist) {
+ ArchiveEntry child = parseElement(subOrder, subHierarchy, c, configuredFields);
+ entry.addSubEntry(child);
+ child.setParentNode(entry);
+ subOrder++;
+ }
+ }
+
+ // generate new id, if id is null
+ if (entry.getId() == null) {
+ entry.setId(String.valueOf(UUID.randomUUID()));
+ }
+
+ return entry;
+ }
+
+ /**
+ * Add the metadata to the configured level
+ *
+ * @param entry
+ * @param emf
+ * @param stringValue
+ */
+
+ private static void addFieldToEntry(ArchiveEntry entry, ArchiveMetadataField emf, List stringValues) {
+ if (StringUtils.isBlank(entry.getLabel()) && emf.getXpath().contains("unittitle") && stringValues != null && !stringValues.isEmpty()) {
+ entry.setLabel(stringValues.get(0));
+ }
+ ArchiveMetadataField toAdd = new ArchiveMetadataField(emf.getLabel(), emf.getType(), emf.getXpath(), emf.getXpathType());
+ toAdd.setEadEntry(entry);
+
+ if (stringValues != null && !stringValues.isEmpty()) {
+
+ // split single value into multiple fields
+ for (String stringValue : stringValues) {
+ FieldValue fv = new FieldValue(toAdd);
+ fv.setValue(stringValue);
+ toAdd.addFieldValue(fv);
+ }
+ } else {
+ FieldValue fv = new FieldValue(toAdd);
+ toAdd.addFieldValue(fv);
+ }
+
+ switch (toAdd.getType()) {
+ case 1:
+ entry.getIdentityStatementAreaList().add(toAdd);
+ break;
+ case 2:
+ entry.getContextAreaList().add(toAdd);
+ break;
+ case 3:
+ entry.getContentAndStructureAreaAreaList().add(toAdd);
+ break;
+ case 4:
+ entry.getAccessAndUseAreaList().add(toAdd);
+ break;
+ case 5:
+ entry.getAlliedMaterialsAreaList().add(toAdd);
+ break;
+ case 6:
+ entry.getNotesAreaList().add(toAdd);
+ break;
+ case 7:
+ entry.getDescriptionControlAreaList().add(toAdd);
+ break;
+ }
+
+ }
+
+ /**
+ * Parse the string response from the basex database into a xml document
+ *
+ * @param response
+ * @return
+ * @throws IOException
+ * @throws JDOMException
+ */
+ private static Document openDocument(String response) throws JDOMException, IOException {
+ // read response
+ SAXBuilder builder = new SAXBuilder(XMLReaders.NONVALIDATING);
+ builder.setFeature("http://xml.org/sax/features/validation", false);
+ builder.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
+ builder.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+
+ Document document = builder.build(new StringReader(response), "utf-8");
+ return document;
+
+ }
+
+ /**
+ *
+ * @param node
+ * @param searchValue
+ */
+ static void searchInNode(ArchiveEntry node, String searchValue) {
+ if (node.getId() != null && node.getId().equals(searchValue)) {
+ // ID match
+ node.markAsFound(true);
+ } else if (node.getLabel() != null && node.getLabel().toLowerCase().contains(searchValue.toLowerCase())) {
+ // mark element + all parents as displayable
+ node.markAsFound(true);
+ }
+ if (node.getSubEntryList() != null) {
+ for (ArchiveEntry child : node.getSubEntryList()) {
+ searchInNode(child, searchValue);
+ }
+ }
+ }
+
+ /**
+ * Loads fields from the given configuration node.
+ *
+ * @param metadataConfig
+ * @return
+ * @throws ConfigurationException
+ */
+ public BasexEADParser readConfiguration(HierarchicalConfiguration metadataConfig) throws ConfigurationException {
+ if (metadataConfig == null) {
+ throw new ConfigurationException("No basexMetadata configurations found");
+ }
+
+ metadataConfig.setListDelimiter('&');
+ metadataConfig.setExpressionEngine(new XPathExpressionEngine());
+
+ try {
+ List configurations = metadataConfig.configurationsAt("/metadata");
+ if (configurations == null) {
+ throw new ConfigurationException("No basexMetadata configurations found");
+ }
+ configuredFields = new ArrayList<>(configurations.size());
+ for (HierarchicalConfiguration hc : configurations) {
+ ArchiveMetadataField field = new ArchiveMetadataField(hc.getString("[@label]"), hc.getInt("[@type]"), hc.getString("[@xpath]"),
+ hc.getString("[@xpathType]", "element"));
+ configuredFields.add(field);
+ }
+ } catch (Exception e) {
+ throw new ConfigurationException("Error reading basexMetadata configuration", e);
+ }
+
+ return this;
+ }
+
+ /**
+ * @return the selectedDatabase
+ */
+ public String getSelectedDatabase() {
+ return selectedDatabase;
+ }
+
+ /**
+ * @return the basexUrl
+ */
+ public String getBasexUrl() {
+ return basexUrl;
+ }
+}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/archives/FieldValue.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/archives/FieldValue.java
new file mode 100644
index 00000000000..1d644b9f65b
--- /dev/null
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/archives/FieldValue.java
@@ -0,0 +1,27 @@
+package io.goobi.viewer.model.archives;
+
+public class FieldValue {
+
+ private String value;
+ private ArchiveMetadataField field;
+
+ public FieldValue(ArchiveMetadataField field) {
+ this.field = field;
+ }
+
+ /**
+ * @return the value
+ */
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ if (field.getXpath().contains("unittitle")) {
+ field.getEadEntry().setLabel(value);
+ } else {
+ this.value = value;
+ }
+
+ }
+}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/bookmark/Bookmark.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/bookmark/Bookmark.java
index 08da9ffb2ab..9a56a4ab7a0 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/bookmark/Bookmark.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/bookmark/Bookmark.java
@@ -32,10 +32,9 @@
import javax.persistence.TemporalType;
import javax.persistence.Transient;
-import org.apache.commons.text.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.text.StringEscapeUtils;
import org.apache.solr.common.SolrDocument;
-import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -56,7 +55,6 @@
import io.goobi.viewer.model.metadata.MetadataElement;
import io.goobi.viewer.model.search.BrowseElement;
import io.goobi.viewer.model.search.SearchHit;
-import io.goobi.viewer.model.viewer.PageType;
import io.goobi.viewer.model.viewer.StructElement;
/**
@@ -74,7 +72,8 @@ public class Bookmark implements Serializable {
private static final Logger logger = LoggerFactory.getLogger(Bookmark.class);
private static final String[] FIELDS =
- { SolrConstants.THUMBNAIL, SolrConstants.DATAREPOSITORY, SolrConstants.MIMETYPE, SolrConstants.IDDOC, SolrConstants.PI, SolrConstants.ISWORK, SolrConstants.ISANCHOR };
+ { SolrConstants.THUMBNAIL, SolrConstants.DATAREPOSITORY, SolrConstants.MIMETYPE, SolrConstants.IDDOC, SolrConstants.PI,
+ SolrConstants.ISWORK, SolrConstants.ISANCHOR };
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -114,13 +113,13 @@ public class Bookmark implements Serializable {
@Transient
private String url;
-
- @Transient
+
+ @Transient
private BrowseElement browseElement = null;
- @Transient
- private Boolean hasImages = null;
-
+ @Transient
+ private Boolean hasImages = null;
+
/**
* Empty constructor.
*/
@@ -318,9 +317,9 @@ public String getRepresentativeImageUrl(int width, int height)
ThumbnailHandler thumbs = BeanUtils.getImageDeliveryBean().getThumbs();
if (order != null) {
return thumbs.getThumbnailUrl(order, pi, width, height);
- } else {
- return thumbs.getThumbnailUrl(pi, width, height);
}
+
+ return thumbs.getThumbnailUrl(pi, width, height);
}
/**
@@ -589,18 +588,18 @@ public String getMainTitle() {
public void setMainTitle(String mainTitle) {
this.mainTitle = mainTitle;
}
-
+
@JsonIgnore
public MetadataElement getMetadataElement() throws IndexUnreachableException {
SolrDocument doc = retrieveSolrDocument();
- Long iddoc = Long.parseLong((String)doc.getFirstValue(SolrConstants.IDDOC));
+ Long iddoc = Long.parseLong((String) doc.getFirstValue(SolrConstants.IDDOC));
StructElement se = new StructElement(iddoc, doc);
Locale sessionLocale = BeanUtils.getLocale();
String selectedRecordLanguage = sessionLocale.getLanguage();
try {
MetadataElement md = new MetadataElement(se, sessionLocale, selectedRecordLanguage);
return md;
- } catch(DAOException | PresentationException e) {
+ } catch (DAOException | PresentationException e) {
throw new IndexUnreachableException(e.getMessage());
}
}
@@ -611,11 +610,11 @@ public MetadataElement getMetadataElement() throws IndexUnreachableException {
* @throws IndexUnreachableException
*/
private SolrDocument retrieveSolrDocument() throws IndexUnreachableException {
- try {
+ try {
String query = getSolrQueryForDocument();
SolrDocument doc = DataManager.getInstance().getSearchIndex().getFirstDoc(query, null);
return doc;
- } catch(PresentationException e) {
+ } catch (PresentationException e) {
throw new IndexUnreachableException(e.toString());
}
}
@@ -626,7 +625,7 @@ private SolrDocument retrieveSolrDocument() throws IndexUnreachableException {
@JsonIgnore
public String getSolrQueryForDocument() {
String query = "+PI_TOPSTRUCT:%s";
- if(StringUtils.isNotBlank(logId)) {
+ if (StringUtils.isNotBlank(logId)) {
query += " +LOGID:%s";
query = String.format(query, this.pi, this.logId);
} else {
@@ -635,45 +634,47 @@ public String getSolrQueryForDocument() {
}
return query;
}
-
+
@JsonIgnore
public BrowseElement getBrowseElement() throws IndexUnreachableException {
- if(this.browseElement == null) {
- try {
+ if (this.browseElement == null) {
+ try {
SolrDocument doc = retrieveSolrDocument();
- if(doc != null) {
+ if (doc != null) {
Locale locale = BeanUtils.getLocale();
- SearchHit sh = SearchHit.createSearchHit(doc, null, null, locale, "", null, null, null, false, null, null, SearchHit.HitType.DOCSTRCT);
+ SearchHit sh = SearchHit.createSearchHit(doc, null, null, locale, "", null, null, null, null, null,
+ SearchHit.HitType.DOCSTRCT, BeanUtils.getImageDeliveryBean().getThumbs());
this.browseElement = sh.getBrowseElement();
}
- } catch(PresentationException | DAOException | ViewerConfigurationException e) {
+ } catch (PresentationException | DAOException | ViewerConfigurationException e) {
throw new IndexUnreachableException(e.toString());
}
}
-
+
return this.browseElement;
}
-
+
@JsonIgnore
public boolean isHasImages() {
try {
- if(this.hasImages != null) {
+ if (this.hasImages != null) {
//no action required
- } else if(this.browseElement != null) {
+ } else if (this.browseElement != null) {
this.hasImages = this.browseElement.isHasImages();
} else {
this.hasImages = isHasImagesFromSolr();
}
- } catch(IndexUnreachableException | PresentationException e) {
+ } catch (IndexUnreachableException | PresentationException e) {
logger.error("Unable to get browse element for bookmark", e);
return false;
}
return this.hasImages;
}
-
private boolean isHasImagesFromSolr() throws IndexUnreachableException, PresentationException {
- SolrDocument doc = DataManager.getInstance().getSearchIndex().getFirstDoc(getSolrQueryForDocument(), Arrays.asList(SolrConstants.THUMBNAIL, SolrConstants.FILENAME));
+ SolrDocument doc = DataManager.getInstance()
+ .getSearchIndex()
+ .getFirstDoc(getSolrQueryForDocument(), Arrays.asList(SolrConstants.THUMBNAIL, SolrConstants.FILENAME));
return SolrSearchIndex.isHasImages(doc);
}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/cms/CMSContentItem.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/cms/CMSContentItem.java
index 80f1724cab8..64981017654 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/cms/CMSContentItem.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/cms/CMSContentItem.java
@@ -300,6 +300,9 @@ public Functionality createFunctionality(CMSContentItem item) {
@Transient
private int nestedPagesCount = 0;
+
+ @Transient
+ private Map dcStrings = null;
/**
* Noop constructor for javax.persistence
@@ -936,6 +939,7 @@ public String getCollectionField() {
public void setCollectionField(String collectionField) {
this.collectionField = collectionField;
this.collection = null;
+ this.dcStrings = null;
}
/**
@@ -1036,16 +1040,27 @@ public void setBaseCollection(String baseCollection) {
* @throws io.goobi.viewer.exceptions.IndexUnreachableException if any.
*/
public List getPossibleBaseCollectionList() throws IndexUnreachableException {
- if (StringUtils.isBlank(collectionField)) {
- return Collections.singletonList("");
+ if (StringUtils.isBlank(collectionField)) {
+ return Collections.singletonList("");
+ }
+ Map dcStrings = getColletionMap();
+ List list = new ArrayList<>(dcStrings.keySet());
+ list.add(0, "");
+ Collections.sort(list);
+ return list;
+ }
+
+ /**
+ * @return
+ * @throws IndexUnreachableException
+ */
+ public Map getColletionMap() throws IndexUnreachableException {
+ if(dcStrings == null) {
+ dcStrings =
+ SearchHelper.findAllCollectionsFromField(collectionField, collectionField, getSearchPrefix(), true, true,
+ DataManager.getInstance().getConfiguration().getCollectionSplittingChar(collectionField));
}
- Map dcStrings =
- SearchHelper.findAllCollectionsFromField(collectionField, collectionField, getSearchPrefix(), true, true,
- DataManager.getInstance().getConfiguration().getCollectionSplittingChar(collectionField));
- List list = new ArrayList<>(dcStrings.keySet());
- list.add(0, "");
- Collections.sort(list);
- return list;
+ return dcStrings;
}
/**
@@ -1058,9 +1073,7 @@ public List getPossibleIgnoreCollectionList() throws IndexUnreachableExc
if (StringUtils.isBlank(collectionField)) {
return Collections.singletonList("");
}
- Map dcStrings =
- SearchHelper.findAllCollectionsFromField(collectionField, collectionField, getSearchPrefix(), true, true,
- DataManager.getInstance().getConfiguration().getCollectionSplittingChar(collectionField));
+ Map dcStrings = getColletionMap();
List list = new ArrayList<>(dcStrings.keySet());
list = list.stream()
.filter(c -> StringUtils.isBlank(getBaseCollection()) || c.startsWith(getBaseCollection() + "."))
@@ -1738,4 +1751,5 @@ public boolean isPaginated() {
return ContentItemMode.paginated.equals(getMode());
}
+
}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/cms/CMSNavigationManager.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/cms/CMSNavigationManager.java
index 962bf0b0e7a..b56049af339 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/cms/CMSNavigationManager.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/cms/CMSNavigationManager.java
@@ -87,7 +87,7 @@ public final void loadItems() throws DAOException {
addAvailableItem(searchcalendar);
SelectableNavigationItem browse = new SelectableNavigationItem("browse", "browse");
addAvailableItem(browse);
- SelectableNavigationItem timematrix = new SelectableNavigationItem("timematrix", "timematrix__title");
+ SelectableNavigationItem timematrix = new SelectableNavigationItem("timematrix", "timematrix");
addAvailableItem(timematrix);
SelectableNavigationItem statistics = new SelectableNavigationItem("statistics", "statistics");
addAvailableItem(statistics);
@@ -100,6 +100,10 @@ public final void loadItems() throws DAOException {
addAvailableItem(user);
SelectableNavigationItem campaigns = new SelectableNavigationItem("campaigns", "admin__crowdsourcing_campaigns");
addAvailableItem(campaigns);
+ SelectableNavigationItem tectonics = new SelectableNavigationItem("archivetree", "archive");
+ addAvailableItem(tectonics);
+ SelectableNavigationItem archives = new SelectableNavigationItem("archives", "archives");
+ addAvailableItem(archives);
addModuleItems();
addCMSPageItems();
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/cms/CMSPage.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/cms/CMSPage.java
index e334c9241c0..1222e92b748 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/cms/CMSPage.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/cms/CMSPage.java
@@ -109,10 +109,10 @@ public class CMSPage implements Comparable, Harvestable {
@Column(name = "template_id", nullable = false)
private String templateId;
- @Column(name = "date_created", nullable = false, columnDefinition = "TIMESTAMP(9)")
+ @Column(name = "date_created", nullable = false, columnDefinition = "TIMESTAMP")
private LocalDateTime dateCreated;
- @Column(name = "date_updated", columnDefinition = "TIMESTAMP(9)")
+ @Column(name = "date_updated", columnDefinition = "TIMESTAMP")
private LocalDateTime dateUpdated;
@Column(name = "published", nullable = false)
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/cms/itemfunctionality/SearchFunctionality.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/cms/itemfunctionality/SearchFunctionality.java
index a1eabd02bd9..ccbcf94da04 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/cms/itemfunctionality/SearchFunctionality.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/cms/itemfunctionality/SearchFunctionality.java
@@ -31,12 +31,14 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import io.goobi.viewer.controller.DataManager;
import io.goobi.viewer.exceptions.DAOException;
import io.goobi.viewer.exceptions.IndexUnreachableException;
import io.goobi.viewer.exceptions.PresentationException;
import io.goobi.viewer.exceptions.ViewerConfigurationException;
import io.goobi.viewer.managedbeans.SearchBean;
import io.goobi.viewer.managedbeans.utils.BeanUtils;
+import io.goobi.viewer.model.cms.CMSPage;
import io.goobi.viewer.model.search.SearchFacets;
import io.goobi.viewer.model.search.SearchFilter;
import io.goobi.viewer.model.search.SearchInterface;
@@ -167,7 +169,7 @@ public void searchFacetted() {
* @throws io.goobi.viewer.exceptions.DAOException if any.
* @throws io.goobi.viewer.exceptions.ViewerConfigurationException if any.
*/
- public void search() throws PresentationException, IndexUnreachableException, DAOException, ViewerConfigurationException {
+ public void search(String subtheme) throws PresentationException, IndexUnreachableException, DAOException, ViewerConfigurationException {
logger.trace("searchAction");
SearchBean searchBean = getSearchBean();
if (searchBean == null) {
@@ -175,7 +177,7 @@ public void search() throws PresentationException, IndexUnreachableException, DA
return;
}
String facetString = getSearchBean().getFacets().getCurrentFacetString();
- searchBean.getFacets().setCurrentFacetString(getCompleteFacetString(facetString));
+ searchBean.getFacets().setCurrentFacetString(getCompleteFacetString(facetString, subtheme));
searchBean.search();
searchBean.getFacets().setCurrentFacetString(facetString);
}
@@ -183,20 +185,29 @@ public void search() throws PresentationException, IndexUnreachableException, DA
/**
* @return
*/
- private String getCompleteFacetString(String baseFacetString) {
+ private String getCompleteFacetString(String baseFacetString, String subtheme) {
StringBuilder sb = new StringBuilder();
if (StringUtils.isNotBlank(getPageFacetString())) {
String pageFacetString = getPageFacetString().replaceAll("(?i)^(AND|OR)\\s", "");
sb.append(pageFacetString);
- if (StringUtils.isNotBlank(baseFacetString) && !"-".equals(baseFacetString) && !sb.toString().equals(baseFacetString)) {
- sb.append(";;").append(baseFacetString);
+ }
+ if (StringUtils.isNotBlank(subtheme)) {
+ if(sb.length() > 0) {
+ sb.append(";;");
+ }
+ String subthemeDiscriminatorField = DataManager.getInstance().getConfiguration().getSubthemeDiscriminatorField();
+ sb.append(subthemeDiscriminatorField).append(":").append(subtheme);
+ }
+ if (StringUtils.isNotBlank(baseFacetString) && !"-".equals(baseFacetString)) {
+ if(sb.length() > 0) {
+ sb.append(";;");
}
- } else if (StringUtils.isNotBlank(baseFacetString) && !"-".equals(baseFacetString)) {
sb.append(baseFacetString);
}
return sb.toString();
}
+
/**
* The part of the search url before the page number
*
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/crowdsourcing/campaigns/Campaign.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/crowdsourcing/campaigns/Campaign.java
index bac19132718..3946462567a 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/crowdsourcing/campaigns/Campaign.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/crowdsourcing/campaigns/Campaign.java
@@ -25,6 +25,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
+import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -137,6 +138,20 @@ public static CampaignVisibility getByName(String name) {
return null;
}
}
+
+ public enum ReviewMode {
+ REQUIRE_REVIEW("label__require_review"),
+ NO_REVIEW("label__no_review"),
+ LIMIT_REVIEW_TO_USERGROUP("label__limit_review_to_usergroup");
+
+ private final String label;
+ private ReviewMode(String label) {
+ this.label = label;
+ }
+ public String getLabel() {
+ return this.label;
+ }
+ }
private static final Logger logger = LoggerFactory.getLogger(Campaign.class);
@@ -149,11 +164,11 @@ public static CampaignVisibility getByName(String name) {
@Column(name = "campaign_id")
private Long id;
- @Column(name = "date_created", nullable = false)
+ @Column(name = "date_created", nullable = false, columnDefinition = "TIMESTAMP")
@JsonIgnore
private LocalDateTime dateCreated;
- @Column(name = "date_updated")
+ @Column(name = "date_updated", columnDefinition = "TIMESTAMP")
@JsonIgnore
private LocalDateTime dateUpdated;
@@ -162,11 +177,11 @@ public static CampaignVisibility getByName(String name) {
@JsonIgnore
private CampaignVisibility visibility = CampaignVisibility.PRIVATE;
- @Column(name = "date_start")
+ @Column(name = "date_start", columnDefinition = "TIMESTAMP")
@JsonIgnore
private LocalDateTime dateStart;
- @Column(name = "date_end")
+ @Column(name = "date_end", columnDefinition = "TIMESTAMP")
@JsonIgnore
private LocalDateTime dateEnd;
@@ -193,6 +208,14 @@ public static CampaignVisibility getByName(String name) {
@JoinColumn(name = "user_group_id")
@JsonIgnore
private UserGroup userGroup;
+
+ @Column(name = "review_mode")
+ private ReviewMode reviewMode = ReviewMode.REQUIRE_REVIEW;
+
+ @ManyToOne
+ @JoinColumn(name = "revewier_user_group_id")
+ @JsonIgnore
+ private UserGroup reviewerUserGroup;
@Column(name = "time_period_enabled")
@JsonIgnore
@@ -284,6 +307,8 @@ public Campaign(Campaign orig) {
this.timePeriodEnabled = orig.timePeriodEnabled;
this.userGroup = orig.userGroup;
this.limitToGroup = orig.limitToGroup;
+ this.reviewMode = orig.reviewMode;
+ this.reviewerUserGroup = orig.reviewerUserGroup;
}
/**
@@ -569,26 +594,36 @@ public boolean isHasEnded() {
*/
public boolean isUserAllowedAction(User user, CampaignRecordStatus status) throws PresentationException, IndexUnreachableException, DAOException {
// logger.trace("isUserAllowedAction: {}", status);
- if (CampaignVisibility.PUBLIC.equals(visibility)) {
- return true;
- }
- if (user == null || status == null) {
+ if (status == null) {
return false;
}
if (!isHasStarted() || isHasEnded()) {
return false;
}
- if (user.isSuperuser()) {
+ if (user != null && user.isSuperuser()) {
return true;
}
- if (CampaignVisibility.PRIVATE.equals(visibility) && isGroupLimitActive()) {
- return userGroup.getMembersAndOwner().contains(user);
- }
switch (status) {
case ANNOTATE:
- return user.isHasCrowdsourcingPrivilege(IPrivilegeHolder.PRIV_CROWDSOURCING_ANNOTATE_CAMPAIGN);
+ if (CampaignVisibility.PUBLIC.equals(visibility)) {
+ return true;
+ } else if(user == null) {
+ return false;
+ } else if (CampaignVisibility.PRIVATE.equals(visibility) && isGroupLimitActive()) {
+ return userGroup.getMembersAndOwner().contains(user);
+ } else {
+ return user.isHasCrowdsourcingPrivilege(IPrivilegeHolder.PRIV_CROWDSOURCING_ANNOTATE_CAMPAIGN);
+ }
case REVIEW:
- return user.isHasCrowdsourcingPrivilege(IPrivilegeHolder.PRIV_CROWDSOURCING_REVIEW_CAMPAIGN);
+ if (isReviewGroupLimitActive()) {
+ return user != null && reviewerUserGroup.getMembersAndOwner().contains(user);
+ } else if (CampaignVisibility.PUBLIC.equals(visibility)) {
+ return true;
+ } else if(user == null) {
+ return false;
+ } else {
+ return user.isHasCrowdsourcingPrivilege(IPrivilegeHolder.PRIV_CROWDSOURCING_REVIEW_CAMPAIGN);
+ }
default:
return false;
}
@@ -1459,6 +1494,14 @@ public CampaignRecordStatus getRecordStatus(String pi) {
public boolean isGroupLimitActive() {
return limitToGroup && userGroup != null;
}
+
+ public boolean isReviewGroupLimitActive() {
+ return ReviewMode.LIMIT_REVIEW_TO_USERGROUP.equals(this.reviewMode) && reviewerUserGroup != null;
+ }
+
+ public boolean isReviewModeActive() {
+ return !ReviewMode.NO_REVIEW.equals(this.reviewMode);
+ }
/**
* Updates record status in the campaign statistics.
@@ -1550,7 +1593,21 @@ public boolean isLimitToGroup() {
public void setLimitToGroup(boolean limitToGroup) {
this.limitToGroup = limitToGroup;
}
-
+
+ /**
+ * @return the reviewMode
+ */
+ public ReviewMode getReviewMode() {
+ return reviewMode;
+ }
+
+ /**
+ * @param reviewMode the reviewMode to set
+ */
+ public void setReviewMode(ReviewMode reviewMode) {
+ this.reviewMode = reviewMode;
+ }
+
/**
* @return the userGroup
*/
@@ -1564,6 +1621,20 @@ public UserGroup getUserGroup() {
public void setUserGroup(UserGroup userGroup) {
this.userGroup = userGroup;
}
+
+ /**
+ * @return the reviewerUserGroup
+ */
+ public UserGroup getReviewerUserGroup() {
+ return reviewerUserGroup;
+ }
+
+ /**
+ * @param reviewerUserGroup the reviewerUserGroup to set
+ */
+ public void setReviewerUserGroup(UserGroup reviewerUserGroup) {
+ this.reviewerUserGroup = reviewerUserGroup;
+ }
/**
* @return the timePeriodEnabled
@@ -1636,6 +1707,13 @@ public CategorizableTranslatedSelectable getMediaItemWrapper() {
return null;
}
-
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return getTitle();
+ }
}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/crowdsourcing/campaigns/CampaignItem.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/crowdsourcing/campaigns/CampaignItem.java
index ea306b0be93..806a4fda211 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/crowdsourcing/campaigns/CampaignItem.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/crowdsourcing/campaigns/CampaignItem.java
@@ -17,7 +17,10 @@
import java.net.URI;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
@@ -42,6 +45,7 @@ public class CampaignItem {
private Campaign campaign;
private CampaignRecordStatus recordStatus = null;
private List log = null;
+ private Map> metadata = new LinkedHashMap<>();
@JsonProperty("creator")
private URI creatorURI = null;
@@ -175,5 +179,19 @@ public void setLog(List log) {
public List getLog() {
return log;
}
+
+ /**
+ * @return the metadata
+ */
+ public Map> getMetadata() {
+ return Collections.unmodifiableMap(this.metadata);
+ }
+
+ /**
+ * @param metadata the metadata to set
+ */
+ public void setMetadata(Map> metadata) {
+ this.metadata = metadata;
+ }
}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/crowdsourcing/questions/Question.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/crowdsourcing/questions/Question.java
index 050a95fbccf..ae5b85c8b7a 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/crowdsourcing/questions/Question.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/crowdsourcing/questions/Question.java
@@ -15,15 +15,20 @@
*/
package io.goobi.viewer.model.crowdsourcing.questions;
+import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import javax.faces.event.ValueChangeEvent;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Convert;
@@ -38,8 +43,13 @@
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
+import javax.persistence.Transient;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.solr.client.solrj.SolrServerException;
import org.eclipse.persistence.annotations.PrivateOwned;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
@@ -47,7 +57,11 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import io.goobi.viewer.controller.DataManager;
+import io.goobi.viewer.dao.converter.StringListConverter;
import io.goobi.viewer.dao.converter.TranslatedTextConverter;
+import io.goobi.viewer.exceptions.DAOException;
+import io.goobi.viewer.managedbeans.utils.BeanUtils;
+import io.goobi.viewer.messages.Messages;
import io.goobi.viewer.model.crowdsourcing.campaigns.Campaign;
import io.goobi.viewer.model.misc.IPolyglott;
import io.goobi.viewer.model.misc.TranslatedText;
@@ -64,6 +78,8 @@
@JsonInclude(Include.NON_EMPTY)
public class Question {
+ private static final Logger logger = LoggerFactory.getLogger(Question.class);
+
private static final String URI_ID_TEMPLATE =
DataManager.getInstance().getConfiguration().getRestApiUrl() + "crowdsourcing/campaigns/{campaignId}/questions/{questionId}";
private static final String URI_ID_REGEX = ".*/crowdsourcing/campaigns/(\\d+)/questions/(\\d+)/?$";
@@ -103,6 +119,13 @@ public class Question {
@Column(name = "target_frequency", nullable = false)
private int targetFrequency;
+ @Column(name = "metadata_fields", nullable = true, columnDefinition = "LONGTEXT")
+ @Convert(converter = StringListConverter.class)
+ private List metadataFields = new ArrayList<>();
+
+ @Transient
+ private Map metadataFieldSelection = null;
+
/**
* Empty constructor.
*/
@@ -143,6 +166,7 @@ public Question(Question orig) {
this.targetFrequency = orig.targetFrequency;
this.targetSelector = orig.targetSelector;
this.text = new TranslatedText(orig.text, IPolyglott.getLocalesStatic(), IPolyglott.getCurrentLocale());
+ this.metadataFields = new ArrayList<>(orig.metadataFields);
}
/**
@@ -181,17 +205,15 @@ private void serializeTranslations() {
this.translationsLegacy = Collections.emptyList();
- // Map locationsMap = this.text.map();
- // for (Entry entry : locationsMap.entrySet()) {
- // Locale locale = entry.getKey();
- // String value = entry.getValue();
- // QuestionTranslation translation = translations.stream().filter(t -> t.getLanguage().equals(locale.getLanguage())).findAny()
- // .orElseGet(() -> this.addTranslation(locale));
- // translation.setValue(value);
- // }
- //
- //
- // this.translations = this.text.stream().map(t -> new QuestionTranslation(t, this)).collect(Collectors.toList());
+ }
+
+ /**
+ * Call when metadata list changes
+ */
+ public void serializeMetadataFields() {
+ if(QuestionType.METADATA.equals(getQuestionType())) {
+ this.metadataFields = getMetadataFieldSelection().entrySet().stream().filter(e -> e.getValue()).map(e -> e.getKey()).collect(Collectors.toList());
+ }
}
private void deserializeTranslations() {
@@ -336,7 +358,63 @@ public void setTargetSelector(TargetSelector targetSelector) {
public int getTargetFrequency() {
return targetFrequency;
}
+
+ /**
+ * @return the metadataFields
+ */
+ public List getMetadataFields() {
+ return metadataFields;
+ }
+
+ /**
+ * @param metadataFields the metadataFields to set
+ */
+ public void setMetadataFields(List metadataFields) {
+ this.metadataFields = new ArrayList<>(metadataFields);
+ }
+
+ public void addMetadataField(String field) {
+ this.metadataFields.add(field);
+ }
+
+ public void removeMetadataField(String field) {
+ this.metadataFields.remove(field);
+ }
+
+ /**
+ * @param metadataToAdd the metadataToAdd to set
+ */
+ public void setMetadataToAdd(String metadataToAdd) {
+ if(StringUtils.isNotBlank(metadataToAdd)) {
+ addMetadataField(metadataToAdd);
+ }
+ }
+
+ /**
+ * @return the metadataToAdd
+ */
+ public String getMetadataToAdd() {
+ return "";
+ }
+ /**
+ *
+ * @return a list of all "MD_" fields from solr
+ * @throws IOException
+ * @throws SolrServerException
+ */
+ @JsonIgnore
+ public List getAvailableMetadataFields() throws DAOException {
+ Locale locale = BeanUtils.getLocale();
+ return DataManager.getInstance().getSearchIndex().getAllFieldNames().stream()
+ .filter(field -> field.startsWith("MD_"))
+ .filter(field -> !field.endsWith("_UNTOKENIZED"))
+ .map(field -> field.replaceAll("_LANG_.*", ""))
+// .filter(field -> !this.metadataFields.contains(field))
+ .distinct()
+ .sorted((f1,f2) -> Messages.translate(f1, locale).compareToIgnoreCase(Messages.translate(f2, locale)))
+ .collect(Collectors.toList());
+ }
/**
*
* Setter for the field targetFrequency .
@@ -406,5 +484,27 @@ public NormdataAuthority getAuthorityData() {
return null;
}
+
+ /**
+ * @return the metadataFieldSelection
+ * @throws IOException
+ * @throws SolrServerException
+ */
+ public Map getMetadataFieldSelection() {
+ if(this.metadataFieldSelection == null) {
+ try {
+ this.metadataFieldSelection = getAvailableMetadataFields().stream().collect(Collectors.toMap(field -> field, field -> this.metadataFields.contains(field)));
+ } catch(DAOException e) {
+ //If the possible fields cannot be retrieved from solr, just show the already selected ones
+ logger.error("Failed to load all possible metadata fields " + e.toString());
+ this.metadataFieldSelection = this.metadataFields.stream().collect(Collectors.toMap(field -> field, field -> Boolean.TRUE));
+ }
+ }
+ return metadataFieldSelection;
+ }
+
+ public List getSelectedMetadataFields() throws SolrServerException, IOException {
+ return this.getMetadataFieldSelection().entrySet().stream().filter(Entry::getValue).map(Entry::getKey).collect(Collectors.toList());
+ }
}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/crowdsourcing/questions/QuestionType.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/crowdsourcing/questions/QuestionType.java
index 99a4650dcb3..29ad6fe264d 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/crowdsourcing/questions/QuestionType.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/crowdsourcing/questions/QuestionType.java
@@ -26,10 +26,7 @@ public enum QuestionType {
PLAINTEXT,
RICHTEXT,
GEOLOCATION_POINT,
- NORMDATA
- /**
- * Not implemented yet DATE_PICKER, GEOLOCATION_AREA, TRANSCRIPTION, KEY_VALUE_LIST
- **/
- ;
+ NORMDATA,
+ METADATA;
}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/iiif/presentation/builder/CollectionBuilder.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/iiif/presentation/builder/CollectionBuilder.java
index ac9eed50db5..e6076b46ac6 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/iiif/presentation/builder/CollectionBuilder.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/iiif/presentation/builder/CollectionBuilder.java
@@ -46,6 +46,7 @@
import io.goobi.viewer.api.rest.v1.ApiUrls;
import io.goobi.viewer.controller.DataManager;
import io.goobi.viewer.controller.SolrConstants;
+import io.goobi.viewer.exceptions.DAOException;
import io.goobi.viewer.exceptions.IndexUnreachableException;
import io.goobi.viewer.exceptions.PresentationException;
import io.goobi.viewer.exceptions.ViewerConfigurationException;
@@ -416,7 +417,7 @@ public String getFacetField(String collectionField) {
if (!DataManager.getInstance().getSearchIndex().getAllFieldNames().contains(facetField)) {
facetField = collectionField;
}
- } catch (SolrServerException | IOException e) {
+ } catch (DAOException e) {
logger.warn("Unable to query for facet field", e);
facetField = collectionField;
}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/iiif/presentation/builder/SequenceBuilder.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/iiif/presentation/builder/SequenceBuilder.java
index 8e272b7df46..041e39ca2e9 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/iiif/presentation/builder/SequenceBuilder.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/iiif/presentation/builder/SequenceBuilder.java
@@ -95,7 +95,7 @@ public class SequenceBuilder extends AbstractBuilder {
protected ImageDeliveryBean imageDelivery = BeanUtils.getImageDeliveryBean();
private BuildMode buildMode = BuildMode.IIIF;
- private PageType preferredView = PageType.viewObject;
+ private PageType preferedView = PageType.viewObject;
/**
*
@@ -325,7 +325,7 @@ public Canvas generateCanvas(StructElement doc, PhysicalElement page)
Sequence parent = new Sequence(getSequenceURI(doc.getPi(), null));
canvas.addWithin(parent);
- LinkingContent viewerPage = new LinkingContent(new URI(getViewUrl(page, getPreferredView())));
+ LinkingContent viewerPage = new LinkingContent(new URI(getViewUrl(page, getPreferedView())));
viewerPage.setLabel(new SimpleMetadataValue("goobi viewer"));
canvas.addRendering(viewerPage);
@@ -564,8 +564,8 @@ public SequenceBuilder setBuildMode(BuildMode buildMode) {
*
* @return the preferredView
*/
- public PageType getPreferredView() {
- return preferredView;
+ public PageType getPreferedView() {
+ return preferedView;
}
/**
@@ -576,9 +576,10 @@ public PageType getPreferredView() {
* @param preferredView the preferredView to set
* @return a {@link io.goobi.viewer.model.iiif.presentation.builder.SequenceBuilder} object.
*/
- public SequenceBuilder setPreferredView(PageType preferredView) {
- this.preferredView = preferredView;
+ public SequenceBuilder setPreferedView(PageType preferredView) {
+ this.preferedView = preferredView;
return this;
}
+
}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/misc/SelectionManager.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/misc/SelectionManager.java
new file mode 100644
index 00000000000..127a1eabae3
--- /dev/null
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/misc/SelectionManager.java
@@ -0,0 +1,195 @@
+/**
+ * This file is part of the Goobi viewer - a content presentation and management application for digitized objects.
+ *
+ * Visit these websites for more information.
+ * - http://www.intranda.com
+ * - http://digiverso.com
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If not, see .
+ */
+package io.goobi.viewer.model.misc;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author florian
+ *
+ */
+public class SelectionManager implements Map {
+
+ private final Map selectionMap;
+
+ private boolean selectAll = false;
+
+ /**
+ * @param collect
+ */
+ public SelectionManager(List allEntries) {
+ this.selectionMap = allEntries.stream().collect(Collectors.toMap(t -> t, t -> false));
+ }
+
+ public SelectionManager() {
+ this.selectionMap = new HashMap<>();
+ }
+
+ /**
+ * @return the selectAll
+ */
+ public boolean isSelectAll() {
+ return selectAll;
+ }
+
+ /**
+ * @param selectAll the selectAll to set
+ */
+ public void setSelectAll(boolean selectAll) {
+ this.selectAll = selectAll;
+ }
+
+ public Boolean select(T item) {
+ return setSelected(item, Boolean.TRUE);
+ }
+
+ public Boolean deselect(T item) {
+ return setSelected(item, Boolean.FALSE);
+ }
+
+ public Boolean get(Object item) {
+ if(isSelectAll()) {
+ return Boolean.TRUE;
+ } else {
+ return Optional.ofNullable(selectionMap.get(item)).orElse(Boolean.FALSE);
+ }
+ }
+
+ public Boolean put(Object item, Boolean selected) {
+ try {
+ return setSelected((T) item, selected);
+ } catch (ClassCastException e) {
+ throw new IllegalArgumentException("Key is wong type");
+ }
+ }
+
+ public Boolean isSelected(T item) {
+ return get(item);
+ }
+
+ public Boolean setSelected(T item, Boolean selected) {
+ if(!selected) {
+ setSelectAll(false);
+ }
+ return selectionMap.put(item, selected);
+ }
+
+ /**
+ *
+ */
+ public List getAllSelected() {
+ if(isSelectAll()) {
+ return new ArrayList<>(selectionMap.keySet());
+ } else {
+ return selectionMap.entrySet().stream().filter(e -> Boolean.TRUE.equals(e.getValue())).map(e -> e.getKey()).collect(Collectors.toList());
+ }
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.util.Map#clear()
+ */
+ @Override
+ public void clear() {
+ this.setSelectAll(false);
+ selectionMap.clear();
+ }
+
+ /* (non-Javadoc)
+ * @see java.util.Map#containsKey(java.lang.Object)
+ */
+ @Override
+ public boolean containsKey(Object key) {
+ return selectionMap.containsKey(key);
+ }
+
+ /* (non-Javadoc)
+ * @see java.util.Map#containsValue(java.lang.Object)
+ */
+ @Override
+ public boolean containsValue(Object value) {
+ return selectionMap.containsValue(value);
+ }
+
+ /* (non-Javadoc)
+ * @see java.util.Map#entrySet()
+ */
+ @Override
+ public Set> entrySet() {
+ return selectionMap.entrySet();
+ }
+
+ /* (non-Javadoc)
+ * @see java.util.Map#isEmpty()
+ */
+ @Override
+ public boolean isEmpty() {
+ return selectionMap.isEmpty();
+ }
+
+ /* (non-Javadoc)
+ * @see java.util.Map#keySet()
+ */
+ @Override
+ public Set keySet() {
+ return selectionMap.keySet();
+ }
+
+ /* (non-Javadoc)
+ * @see java.util.Map#putAll(java.util.Map)
+ */
+ @Override
+ public void putAll(Map extends T, ? extends Boolean> m) {
+ if(m.containsValue(Boolean.FALSE)) {
+ setSelectAll(false);
+ }
+ selectionMap.putAll(m);
+ }
+
+ /* (non-Javadoc)
+ * @see java.util.Map#remove(java.lang.Object)
+ */
+ @Override
+ public Boolean remove(Object key) {
+ return selectionMap.remove(key);
+ }
+
+ /* (non-Javadoc)
+ * @see java.util.Map#size()
+ */
+ @Override
+ public int size() {
+ return selectionMap.size();
+ }
+
+ /* (non-Javadoc)
+ * @see java.util.Map#values()
+ */
+ @Override
+ public Collection values() {
+ return selectionMap.values();
+ }
+
+
+
+}
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/search/BrowseElement.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/search/BrowseElement.java
index 61b1f3218c0..4a009d6e5ec 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/search/BrowseElement.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/search/BrowseElement.java
@@ -170,14 +170,13 @@ public class BrowseElement implements Serializable {
* @param metadataList
* @param locale
* @param fulltext
- * @param useThumbnail
* @param
* @throws PresentationException
* @throws IndexUnreachableException
* @throws DAOException
* @throws ViewerConfigurationException
*/
- BrowseElement(StructElement structElement, List metadataList, Locale locale, String fulltext, boolean useThumbnail,
+ BrowseElement(StructElement structElement, List metadataList, Locale locale, String fulltext,
Map> searchTerms, ThumbnailHandler thumbs)
throws PresentationException, IndexUnreachableException, DAOException, ViewerConfigurationException {
this.metadataList = metadataList;
diff --git a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/search/SearchHelper.java b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/search/SearchHelper.java
index 306fff22568..a5d5b606556 100644
--- a/goobi-viewer-core/src/main/java/io/goobi/viewer/model/search/SearchHelper.java
+++ b/goobi-viewer-core/src/main/java/io/goobi/viewer/model/search/SearchHelper.java
@@ -67,6 +67,7 @@
import io.goobi.viewer.controller.SolrConstants.DocType;
import io.goobi.viewer.controller.SolrSearchIndex;
import io.goobi.viewer.controller.StringTools;
+import io.goobi.viewer.controller.imaging.ThumbnailHandler;
import io.goobi.viewer.controller.language.LocaleComparator;
import io.goobi.viewer.exceptions.DAOException;
import io.goobi.viewer.exceptions.IndexUnreachableException;
@@ -198,6 +199,7 @@ public static List searchWithFulltext(String query, int first, int ro
logger.trace("hits found: {}; results returned: {}", resp.getResults().getNumFound(), resp.getResults().size());
List ret = new ArrayList<>(resp.getResults().size());
int count = 0;
+ ThumbnailHandler thumbs = BeanUtils.getImageDeliveryBean().getThumbs();
for (SolrDocument doc : resp.getResults()) {
logger.trace("result iddoc: {}", doc.getFieldValue(SolrConstants.IDDOC));
String fulltext = null;
@@ -244,8 +246,8 @@ public static List searchWithFulltext(String query, int first, int ro
}
SearchHit hit =
- SearchHit.createSearchHit(doc, ownerDoc, null, locale, fulltext, searchTerms, exportFields, sortFields, true, ignoreFields,
- translateFields, null);
+ SearchHit.createSearchHit(doc, ownerDoc, null, locale, fulltext, searchTerms, exportFields, sortFields,
+ ignoreFields, translateFields, null, thumbs);
if (keepSolrDoc) {
hit.setSolrDoc(doc);
}
@@ -277,8 +279,9 @@ public static List searchWithFulltext(String query, int first, int ro
* @throws io.goobi.viewer.exceptions.DAOException if any.
* @throws io.goobi.viewer.exceptions.ViewerConfigurationException if any.
*/
- public static List searchWithAggregation(String query, int first, int rows, List sortFields, List resultFields,
- List filterQueries, Map params, Map> searchTerms, List exportFields, Locale locale)
+ public static List searchWithAggregation(String query, int first, int rows, List sortFields,
+ List resultFields, List filterQueries, Map params, Map> searchTerms,
+ List exportFields, Locale locale)
throws PresentationException, IndexUnreachableException, DAOException, ViewerConfigurationException {
return searchWithAggregation(query, first, rows, sortFields, resultFields, filterQueries, params, searchTerms, exportFields, locale, false);
}
@@ -303,9 +306,9 @@ public static List searchWithAggregation(String query, int first, int
* @throws io.goobi.viewer.exceptions.DAOException if any.
* @throws io.goobi.viewer.exceptions.ViewerConfigurationException if any.
*/
- public static List searchWithAggregation(String query, int first, int rows, List sortFields, List resultFields,
- List filterQueries, Map params, Map> searchTerms, List exportFields, Locale locale,
- boolean keepSolrDoc)
+ public static List searchWithAggregation(String query, int first, int rows, List sortFields,
+ List |