diff --git a/processes/collections-backup/build.gradle b/processes/collections-backup/build.gradle
new file mode 100644
index 000000000..a7e3aa47e
--- /dev/null
+++ b/processes/collections-backup/build.gradle
@@ -0,0 +1,11 @@
+description "Collections backup/restore"
+
+
+dependencies {
+ implementation project(':shared:common')
+ implementation project(':processes:import')
+
+ implementation 'org.json:json:20140107'
+
+}
+
diff --git a/processes/collections-backup/src/main/java/cz/inovatika/collections/Backup.java b/processes/collections-backup/src/main/java/cz/inovatika/collections/Backup.java
new file mode 100644
index 000000000..702fb0c41
--- /dev/null
+++ b/processes/collections-backup/src/main/java/cz/inovatika/collections/Backup.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) Nov 29, 2023 Pavel Stastny
+ *
+ * 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 3 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 cz.inovatika.collections;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+
+//import static cz.incad.kramerius.utils.XMLUtils.LOGGER;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.security.NoSuchAlgorithmException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Stack;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerException;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+
+import cz.incad.kramerius.FedoraNamespaces;
+import cz.incad.kramerius.statistics.StatisticReport;
+import cz.incad.kramerius.utils.RESTHelper;
+import cz.incad.kramerius.utils.XMLUtils;
+import cz.incad.kramerius.utils.conf.KConfiguration;
+
+
+public class Backup {
+
+ public static final Logger LOGGER = Logger.getLogger(Backup.class.getName());
+
+ public static void main(String[] args) throws TransformerException, ParserConfigurationException, SAXException, IOException {
+ LOGGER.log(Level.INFO, "Process parameters: " + Arrays.asList(args).toString());
+ if (args.length > 2) {
+ Client client = Client.create();
+
+ String target = args[1];
+ String nameOfBackup = args[2];
+ String tmpDirPath = System.getProperty("java.io.tmpdir");
+
+ String subdirectoryPath = tmpDirPath + File.separator + nameOfBackup;
+ FileUtils.forceMkdir(new File(subdirectoryPath));
+
+
+ for (String pid : extractPids(target)) {
+ List collectionProcessed = new ArrayList<>();
+ Stack processingStack = new Stack<>();
+ processingStack.add(pid);
+ while(!processingStack.isEmpty()) {
+ String processingPid = processingStack.pop();
+ if (collectionProcessed.contains(processingPid)) {
+ LOGGER.warning(String.format("Found cycle on %s", processingPid));
+ continue;
+ }
+ collectionProcessed.add(processingPid);
+ if (head(client, processingPid) == 200) {
+ Document parsed = foxml(client, processingPid);
+ StringWriter writer = new StringWriter();
+ XMLUtils.print(parsed, writer);
+ LOGGER.info(String.format("Writing to %s", new File(new File(subdirectoryPath), processingPid.replace(":", "_")).getAbsolutePath()));
+ FileUtils.writeByteArrayToFile(new File(new File(subdirectoryPath), processingPid.replace(":", "_")+".xml"), writer.toString().getBytes("UTF-8"));
+ List recursiveElements = XMLUtils.getElementsRecursive(parsed.getDocumentElement(), new XMLUtils.ElementsFilter() {
+
+ @Override
+ public boolean acceptElement(Element element) {
+ boolean equals = element.getLocalName().equals("contains");
+ return equals;
+ }
+ });
+
+
+ List pids = recursiveElements.stream().map(elm-> {
+ String attributeNS = elm.getAttributeNS(FedoraNamespaces.RDF_NAMESPACE_URI, "resource");
+ if (attributeNS.contains("info:fedora/")) {
+ String containsPid = attributeNS.substring("info:fedora/".length());
+ return containsPid;
+ } else return null;
+ }).filter(Objects::nonNull).collect(Collectors.toList());
+
+
+ if (pids.size() > 0) {
+
+ int batchSize = 40;
+ int numberOfIteration = pids.size() / batchSize;
+ if (pids.size() % batchSize != 0) {
+ numberOfIteration = numberOfIteration + 1;
+ }
+ for (int iteration = 0; iteration < numberOfIteration; iteration++) {
+ int start = iteration* batchSize;
+ int stop = Math.min((iteration+1)*batchSize, pids.size());
+ List subPids = pids.subList(start, stop);
+
+ String query = subPids.stream().map(it-> {return '"' + it +'"';}).collect(Collectors.joining(" OR "));
+ String encodedCondition = URLEncoder.encode(" AND pid:(" + query + ")", "UTF-8");
+
+
+ String solrSearchHost = KConfiguration.getInstance().getSolrSearchHost()+String.format("/select?fq=model:collection%s&q=*&fl=pid&wt=json", encodedCondition);
+
+ InputStream inputStream = RESTHelper.inputStream(solrSearchHost, "", "");
+ String string = IOUtils.toString(inputStream, "UTF-8");
+ JSONObject object = new JSONObject(string);
+ JSONObject response = object.getJSONObject("response");
+ JSONArray docs = response.getJSONArray("docs");
+ for (int i = 0; i < docs.length(); i++) {
+ JSONObject doc = docs.getJSONObject(i);
+ String collectionPid = doc.optString("pid");
+ processingStack.push(collectionPid);
+ }
+
+ }
+
+ }
+ } else {
+ LOGGER.warning(String.format("Pid %s doesnt exists",processingPid));
+ }
+ }
+ }
+
+ File tmpDir = new File(subdirectoryPath);
+ File[] listFiles = tmpDir.listFiles();
+ if (listFiles != null) {
+ String parentZipFolder = KConfiguration.getInstance().getConfiguration().getString("collections.backup.folder");
+ if (parentZipFolder == null) throw new IllegalStateException("configuration property 'collections.backup.folder' must be set ");
+ FileUtils.forceMkdir(new File(parentZipFolder));
+
+ String zipFile = parentZipFolder + File.separator + nameOfBackup+".zip";
+ try {
+ FileOutputStream fos = new FileOutputStream(zipFile);
+ ZipOutputStream zos = new ZipOutputStream(fos);
+
+ for (File lF : listFiles) {
+ addFileToZip("", lF, zos);
+ }
+
+ zos.close();
+ fos.close();
+ } catch (IOException e) {
+ LOGGER.log(Level.SEVERE,e.getMessage(),e);
+ }
+ }
+ }
+ }
+
+
+ private static List extractPids(String target) {
+ if (target.startsWith("pid:")) {
+ String pid = target.substring("pid:".length());
+ List result = new ArrayList<>();
+ result.add(pid);
+ return result;
+ } else if (target.startsWith("pidlist:")) {
+ List pids = Arrays.stream(target.substring("pidlist:".length()).split(";")).map(String::trim).filter(s -> !s.isEmpty()).collect(Collectors.toList());
+ return pids;
+ } else if (target.startsWith("pidlist_file:")) {
+ String filePath = target.substring("pidlist_file:".length());
+ File file = new File(filePath);
+ if (file.exists()) {
+ try {
+ return IOUtils.readLines(new FileInputStream(file), Charset.forName("UTF-8"));
+ } catch (IOException e) {
+ throw new RuntimeException("IOException " + e.getMessage());
+ }
+ } else {
+ throw new RuntimeException("file " + file.getAbsolutePath() + " doesnt exist ");
+ }
+ } else {
+ throw new RuntimeException("invalid target " + target);
+ }
+ }
+
+ private static int head(Client client, String pid) {
+ String url = KConfiguration.getInstance().getConfiguration().getString("api.client.point") + (KConfiguration.getInstance().getConfiguration().getString("api.point").endsWith("/") ? "" : "/") + String.format("items/%s/metadata/mods", pid);
+ LOGGER.info(String.format("Url %s", url));
+
+ WebResource r = client.resource(url);
+
+ WebResource.Builder builder = r.accept(MediaType.APPLICATION_XML);
+ ClientResponse head = builder.head();
+ int status = head.getStatus();
+ return status;
+ }
+
+
+ private static Document foxml(Client client, String processingPid)
+ throws ParserConfigurationException, SAXException, IOException {
+ String url = KConfiguration.getInstance().getConfiguration().getString("api.client.point") + (KConfiguration.getInstance().getConfiguration().getString("api.point").endsWith("/") ? "" : "/") + String.format("items/%s/foxml", processingPid);
+ LOGGER.info(String.format( "Requesting url is %s", url));
+ WebResource r = client.resource(url);
+
+ WebResource.Builder builder = r.accept(MediaType.APPLICATION_XML);
+ InputStream clientResponse = builder.get(InputStream.class);
+ Document parsed = XMLUtils.parseDocument(clientResponse, true);
+ return parsed;
+ }
+
+
+private static void addFileToZip(String path, File srcFile, ZipOutputStream zipOut) throws IOException {
+ FileInputStream fis = new FileInputStream(srcFile);
+ ZipEntry zipEntry = new ZipEntry(path + "/" + srcFile.getName());
+ zipOut.putNextEntry(zipEntry);
+
+ byte[] bytes = new byte[1024];
+ int length;
+ while ((length = fis.read(bytes)) >= 0) {
+ zipOut.write(bytes, 0, length);
+ }
+ fis.close();
+}
+
+}
diff --git a/processes/collections-backup/src/main/java/cz/inovatika/collections/Restore.java b/processes/collections-backup/src/main/java/cz/inovatika/collections/Restore.java
new file mode 100644
index 000000000..bdffadbf4
--- /dev/null
+++ b/processes/collections-backup/src/main/java/cz/inovatika/collections/Restore.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) Nov 29, 2023 Pavel Stastny
+ *
+ * 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 3 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 cz.inovatika.collections;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import javax.xml.bind.JAXBException;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerException;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.solr.client.solrj.SolrServerException;
+import org.xml.sax.SAXException;
+
+import com.sun.jersey.api.client.Client;
+
+import cz.incad.kramerius.processes.new_api.ProcessScheduler;
+import cz.incad.kramerius.utils.conf.KConfiguration;
+import cz.inovatika.collections.migrations.FromK5Instance;
+
+public class Restore {
+
+ public static final Logger LOGGER = Logger.getLogger(Restore.class.getName());
+
+ public static void main(String[] args) throws TransformerException, ParserConfigurationException, SAXException, IOException, JAXBException, InterruptedException, SolrServerException {
+ LOGGER.log(Level.INFO, "Process parameters: " + Arrays.asList(args).toString());
+ if (args.length > 1) {
+ String authToken = args[0];
+ String target = args[1];
+
+ String parentZipFolder = KConfiguration.getInstance().getConfiguration().getString("collections.backup.folder");
+ if (parentZipFolder == null) throw new IllegalStateException("configuration property 'collections.backup.folder' must be set ");
+ String zipFile = parentZipFolder + File.separator + target;
+
+
+
+ String tmpDirPath = System.getProperty("java.io.tmpdir");
+ String subdirectoryPath = tmpDirPath + File.separator + target;
+ FileUtils.forceMkdir(new File(subdirectoryPath));
+ unzip(zipFile, subdirectoryPath);
+
+
+ LOGGER.info("Scheduling import "+subdirectoryPath);
+ FromK5Instance.importTmpDir(subdirectoryPath, true, authToken);
+ } else {
+ throw new IllegalArgumentException("expecting 2 arguments (authtoken, zipfile)");
+ }
+ }
+
+
+ public static void unzip(String zipFile, String outputFolder) throws IOException {
+ LOGGER.info("Unzipping file to "+outputFolder);
+
+ byte[] buffer = new byte[1024];
+
+ try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile))) {
+ ZipEntry zipEntry = zis.getNextEntry();
+ while (zipEntry != null) {
+ String fileName = zipEntry.getName();
+ File newFile = new File(outputFolder + File.separator + fileName);
+
+ // Vytvoření nadřazeného adresáře pro soubor, pokud neexistuje
+ new File(newFile.getParent()).mkdirs();
+
+ try (FileOutputStream fos = new FileOutputStream(newFile)) {
+ int len;
+ while ((len = zis.read(buffer)) > 0) {
+ fos.write(buffer, 0, len);
+ }
+ }
+
+ zipEntry = zis.getNextEntry();
+ }
+
+ zis.closeEntry();
+ }
+ }
+}
diff --git a/processes/collections-backup/src/main/java/cz/inovatika/collections/migrations/FromK5Instance.java b/processes/collections-backup/src/main/java/cz/inovatika/collections/migrations/FromK5Instance.java
new file mode 100644
index 000000000..f30303235
--- /dev/null
+++ b/processes/collections-backup/src/main/java/cz/inovatika/collections/migrations/FromK5Instance.java
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) Dec 3, 2023 Pavel Stastny
+ *
+ * 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 3 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 cz.inovatika.collections.migrations;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.security.SecureRandom;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.core.MediaType;
+import javax.xml.bind.JAXBException;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerException;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.solr.client.solrj.SolrServerException;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.kramerius.Import;
+import org.kramerius.ImportModule;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.name.Names;
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+
+import cz.incad.kramerius.FedoraAccess;
+import cz.incad.kramerius.FedoraNamespaces;
+import cz.incad.kramerius.fedora.RepoModule;
+import cz.incad.kramerius.resourceindex.ProcessingIndexFeeder;
+import cz.incad.kramerius.resourceindex.ResourceIndexModule;
+import cz.incad.kramerius.service.SortingService;
+import cz.incad.kramerius.solr.SolrModule;
+import cz.incad.kramerius.statistics.NullStatisticsModule;
+import cz.incad.kramerius.utils.IterationUtils;
+import cz.incad.kramerius.utils.XMLUtils;
+import cz.incad.kramerius.utils.IterationUtils.IterationCallback;
+import cz.incad.kramerius.utils.IterationUtils.IterationEndCallback;
+import cz.incad.kramerius.utils.conf.KConfiguration;
+import cz.inovatika.collections.Restore;
+
+import static cz.incad.kramerius.utils.IterationUtils.*;
+
+public class FromK5Instance {
+
+
+ private static final String ALLOWED_CHARACTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+ public static String generateRandomString(int length) {
+ SecureRandom random = new SecureRandom();
+ StringBuilder sb = new StringBuilder(length);
+ for (int i = 0; i < length; i++) {
+ int randomIndex = random.nextInt(ALLOWED_CHARACTERS.length());
+ char randomChar = ALLOWED_CHARACTERS.charAt(randomIndex);
+ sb.append(randomChar);
+ }
+
+ return sb.toString();
+ }
+
+ public static Logger LOGGER = Logger.getLogger(FromK5Instance.class.getName());
+
+ public static void main(String[] args) throws IOException, ParserConfigurationException, SAXException, TransformerException, JAXBException, InterruptedException, SolrServerException, BrokenBarrierException {
+ LOGGER.log(Level.INFO, "Process parameters: " + Arrays.asList(args).toString());
+ if (args.length > 1) {
+ Client client = Client.create();
+
+ LOGGER.info("Reloading cloection");
+ String authToken = args[0];
+ String url = args[1];
+ if (url.endsWith("/")) {
+ url = url.substring(0,url.length()-1);
+ }
+
+ String tmpDirPath = System.getProperty("java.io.tmpdir");
+
+ String subdirectoryPath = tmpDirPath + File.separator + generateRandomString(5);
+ FileUtils.forceMkdir(new File(subdirectoryPath));
+
+ LOGGER.info(String.format("Generating temp folder %s", subdirectoryPath));
+
+
+ JSONArray collections = collections(client, url);
+ for (int i = 0; i < collections.length(); i++) {
+ JSONObject col = collections.getJSONObject(i);
+ String pid = col.getString("pid");
+ JSONObject desc = col.getJSONObject("descs");
+
+ List pids = pids(client, pid, url);
+ LOGGER.info(String.format("Found root pids %d", pids.size()));
+
+ Document foxml = foxml(client, pid, url);
+ createBiblioMods(foxml, desc);
+
+ // remove dc
+ removeStream(foxml,"DC");
+ removeStream(foxml,"AUDIT");
+ removeStream(foxml,"TEXT");
+ removeStream(foxml,"TEXT_cs");
+ removeStream(foxml,"TEXT_en");
+ addContainsRelations(foxml, pids);
+
+ File vcFile = new File(subdirectoryPath, pid.replace(':', '_')+".xml");
+ try (FileOutputStream fos = new FileOutputStream(vcFile)) {
+ XMLUtils.print(foxml, new FileOutputStream(vcFile));
+ }
+ }
+
+ LOGGER.info("Scheduling import "+subdirectoryPath);
+ FromK5Instance.importTmpDir(subdirectoryPath, true, authToken);
+
+ } else {
+ throw new IllegalArgumentException("");
+ }
+ }
+
+ private static void addContainsRelations(Document foxml, List pids) {
+ //
+ Element relsExt = XMLUtils.findElement(foxml.getDocumentElement(), new XMLUtils.ElementsFilter() {
+ @Override
+ public boolean acceptElement(Element element) {
+ String localName = element.getLocalName();
+ String attribute = element.getAttribute("ID");
+ return localName != null && localName.equals("datastream") && attribute != null && attribute.equals("RELS-EXT");
+ }
+ });
+
+ if (relsExt != null) {
+ Element rdfDecription = XMLUtils.findElement(relsExt, new XMLUtils.ElementsFilter() {
+ @Override
+ public boolean acceptElement(Element element) {
+ String localName = element.getLocalName();
+ String nameSpace = element.getNamespaceURI();
+ return localName != null && localName.equals("Description") && nameSpace.equals(FedoraNamespaces.RDF_NAMESPACE_URI);
+ }
+ });
+
+ if (rdfDecription != null) {
+ for (String pid : pids) {
+ Element contains = foxml.createElementNS(FedoraNamespaces.ONTOLOGY_RELATIONSHIP_NAMESPACE_URI, "rel:contains");
+ contains.setPrefix("rel");
+ contains.setAttributeNS(FedoraNamespaces.RDF_NAMESPACE_URI, "rdf:resource", String.format("info:fedora/%s", pid));
+ rdfDecription.appendChild(contains);
+ }
+ }
+ }
+ }
+
+ private static void createBiblioMods(Document foxml, JSONObject desc) {
+ Element dataStream = foxml.createElementNS(FedoraNamespaces.FEDORA_FOXML_URI, "datastream");
+ dataStream.setAttribute("ID", "BIBLIO_MODS");
+ dataStream.setAttribute("CONTROL_GROUP", "X");
+ dataStream.setAttribute("STATE", "A");
+ dataStream.setAttribute("VERSIONABLE", "false");
+ foxml.getDocumentElement().appendChild(dataStream);
+
+
+ Element datastreamVersion = foxml.createElementNS(FedoraNamespaces.FEDORA_FOXML_URI, "datastreamVersion");
+ datastreamVersion.setAttribute("ID", "BIBLIO_MODS.0");
+
+ DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
+ // Získejte aktuální datum a čas jako LocalDateTime
+ LocalDateTime currentDateTime = LocalDateTime.now();
+ // Naformátujte aktuální datum a čas podle zadaného formátu
+ String formattedDateTime = currentDateTime.format(formatter);
+ datastreamVersion.setAttribute("CREATED", formattedDateTime);
+ datastreamVersion.setAttribute("MIMETYPE", "text/xml");
+ datastreamVersion.setAttribute("FORMAT_URI", "http://www.loc.gov/mods/v3");
+ dataStream.appendChild(datastreamVersion);
+
+ Element xmlContent = foxml.createElementNS(FedoraNamespaces.FEDORA_FOXML_URI, "xmlContent");
+ datastreamVersion.appendChild(xmlContent);
+
+ Element modsCollection = foxml.createElementNS(FedoraNamespaces.BIBILO_MODS_URI, "modsCollection");
+ xmlContent.appendChild(modsCollection);
+
+ Element mods = foxml.createElementNS(FedoraNamespaces.BIBILO_MODS_URI, "mods");
+ mods.setAttribute("version", "3.4");
+ modsCollection.appendChild(mods);
+
+ if (desc.has("cs")) {
+ String csAbstract = null;
+
+ // TEXT_cs - stream
+ Element textcs = XMLUtils.findElement(foxml.getDocumentElement(), new XMLUtils.ElementsFilter() {
+
+ @Override
+ public boolean acceptElement(Element element) {
+ String id = element.getAttribute("ID");
+ return id != null && id.equals("TEXT_cs");
+ }
+ });
+ if (textcs != null) {
+ Element binary = XMLUtils.findElement(textcs, new XMLUtils.ElementsFilter() {
+
+ @Override
+ public boolean acceptElement(Element element) {
+ String lname = element.getLocalName();
+ return lname != null && lname.equals("binaryContent");
+ }
+ });
+
+ csAbstract= new String(Base64.decodeBase64(binary.getTextContent()));
+ Element abstractElm = foxml.createElementNS(FedoraNamespaces.BIBILO_MODS_URI, "abstract");
+ abstractElm.setAttribute("lang", "cze");
+ abstractElm.setTextContent(csAbstract);
+ mods.appendChild(abstractElm);
+ }
+
+
+
+ Element titleInfo = foxml.createElementNS(FedoraNamespaces.BIBILO_MODS_URI, "titleInfo");
+ titleInfo.setAttribute("lang", "cze");
+ mods.appendChild(titleInfo);
+
+ Element title = foxml.createElementNS(FedoraNamespaces.BIBILO_MODS_URI, "title");
+ title.setTextContent(desc.getString("cs"));
+ titleInfo.appendChild(title);
+
+ }
+
+ if (desc.has("en")) {
+ String csAbstract = null;
+
+ // TEXT_cs - stream
+ Element textcs = XMLUtils.findElement(foxml.getDocumentElement(), new XMLUtils.ElementsFilter() {
+
+ @Override
+ public boolean acceptElement(Element element) {
+ String id = element.getAttribute("ID");
+ return id != null && id.equals("TEXT_en");
+ }
+ });
+ if (textcs != null) {
+ Element binary = XMLUtils.findElement(textcs, new XMLUtils.ElementsFilter() {
+
+ @Override
+ public boolean acceptElement(Element element) {
+ String lname = element.getLocalName();
+ return lname != null && lname.equals("binaryContent");
+ }
+ });
+
+ csAbstract= new String(Base64.decodeBase64(binary.getTextContent()));
+ Element abstractElm = foxml.createElementNS(FedoraNamespaces.BIBILO_MODS_URI, "abstract");
+ abstractElm.setAttribute("lang", "eng");
+ abstractElm.setTextContent(csAbstract);
+ mods.appendChild(abstractElm);
+ }
+
+ Element titleInfo = foxml.createElementNS(FedoraNamespaces.BIBILO_MODS_URI, "titleInfo");
+ titleInfo.setAttribute("lang", "eng");
+ mods.appendChild(titleInfo);
+
+ Element title = foxml.createElementNS(FedoraNamespaces.BIBILO_MODS_URI, "title");
+ title.setTextContent(desc.getString("en"));
+ titleInfo.appendChild(title);
+ }
+ }
+
+ private static void removeStream(Document foxml, String id) {
+ Element elm = XMLUtils.findElement(foxml.getDocumentElement(), new XMLUtils.ElementsFilter() {
+ @Override
+ public boolean acceptElement(Element element) {
+ String idAttrVal = element.getAttribute("ID");
+ return idAttrVal != null && idAttrVal.equals(id);
+ }
+ });
+ if (elm != null) elm.getParentNode().removeChild(elm);
+ }
+
+ //https://kramerius.lib.cas.cz/search/api/v5.0/vc
+ private static JSONArray collections(Client client, String api) throws IOException {
+ String url = String.format("%s/search/api/v5.0/vc", api);
+ LOGGER.info(String.format( "Requesting url is %s", url));
+ WebResource r = client.resource(url);
+
+ WebResource.Builder builder = r.accept(MediaType.APPLICATION_JSON);
+ InputStream clientResponse = builder.get(InputStream.class);
+ String string = IOUtils.toString(clientResponse, "UTF-8");
+ return new JSONArray(string);
+
+ }
+
+ private static Document foxml(Client client, String vcPid, String api)
+ throws ParserConfigurationException, SAXException, IOException {
+ String url = String.format("%s/search/api/v5.0/item/%s/foxml",api, vcPid);
+ LOGGER.info(String.format( "Requesting url is %s", url));
+ WebResource r = client.resource(url);
+
+ WebResource.Builder builder = r.accept(MediaType.APPLICATION_XML);
+ InputStream clientResponse = builder.get(InputStream.class);
+ String document = IOUtils.toString(clientResponse, "UTF-8");
+ String replaced = document.replaceAll("vc\\:", "uuid:");
+ Document parsed = XMLUtils.parseDocument(new StringReader(replaced), true);
+ return parsed;
+ }
+
+ //https://kramerius.lib.cas.cz/search/api/v5.0/search?q=*:*&fq=(fedora.model:monograph%20OR%20fedora.model:periodical%20OR%20fedora.model:sheetmusic%20OR%20fedora.model:monographunit)%20AND%20(collection:%22vc:5c2321df-2d8d-4a87-a262-15ffff990d81%22)&fl=PID,dostupnost,fedora.model,dc.creator,dc.title,dc.title,root_title,datum_str,dnnt-labels&facet=true&facet.mincount=1&facet.field=keywords&facet.field=language&facet.field=dnnt-labels&facet.field=mods.physicalLocation&facet.field=geographic_names&facet.field=facet_autor&facet.field=model_path&sort=created_date%20desc&rows=60&start=0
+
+ public static void collectionIterations(Client client,String address, String masterQuery,IterationCallback callback, IterationEndCallback endCallback) throws ParserConfigurationException, SAXException, IOException, InterruptedException, BrokenBarrierException {
+ String cursorMark = null;
+ String queryCursorMark = null;
+ do {
+ Element element = pidsCursorQuery(client, address, masterQuery, cursorMark);
+ cursorMark = IterationUtils.findCursorMark(element);
+ queryCursorMark = IterationUtils.findQueryCursorMark(element);
+ callback.call(element, cursorMark);
+ } while((cursorMark != null && queryCursorMark != null) && !cursorMark.equals(queryCursorMark));
+ endCallback.end();
+ }
+
+ static Element pidsCursorQuery(Client client, String url, String mq, String cursor) throws ParserConfigurationException, SAXException, IOException{
+ int rows = 1000;
+ String query = "search" + "?q="+mq + (cursor!= null ? String.format("&rows=%d&cursorMark=%s", rows, cursor) : String.format("&rows=%d&cursorMark=*", rows))+"&sort=" + URLEncoder.encode("PID desc", "UTF-8")+"&fl=PID";
+ return IterationUtils.executeQuery(client, url, query);
+ }
+
+
+ private static List pids(Client client, String vcPid, String api)
+ throws ParserConfigurationException, SAXException, IOException, InterruptedException, BrokenBarrierException {
+
+ final List returnPids = new ArrayList<>();
+
+ String url = null;
+ if (api.endsWith("/")) {
+ url = String.format("%ssearch/api/v5.0/",api);
+
+ } else {
+ url = String.format("%s/search/api/v5.0/",api);
+ }
+
+
+ List models = Arrays.asList(
+ "monograph",
+ "periodical",
+ "sheetmusic",
+ "monographunit"
+ );
+
+ String fqModel = "fedora.model:("+models.stream().collect(Collectors.joining(" OR "))+") AND ";
+ String collectionPid="(collection:\""+vcPid+"\")";
+ String masterQuery= URLEncoder.encode(fqModel + collectionPid, "UTF-8");
+
+ collectionIterations(client, url, masterQuery , (elm, i) -> {
+
+ Element result = XMLUtils.findElement(elm, new XMLUtils.ElementsFilter() {
+ @Override
+ public boolean acceptElement(Element element) {
+ String nodeName = element.getNodeName();
+ return nodeName.equals("result");
+ }
+ });
+ if (result != null) {
+ List elements = XMLUtils.getElements(result, new XMLUtils.ElementsFilter() {
+ @Override
+ public boolean acceptElement(Element element) {
+ String nodeName = element.getNodeName();
+ return nodeName.equals("doc");
+ }
+ });
+
+ List idents = elements.stream().map(item -> {
+ Element str = XMLUtils.findElement(item, new XMLUtils.ElementsFilter() {
+ @Override
+ public boolean acceptElement(Element element) {
+ return element.getNodeName().equals("str");
+ }
+ }
+ );
+ return str.getTextContent();
+ }
+ ).collect(Collectors.toList());
+
+ returnPids.addAll(idents);
+
+ }
+ }, ()->{});
+ return returnPids;
+ }
+
+
+ public static void importTmpDir(String exportRoot, boolean startIndexer, String authToken) throws JAXBException, IOException, InterruptedException, SAXException, SolrServerException {
+ Injector injector = Guice.createInjector(new SolrModule(), new ResourceIndexModule(), new RepoModule(), new NullStatisticsModule(), new ImportModule());
+ FedoraAccess fa = injector.getInstance(Key.get(FedoraAccess.class, Names.named("rawFedoraAccess")));
+ SortingService sortingServiceLocal = injector.getInstance(SortingService.class);
+ ProcessingIndexFeeder feeder = injector.getInstance(ProcessingIndexFeeder.class);
+
+ Import.run(fa, feeder, sortingServiceLocal,
+ KConfiguration.getInstance().getProperty("ingest.url"),
+ KConfiguration.getInstance().getProperty("ingest.user"),
+ KConfiguration.getInstance().getProperty("ingest.password"),
+ exportRoot, startIndexer, authToken);
+
+ Restore.LOGGER.info(String.format("Deleting directory %s", exportRoot));
+ File exportFolder = new File(exportRoot);
+ FileUtils.deleteDirectory(exportFolder);
+ }
+
+// public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException, InterruptedException, BrokenBarrierException {
+// Client client = Client.create();
+// List models = Arrays.asList(
+// "monograph",
+// "periodical",
+// "sheetmusic",
+// "monographunit"
+// );
+//
+// String fqModel = "fedora.model:("+models.stream().collect(Collectors.joining(" OR "))+") AND ";
+// String collectionPid="(collection:\"vc:809d92e6-abd0-4642-ba73-f1f259275d96\")";
+// String masterQuery= URLEncoder.encode(fqModel + collectionPid, "UTF-8");
+//
+// collectionIterations(client, "https://kramerius.lib.cas.cz/search/api/v5.0", masterQuery, (elm, i) -> {
+//
+// Element result = XMLUtils.findElement(elm, new XMLUtils.ElementsFilter() {
+// @Override
+// public boolean acceptElement(Element element) {
+// String nodeName = element.getNodeName();
+// return nodeName.equals("result");
+// }
+// });
+// if (result != null) {
+// List elements = XMLUtils.getElements(result, new XMLUtils.ElementsFilter() {
+// @Override
+// public boolean acceptElement(Element element) {
+// String nodeName = element.getNodeName();
+// return nodeName.equals("doc");
+// }
+// });
+//
+// List idents = elements.stream().map(item -> {
+// Element str = XMLUtils.findElement(item, new XMLUtils.ElementsFilter() {
+// @Override
+// public boolean acceptElement(Element element) {
+// return element.getNodeName().equals("str");
+// }
+// }
+// );
+// return str.getTextContent();
+// }
+// ).collect(Collectors.toList());
+//
+// System.out.println(idents);
+// }
+// }, ()->{});
+// }
+
+
+}
+
+
diff --git a/processes/collections-backup/src/main/java/cz/inovatika/collections/utils/ImportCollectionUtils.java b/processes/collections-backup/src/main/java/cz/inovatika/collections/utils/ImportCollectionUtils.java
new file mode 100644
index 000000000..17db0ea37
--- /dev/null
+++ b/processes/collections-backup/src/main/java/cz/inovatika/collections/utils/ImportCollectionUtils.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) Dec 3, 2023 Pavel Stastny
+ *
+ * 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 3 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 cz.inovatika.collections.utils;
+
+public class ImportCollectionUtils {
+
+ private ImportCollectionUtils() {}
+}
diff --git a/processes/import/src/main/java/org/kramerius/Import.java b/processes/import/src/main/java/org/kramerius/Import.java
index df578223f..64839898a 100644
--- a/processes/import/src/main/java/org/kramerius/Import.java
+++ b/processes/import/src/main/java/org/kramerius/Import.java
@@ -6,6 +6,8 @@
import com.google.inject.name.Names;
import com.qbizm.kramerius.imp.jaxb.*;
import cz.incad.kramerius.FedoraAccess;
+import cz.incad.kramerius.FedoraNamespaceContext;
+import cz.incad.kramerius.FedoraNamespaces;
import cz.incad.kramerius.fedora.RepoModule;
import cz.incad.kramerius.fedora.om.Repository;
import cz.incad.kramerius.fedora.om.RepositoryDatastream;
@@ -26,6 +28,7 @@
import cz.incad.kramerius.utils.pid.LexerException;
import cz.incad.kramerius.utils.pid.PIDParser;
import org.apache.solr.client.solrj.SolrServerException;
+import org.fcrepo.common.rdf.FedoraNamespace;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
@@ -167,9 +170,12 @@ public static void run(FedoraAccess fa, ProcessingIndexFeeder feeder, SortingSer
Set classicRoots = new HashSet();
Set convolutes = new HashSet();
+
+ Set collections = new HashSet();
+
Set sortRelations = new HashSet();
if (importFile.isDirectory()) {
- visitAllDirsAndFiles(fa, importFile, classicRoots, convolutes, sortRelations, updateExisting);
+ visitAllDirsAndFiles(fa, importFile, classicRoots, convolutes, collections, sortRelations, updateExisting);
} else {
BufferedReader reader = null;
try {
@@ -193,7 +199,7 @@ public static void run(FedoraAccess fa, ProcessingIndexFeeder feeder, SortingSer
continue;
}
log.info("Importing " + importItem.getAbsolutePath());
- visitAllDirsAndFiles(fa, importItem, classicRoots, convolutes, sortRelations, updateExisting);
+ visitAllDirsAndFiles(fa, importItem, classicRoots, convolutes, collections, sortRelations, updateExisting);
}
reader.close();
} catch (IOException e) {
@@ -220,6 +226,31 @@ public static void run(FedoraAccess fa, ProcessingIndexFeeder feeder, SortingSer
}
if (startIndexer) {
+
+ if (collections.isEmpty()) {
+ log.info("NO COLLECTIONS FOR INDEXING FOUND.");
+ } else {
+ try {
+ String waitIndexerProperty = System.getProperties().containsKey("ingest.startIndexer.wait") ? System.getProperty("ingest.startIndexer.wait") : KConfiguration.getInstance().getConfiguration().getString("ingest.startIndexer.wait", "1000");
+ // should wait
+ log.info("Waiting for soft commit :" + waitIndexerProperty + " s");
+ Thread.sleep(Integer.parseInt(waitIndexerProperty));
+
+ if (authToken != null) {
+ for (TitlePidTuple col : collections) {
+
+ ProcessScheduler.scheduleIndexation(col.pid, col.title, false, authToken);
+ }
+ log.info("ALL COLLECTIONS SCHEDULED FOR INDEXING.");
+ } else {
+ log.warning("cannot schedule indexation due to missing process credentials");
+ }
+ } catch (Exception e) {
+ log.log(Level.WARNING, e.getMessage(), e);
+ }
+ }
+
+
if (classicRoots.isEmpty()) {
log.info("NO ROOT OBJECTS FOR INDEXING FOUND.");
} else {
@@ -231,7 +262,13 @@ public static void run(FedoraAccess fa, ProcessingIndexFeeder feeder, SortingSer
if (authToken != null) {
for (TitlePidTuple root : classicRoots) {
- ProcessScheduler.scheduleIndexation(root.pid, root.title, true, authToken);
+
+ if (fa.isObjectAvailable(root.pid)) {
+ ProcessScheduler.scheduleIndexation(root.pid, root.title, true, authToken);
+ } else {
+ LOGGER.warning(String.format("Object '%s' does not exist in the repository. ", root.pid));
+ }
+
}
log.info("ALL ROOT OBJECTS SCHEDULED FOR INDEXING.");
} else {
@@ -282,7 +319,11 @@ protected PasswordAuthentication getPasswordAuthentication() {
of = new ObjectFactory();
}
- private static void visitAllDirsAndFiles(FedoraAccess fa, File importFile, Set classicRoots, Set convolutes, Set sortRelations, boolean updateExisting) {
+ private static void visitAllDirsAndFiles(FedoraAccess fa, File importFile, Set classicRoots,
+ Set convolutes,
+ Set collections,
+
+ Set sortRelations, boolean updateExisting) {
if (importFile == null) {
return;
}
@@ -301,7 +342,7 @@ private static void visitAllDirsAndFiles(FedoraAccess fa, File importFile, Set convolutes) {
+ private static void checkModelIsConvoluteOrCollection(DigitalObject dobj, Set convolutes, Set collections, Set roots) {
try {
boolean isConvolute = false;
+ boolean isCollection = false;
String title = "";
for (DatastreamType ds : dobj.getDatastream()) {
if ("DC".equals(ds.getID())) {//obtain title from DC stream
@@ -702,6 +744,30 @@ private static void checkModelIsConvolute(DigitalObject dobj, Set
if (type.startsWith("info:fedora/model:")) {
String model = type.substring(18);//get the string after info:fedora/model:
isConvolute = "convolute".equals(model);
+ isCollection = "collection".equals(model);
+ }
+ }
+ }
+ //
+ XmlContentType xmlContent = v.getXmlContent();
+ List any = xmlContent.getAny();
+ for (Element elm : any) {
+ List pids = XMLUtils.getElementsRecursive(elm, new XMLUtils.ElementsFilter() {
+ @Override
+ public boolean acceptElement(Element element) {
+ boolean equals = element.getLocalName().equals("contains");
+ return equals;
+ }
+ });
+ for (int i = 0; i < pids.size();i++) {
+ String attributeNS = pids.get(i).getAttributeNS(FedoraNamespaces.RDF_NAMESPACE_URI, "resource");
+ if (attributeNS.contains("info:fedora/")) {
+ String rootPid = attributeNS.substring("info:fedora/".length());
+ TitlePidTuple npt = new TitlePidTuple(rootPid, rootPid);
+
+ LOGGER.info(String.format("Adding contains relation from collection %s", rootPid));
+
+ roots.add(npt);
}
}
}
@@ -714,6 +780,13 @@ private static void checkModelIsConvolute(DigitalObject dobj, Set
convolutes.add(npt);
log.info("Found (convolute) object for indexing - " + npt);
}
+ } else if (isCollection) {
+ TitlePidTuple npt = new TitlePidTuple(title, dobj.getPID());
+ if (collections != null) {
+ collections.add(npt);
+ log.info("Found (collection) object for indexing - " + npt);
+ }
+
}
} catch (Exception ex) {
diff --git a/processes/nkp-logs/src/main/java/cz/incad/kramerius/statistics/impl/nkp/SendEmail.java b/processes/nkp-logs/src/main/java/cz/incad/kramerius/statistics/impl/nkp/SendEmail.java
new file mode 100644
index 000000000..5dcf27838
--- /dev/null
+++ b/processes/nkp-logs/src/main/java/cz/incad/kramerius/statistics/impl/nkp/SendEmail.java
@@ -0,0 +1,49 @@
+package cz.incad.kramerius.statistics.impl.nkp;
+import java.util.Properties;
+import javax.mail.*;
+import javax.mail.internet.*;
+
+public class SendEmail {
+
+ public static void main(String[] args) {
+
+ // Nastavení e-mailového účtu Gmail
+ String username = "k4system@gmail.com";
+ //String username = "pavel.stastny@inovatika.cz";
+
+ //String password = "veje vbng xuhv goyq";
+ String password = "cast wosb orpa jyny";
+
+ // Nastavení vlastností pro spojení s Gmail SMTP serverem
+ Properties props = new Properties();
+ props.put("mail.smtp.auth", "true");
+ props.put("mail.smtp.starttls.enable", "true");
+ props.put("mail.smtp.host", "smtp.gmail.com");
+ props.put("mail.smtp.port", "587");
+
+ // Vytvoření Session
+ Session session = Session.getInstance(props, new Authenticator() {
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+
+ try {
+ // Vytvoření zprávy
+ Message message = new MimeMessage(session);
+ message.setFrom(new InternetAddress(username));
+ message.setRecipients(Message.RecipientType.TO, InternetAddress.parse("pavel.stastny@gmail.com"));
+ message.setSubject("Předmět zprávy");
+ message.setText("Toto je text zprávy.");
+
+ // Odeslání zprávy
+ Transport.send(message);
+
+ System.out.println("Zpráva byla úspěšně odeslána.");
+
+ } catch (MessagingException e) {
+ e.printStackTrace();
+ System.out.println("Chyba při odesílání zprávy: " + e.getMessage());
+ }
+ }
+}
diff --git a/rest/build.gradle b/rest/build.gradle
index 1bff81ea4..1a0f91907 100644
--- a/rest/build.gradle
+++ b/rest/build.gradle
@@ -13,6 +13,7 @@ dependencies {
// sdnnt
api project(':processes:sdnnt') //TODO: Move
+ api group: 'commons-fileupload', name: 'commons-fileupload', version: '1.4'
api name:"iiif-presentation-model-api-3.2.5"
api name:"iiif-presentation-model-impl-3.2.5"
diff --git a/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/ServerFilesResource.java b/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/ServerFilesResource.java
index 31c900cd0..0bbce2c02 100644
--- a/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/ServerFilesResource.java
+++ b/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/ServerFilesResource.java
@@ -52,6 +52,47 @@ public class ServerFilesResource extends AdminApiResource {
@javax.inject.Inject
GenerateDownloadLinks genDownloadLinks;
+ @GET
+ @Path("/output-data-dir-for_collectionsbackup{path: (.+)?}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response listFilesInOutputDataDirFor_collections_backup(@PathParam("path") String path,@QueryParam("generatedownloads") Boolean downloadLinks) {
+ try {
+ User user1 = this.userProvider.get();
+ List roles = Arrays.stream(user1.getGroups()).map(Role::getName).collect(Collectors.toList());
+ if (!permitNKPLogsFolders(user1)) {
+ throw new ForbiddenException("user '%s' is not allowed to list files on server (missing action '%s')", user1.getLoginname(), SecuredActions.A_IMPORT.getFormalName()); //403
+ }
+
+ String collectionsFolder = KConfiguration.getInstance().getConfiguration().getString("collections.backup.folder");
+ if (collectionsFolder != null) {
+ File f = new File(collectionsFolder, path);
+ if (f.isDirectory()) {
+ return listFilesInDir("collections.backup.folder", path, (file)->{
+ return file.isFile() && file.getName().endsWith(".zip");
+ },Comparator.comparing(File::lastModified).reversed(), -1);
+ } else {
+
+ JSONObject fileJson = new JSONObject();
+ fileJson.put("name", f.getName());
+ fileJson.put("isDir", f.isDirectory());
+
+ fileInfo(f, fileJson);
+
+ if (downloadLinks) {
+ fileJson.put("downloadlink", genDownloadLinks.generateTmpLink(f));
+ }
+
+ return Response.ok(fileJson).build();
+ }
+ } else {
+ return Response.status(Response.Status.BAD_REQUEST).build();
+ }
+ } catch (Exception e) {
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+ }
+ }
+
+ /** Output from nkplogs */
@GET
@Path("/output-data-dir-for_nkplogs{path: (.+)?}")
@Produces(MediaType.APPLICATION_JSON)
diff --git a/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/Collection.java b/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/Collection.java
index eaa006ebd..e30f4bf95 100644
--- a/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/Collection.java
+++ b/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/Collection.java
@@ -6,38 +6,53 @@
import org.json.JSONObject;
import java.time.LocalDateTime;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+/**
+ * Collection data object
+ */
public final class Collection {
-
+
+ public static enum ThumbnailbStateEnum {
+ thumb, content, none;
+ }
+
+ /** pid */
public String pid;
+ /** Localized names */
public String nameUndefined;
public Map names = new HashMap<>();
+
+ /** Localized descriptions */
+ public Map descriptions = new HashMap<>();
-// public String descriptionCz;
-// public String descriptionEn;
+ /** Localized keywords **/
+ public Map> keywords = new HashMap<>();
- public Map descriptions = new HashMap<>();
public String descriptionUndefined;
-// public String contentCz;
-// public String contentEn;
-
+ /** Localized contents */
public Map contents = new HashMap<>();
public String contentUndefined;
public LocalDateTime created;
public LocalDateTime modified;
public Boolean standalone;
-
+
+ /** Content; pids of objects */
public List items;
+ public List clippingItems;
-
+ public String author;
+
+ public ThumbnailbStateEnum thumbnailInfo = ThumbnailbStateEnum.none;
+
@Override
public String toString() {
return "Collection [pid=" + pid + ", names=" + names + ", descriptions=" + descriptions + ", contents="
@@ -50,74 +65,86 @@ public Collection() {
public Collection(Collection original) {
this.pid = original.pid;
-// this.nameCz = original.nameCz;
-// this.nameEn = original.nameEn;
this.names = original.names;
-
-// this.descriptionCz = original.descriptionCz;
-// this.descriptionEn = original.descriptionEn;
this.descriptions = original.descriptions;
-
-// this.contentCz = original.contentCz;
-// this.contentEn = original.contentEn;
this.contents = original.contents;
this.created = original.created;
this.modified = original.modified;
this.standalone = original.standalone;
this.items = original.items;
+
+ this.author = original.author;
+
+ this.keywords = original.keywords;
+
+ this.thumbnailInfo = original.thumbnailInfo;
+ this.clippingItems = original.clippingItems;
}
public Collection(JSONObject definition) throws JSONException {
- System.out.println(definition.toString(1));
if (definition.has("pid")) {
this.pid = definition.getString("pid");
}
-// if (definition.has("name_cze")) {
-// this.nameCz = definition.getString("name_cze").trim();
-// }
-// if (definition.has("name_eng")) {
-// this.nameEn = definition.getString("name_eng").trim();
-// }
- mapLoadFromJSON(definition,"names", this.names);
-
-// if (this.nameCz != null) this.names.put("cze", this.nameCz);
-// if (this.nameEn != null) this.names.put("eng", this.nameCz);
-
-
-// if (definition.has("description_cze")) {
-// this.descriptionCz = definition.getString("description_cze").trim();
-// }
-// if (definition.has("description_eng")) {
-// this.descriptionEn = definition.getString("description_eng").trim();
-// }
-
- mapLoadFromJSON(definition,"descriptions", this.descriptions);
-// if (this.descriptionCz != null) this.descriptions.put("cze", this.descriptionCz);
-// if (this.descriptionEn != null) this.descriptions.put("eng", this.descriptionEn);
-//
-//
-// if (definition.has("content_cze")) {
-// this.contentCz = definition.getString("content_cze").trim();
-// }
-// if (definition.has("content_eng")) {
-// this.contentEn = definition.getString("content_eng").trim();
-// }
-
- mapLoadFromJSON(definition,"contents", this.contents);
-
-// if (this.contentCz != null) this.contents.put("cze", this.contentCz);
-// if (this.contentEn != null) this.contents.put("eng", this.contentEn);
+ if (definition.has("author")) {
+ this.author = definition.getString("author");
+ }
+
+ mapLoadFromJSONString(definition,"names", this.names);
+ mapLoadFromJSONString(definition,"descriptions", this.descriptions);
+ mapLoadFromJSONString(definition,"contents", this.contents);
+
+ mapLoadFromArray(definition, "keywords", this.keywords);
if (definition.has("standalone")) {
this.standalone = definition.getBoolean("standalone");
}
-
+ if (definition.has("thumbnail")) {
+ this.thumbnailInfo = ThumbnailbStateEnum.valueOf(definition.optString("thumbnail"));
+ }
+
+ if (definition.has("clippingitems")) {
+ List items = new ArrayList<>();
+ JSONArray jsonArray = definition.getJSONArray("clippingitems");
+ for (int i = 0; i < jsonArray.length(); i++) {
+ JSONObject clippingDef = jsonArray.getJSONObject(i);
+ items.add(CutItem.fromJSONObject(clippingDef));
+ }
+ this.clippingItems = items;
+ }
}
- private void mapLoadFromJSON(JSONObject definition, String mkey, Map map) {
+ private void mapLoadFromArray(JSONObject definition, String mkey, Map> map) {
+ Iso639Converter converter = new Iso639Converter();
+
+ if (definition.has(mkey)) {
+ JSONObject subObj = definition.getJSONObject(mkey);
+ subObj.keySet().forEach(key-> {
+ if (converter.isConvertable(key.toString())) {
+ List list = converter.convert(key.toString());
+ list.forEach(remappedKey-> {
+ List vals = new ArrayList<>();
+
+ JSONArray jsonArr = subObj.getJSONArray(key.toString());
+ for (int i = 0; i < jsonArr.length(); i++) { vals.add(jsonArr.getString(i));}
+
+ map.put(remappedKey.toString(), vals);
+ });
+ } else {
+ List vals = new ArrayList<>();
+
+ JSONArray jsonArr = subObj.getJSONArray(key.toString());
+ for (int i = 0; i < jsonArr.length(); i++) { vals.add(jsonArr.getString(i));}
+
+ map.put(key.toString(), vals);
+ }
+ });
+ }
+ }
+
+ private void mapLoadFromJSONString(JSONObject definition, String mkey, Map map) {
Iso639Converter converter = new Iso639Converter();
if (definition.has(mkey)) {
@@ -138,42 +165,30 @@ private void mapLoadFromJSON(JSONObject definition, String mkey, Map0) {
+ JSONArray jsonArr = new JSONArray();
+ this.clippingItems.stream().forEach(itm-> {
+ JSONObject itmJSON = itm.toJSON();
+ jsonArr.put(itmJSON);
+ });
+ json.put("clippingitems", jsonArr);
+ }
return json;
}
- private void mapToObj(JSONObject json, String masterKey, Map map) {
+ private void simpleMapToObj(JSONObject json, String masterKey, Map map) {
if (map != null && map.size()>0) {
JSONObject obj = new JSONObject();
map.keySet().forEach(key-> {
@@ -203,7 +227,22 @@ private void mapToObj(JSONObject json, String masterKey, Map map
}
}
-
+
+ private void arrayMapToObj(JSONObject json, String masterKey, Map> map) {
+ if (map != null && map.size()>0) {
+ JSONObject obj = new JSONObject();
+ map.keySet().forEach(key-> {
+ List vals = map.get(key);
+ JSONArray jsonArr = new JSONArray();
+ vals.stream().forEach(jsonArr::put);
+ obj.put(key, jsonArr);
+ });
+ json.put(masterKey, obj);
+
+ }
+ }
+
+
public boolean equalsInDataModifiableByClient(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@@ -211,7 +250,9 @@ public boolean equalsInDataModifiableByClient(Object o) {
return Objects.equals(names, that.names) &&
Objects.equals(descriptions, that.descriptions) &&
Objects.equals(contents, that.contents) &&
- Objects.equals(standalone, that.standalone);
+ Objects.equals(standalone, that.standalone) &&
+ Objects.equals(keywords, that.keywords) &&
+ Objects.equals(author, that.author);
}
diff --git a/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/CollectionsFoxmlBuilder.java b/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/CollectionsFoxmlBuilder.java
index ed678a30b..94223a235 100644
--- a/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/CollectionsFoxmlBuilder.java
+++ b/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/CollectionsFoxmlBuilder.java
@@ -3,16 +3,21 @@
import cz.incad.kramerius.repository.KrameriusRepositoryApi;
import cz.incad.kramerius.repository.RepositoryApi;
import cz.incad.kramerius.rest.apiNew.admin.v70.FoxmlBuilder;
+import cz.incad.kramerius.utils.StringUtils;
+
import org.apache.commons.lang.StringEscapeUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.QName;
+import java.text.BreakIterator;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Locale;
+import java.util.stream.Collectors;
public class CollectionsFoxmlBuilder extends FoxmlBuilder {
public Document buildFoxml(Collection collection, List pidsOfItemsInCollection) {
@@ -102,7 +107,55 @@ public Document buildMods(Collection collection) {
note.addText(StringEscapeUtils.escapeHtml(collection.contents.get(key)));
});
}
+ if (collection.keywords.size() > 0) {
+ collection.keywords.keySet().forEach(key-> {
+ List keywords = collection.keywords.get(key);
+ for (String keyword : keywords) {
+
+ Element subjectElm = addModsElement(mods, "subject");
+ subjectElm.addAttribute("lang", key);
+
+ Element topic = addModsElement(subjectElm, "topic");
+ topic.addText(keyword);
+ }
+
+ });
+ }
+
+
+ if (collection.author != null && StringUtils.isAnyString(collection.author)) {
+
+ List authorParts = new ArrayList<>();
+ BreakIterator wordIterator =
+ BreakIterator.getWordInstance(Locale.getDefault());
+ wordIterator.setText(collection.author);
+
+ // Iterace přes jednotlivá slova
+ int start = wordIterator.first();
+ for (int end = wordIterator.next(); end != BreakIterator.DONE; start = end, end = wordIterator.next()) {
+ String word = collection.author.substring(start, end);
+ authorParts.add(word);
+ }
+
+ if (authorParts.size() > 0) {
+
+ Element personalName = addModsElement(mods, "name");
+ personalName.addAttribute("type", "personal");
+ personalName.addAttribute("usage", "primary");
+
+ Element personalNamePart1 = addModsElement(personalName, "namePart");
+ personalNamePart1.addAttribute("type", "family");
+ personalNamePart1.setText(authorParts.get(0));
+
+ Element personalNamePart2 = addModsElement(personalName, "namePart");
+ personalNamePart2.addAttribute("type", "given");
+ personalNamePart2.setText(authorParts.subList(1, authorParts.size()).stream().collect(Collectors.joining(" ")));
+
+ }
+
+ }
+
return document;
}
diff --git a/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/CollectionsResource.java b/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/CollectionsResource.java
index 0adff96fa..7609cd8f8 100644
--- a/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/CollectionsResource.java
+++ b/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/CollectionsResource.java
@@ -5,7 +5,12 @@
import cz.incad.kramerius.fedora.om.RepositoryException;
import cz.incad.kramerius.repository.KrameriusRepositoryApi;
import cz.incad.kramerius.repository.RepositoryApi;
+import cz.incad.kramerius.repository.KrameriusRepositoryApi.KnownDatastreams;
+import cz.incad.kramerius.repository.KrameriusRepositoryApi.KnownXmlFormatUris;
import cz.incad.kramerius.rest.apiNew.admin.v70.AdminApiResource;
+import cz.incad.kramerius.rest.apiNew.admin.v70.collections.Collection.ThumbnailbStateEnum;
+import cz.incad.kramerius.rest.apiNew.admin.v70.collections.thumbs.SimpleIIIFGenerator;
+import cz.incad.kramerius.rest.apiNew.admin.v70.collections.thumbs.ThumbsGenerator;
import cz.incad.kramerius.rest.apiNew.exceptions.BadRequestException;
import cz.incad.kramerius.rest.apiNew.exceptions.ForbiddenException;
import cz.incad.kramerius.rest.apiNew.exceptions.InternalErrorException;
@@ -15,9 +20,20 @@
import cz.incad.kramerius.security.SpecialObjects;
import cz.incad.kramerius.security.User;
import cz.incad.kramerius.utils.Dom4jUtils;
+import cz.incad.kramerius.utils.StringUtils;
+import cz.incad.kramerius.utils.imgs.ImageMimeType;
+import cz.incad.kramerius.utils.imgs.KrameriusImageSupport;
import cz.incad.kramerius.utils.java.Pair;
import org.apache.commons.collections4.map.HashedMap;
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.commons.fileupload.MultipartStream;
+import org.apache.commons.fileupload.disk.DiskFileItemFactory;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.commons.io.FileSystemUtils;
+import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.http.protocol.HTTP;
import org.apache.solr.client.solrj.SolrServerException;
import org.dom4j.Attribute;
import org.dom4j.Document;
@@ -26,21 +42,44 @@
import org.json.JSONException;
import org.json.JSONObject;
+import javax.imageio.ImageIO;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
+import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
+
@Path("/admin/v7.0/collections")
public class CollectionsResource extends AdminApiResource {
-
+
+ // Stream name
+ private static final String COLLECTION_CLIPS = "COLLECTION_CLIPS";
+ private static final List THUMBS_GENERATOR = new ArrayList<>();
+ static {
+ THUMBS_GENERATOR.add(new SimpleIIIFGenerator());
+ }
+
public static final Logger LOGGER = Logger.getLogger(CollectionsResource.class.getName());
private static final int MAX_BATCH_SIZE = 100;
@@ -58,6 +97,9 @@ public class CollectionsResource extends AdminApiResource {
@Inject
RightsResolver rightsResolver;
+
+ @Inject
+ Provider requestProvider;
/**
* Creates new collection and assigns a pid to it.
@@ -110,8 +152,6 @@ public Response createCollection(JSONObject collectionDefinition) {
public Response getCollection(@PathParam("pid") String pid) {
try {
checkSupportedObjectPid(pid);
- //authentication
- //AuthenticatedUser user = getAuthenticatedUserByOauth();
User user1 = this.userProvider.get();
List roles = Arrays.stream(user1.getGroups()).map(Role::getName).collect(Collectors.toList());
@@ -134,6 +174,8 @@ public Response getCollection(@PathParam("pid") String pid) {
}
}
+
+
@GET
@Path("/prefix")
@Produces(MediaType.APPLICATION_JSON)
@@ -191,7 +233,8 @@ public Response getCollections(@QueryParam("withItem") String itemPid) {
!permitCollectionEdit(this.rightsResolver, user1, SpecialObjects.REPOSITORY.getPid())) {
throw new ForbiddenException("user '%s' is not allowed to create collections (missing action '%s')", user1.getLoginname(), SecuredActions.A_COLLECTIONS_READ); //403
}
-
+
+ // TODO: this kind of sync ??
synchronized (CollectionsResource.class) {
List pids = null;
if (itemPid != null) {
@@ -224,6 +267,64 @@ public Response getCollections(@QueryParam("withItem") String itemPid) {
throw new InternalErrorException(e.getMessage());
}
}
+
+ @POST
+ @Path("{pid}/image/thumb")
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ public Response uploadFile(@PathParam("pid") String pid,
+ InputStream mimeTypeStream
+ ) {
+ try {
+
+ HttpServletRequest req = this.requestProvider.get();
+
+ DiskFileItemFactory factory = new DiskFileItemFactory();
+ ServletFileUpload upload = new ServletFileUpload(factory);
+ List fileItems = upload.parseRequest(req);
+ if (fileItems.size() == 1) {
+ FileItem fileItem = fileItems.get(0);
+
+ InputStream fileItemStream = fileItem.getInputStream();
+ File tmpFile = File.createTempFile("image", "img");
+
+ // Kopírování dat ze vstupního proudu do souboru
+ try (OutputStream out = new FileOutputStream(tmpFile)) {
+ IOUtils.copy(fileItemStream, out);
+ }
+ synchronized (CollectionsResource.class) {
+ //Collection collection = fetchCollectionFromRepository(pid, false, false);
+
+ BufferedImage read = ImageIO.read(new FileInputStream(tmpFile));
+
+ // 127 height
+ // calculate scale factor
+ int height = read.getHeight();
+ int width = read.getWidth();
+
+ double factor = 127d / (double)height;
+ double newHeight = ((double)height * factor);
+ double newWidth = ((double)width * factor);
+
+
+ BufferedImage scaled = KrameriusImageSupport.scale(read, (int)newWidth, (int)newHeight);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ImageIO.write(scaled, "png", bos);
+
+ RepositoryApi repoApi = krameriusRepositoryApi.getLowLevelApi();
+ repoApi.updateBinaryDatastream(pid, KnownDatastreams.IMG_THUMB.name(), "image/png", bos.toByteArray());
+
+ Collection nCol = fetchCollectionFromRepository(pid, true, true);
+ return Response.ok(nCol.toJson()).build();
+ }
+ } else {
+ return Response.status(Response.Status.BAD_REQUEST).build();
+ }
+ } catch (RepositoryException | SolrServerException | IOException | FileUploadException e) {
+ LOGGER.log(Level.SEVERE,e.getMessage(),e);
+ throw new InternalErrorException(e.getMessage());
+ }
+ }
/**
* Updates collections metadata, but not items that collection directly contains.
@@ -276,14 +377,6 @@ public Response updateCollection(@PathParam("pid") String pid, JSONObject collec
}
}
- /* private void scheduleReindexation(String objectPid, String userid, String username, String indexationType, String batchToken, boolean ignoreInconsistentObjects, String title) {
- List paramsList = new ArrayList<>();
- paramsList.add(indexationType);
- paramsList.add(objectPid);
- paramsList.add(Boolean.toString(ignoreInconsistentObjects));
- paramsList.add(title);
- processSchedulingHelper.scheduleProcess("new_indexer_index_object", paramsList, userid, username, batchToken);
- }*/
/**
* Sets items that the collection directly contains. I.e. removes all existing items and adds all items from method's data.
@@ -515,12 +608,6 @@ private String findCyclicPath(String pid, String pidOfObjectNotAllowedOnPath, St
return null;
}
-
-// @PUT
-// @Path("{pid}/children_order")
-// @Produces(MediaType.APPLICATION_JSON)
-// public Response setChildrenOrder(@PathParam("pid") String pid, JSONObject newChildrenOrder) {
-
@PUT
@Path("{collectionPid}/items/delete_batch_items")
@Produces(MediaType.APPLICATION_JSON)
@@ -700,7 +787,220 @@ public Response deleteCollection(@PathParam("pid") String pid) {
throw new InternalErrorException(e.getMessage());
}
}
+
+
+ @POST
+ @Path("{pid}/delete_clip_item")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response removeClipItem(@PathParam("pid") String collectionPid, String itemJsonObj) {
+ try {
+ JSONObject json = new JSONObject(itemJsonObj);
+ CutItem clipItem = CutItem.fromJSONObject(json);
+ if (clipItem == null) {
+ throw new BadRequestException("badREquest");
+ }
+
+ //check collection
+ checkSupportedObjectPid(collectionPid);
+ if (!isModelCollection(collectionPid)) {
+ throw new ForbiddenException("not a collection: " + collectionPid);
+ }
+ User user = this.userProvider.get();
+ if (!permitCollectionEdit(this.rightsResolver, user, collectionPid)) {
+ throw new ForbiddenException("user '%s' is not allowed to modify collection (missing action '%s')", user.getLoginname(), SecuredActions.A_COLLECTIONS_EDIT); //403
+ }
+
+ synchronized (CollectionsResource.class) {
+
+ JSONArray jsonArray = new JSONArray();
+ if (krameriusRepositoryApi.getLowLevelApi().datastreamExists(collectionPid, COLLECTION_CLIPS)) {
+ try(InputStream latestVersionOfDatastream = krameriusRepositoryApi.getLowLevelApi().getLatestVersionOfDatastream(collectionPid, COLLECTION_CLIPS)){
+ jsonArray = new JSONArray( IOUtils.toString(latestVersionOfDatastream, "UTF-8"));
+ }
+ }
+
+ int index = -1;
+ for (int i = 0; i < jsonArray.length(); i++) {
+ CutItem rawCutItem = CutItem.fromJSONObject(jsonArray.getJSONObject(i));
+ if (clipItem.equals(rawCutItem)) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index > -1) {
+ if (clipItem.getUrl() != null) {
+ String thumbName = clipItem.getThumbnailmd5();
+ if (this.krameriusRepositoryApi.getLowLevelApi().datastreamExists(collectionPid, thumbName)) {
+ this.krameriusRepositoryApi.getLowLevelApi().deleteDatastream(collectionPid, thumbName);
+ }
+
+ }
+
+ jsonArray.remove(index);
+
+ krameriusRepositoryApi.getLowLevelApi().updateBinaryDatastream(collectionPid, COLLECTION_CLIPS, "application/json", jsonArray.toString().getBytes(Charset.forName("UTF-8")));
+ Collection collection = fetchCollectionFromRepository(collectionPid, true, true);
+ return Response.ok(collection.toJson()).build();
+
+ } else {
+ return Response.status(Response.Status.BAD_REQUEST).build();
+ }
+ }
+ } catch (WebApplicationException e) {
+ throw e;
+ } catch (Throwable e) {
+ LOGGER.log(Level.SEVERE, e.getMessage(), e);
+ throw new InternalErrorException(e.getMessage());
+ }
+ }
+
+
+ @PUT
+ @Path("{collectionPid}/delete_batch_clipitems")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response removeClipItemsBatch(@PathParam("collectionPid") String collectionPid, String stringBatch) {
+ try {
+ JSONObject batch = new JSONObject(stringBatch);
+ JSONArray batchArray = batch.optJSONArray("clipitems");
+
+ checkSupportedObjectPid(collectionPid);
+ if (!isModelCollection(collectionPid)) {
+ throw new ForbiddenException("not a collection: " + collectionPid);
+ }
+ User user = this.userProvider.get();
+ if (!permitCollectionEdit(this.rightsResolver, user, collectionPid)) {
+ throw new ForbiddenException("user '%s' is not allowed to modify collection (missing action '%s')", user.getLoginname(), SecuredActions.A_COLLECTIONS_EDIT); //403
+ }
+ if (batchArray != null && batchArray.length() > 0) {
+ synchronized (CollectionsResource.class) {
+ boolean cuttingsModified = false;
+ Set thumbsToDelete = new LinkedHashSet<>();
+ JSONArray fetchedJSONArray = new JSONArray();
+ if (krameriusRepositoryApi.getLowLevelApi().datastreamExists(collectionPid, COLLECTION_CLIPS)) {
+ try(InputStream latestVersionOfDatastream = krameriusRepositoryApi.getLowLevelApi().getLatestVersionOfDatastream(collectionPid, COLLECTION_CLIPS)){
+ fetchedJSONArray = new JSONArray( IOUtils.toString(latestVersionOfDatastream, "UTF-8"));
+ }
+ }
+
+ for (int i = 0; i < batchArray.length(); i++) {
+ CutItem toDelete = CutItem.fromJSONObject(batchArray.getJSONObject(i));
+ if (toDelete == null) {
+ throw new BadRequestException("badREquest");
+ }
+
+ int index = -1;
+ for (int j = 0; j < fetchedJSONArray.length(); j++) {
+ CutItem rawCutItem = CutItem.fromJSONObject(fetchedJSONArray.getJSONObject(j));
+ if (toDelete.equals(rawCutItem)) {
+ index = j;
+ cuttingsModified = true;
+ break;
+ }
+ }
+
+ if (index > -1) {
+ if (toDelete.getUrl() != null) {
+ String thumbName = toDelete.getThumbnailmd5();
+ thumbsToDelete.add(thumbName);
+ }
+ fetchedJSONArray.remove(index);
+
+ } else {
+ return Response.status(Response.Status.BAD_REQUEST).build();
+ }
+ }
+ if (cuttingsModified) {
+ krameriusRepositoryApi.getLowLevelApi().updateBinaryDatastream(collectionPid, COLLECTION_CLIPS, "application/json", fetchedJSONArray.toString().getBytes(Charset.forName("UTF-8")));
+ for (String thumbName : thumbsToDelete) {
+ if (this.krameriusRepositoryApi.getLowLevelApi().datastreamExists(collectionPid, thumbName)) {
+ this.krameriusRepositoryApi.getLowLevelApi().deleteDatastream(collectionPid, thumbName);
+ }
+ }
+ }
+
+ }
+ Collection collection = fetchCollectionFromRepository(collectionPid, true, true);
+ return Response.ok(collection.toJson()).build();
+ } else {
+ throw new BadRequestException("badREquest");
+ }
+ } catch (WebApplicationException e) {
+ throw e;
+ } catch (Throwable e) {
+ LOGGER.log(Level.SEVERE, e.getMessage(), e);
+ throw new InternalErrorException(e.getMessage());
+ }
+ }
+
+ @POST
+ @Path("{pid}/add_clip_item")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response addClipItem(@PathParam("pid") String collectionPid, String itemJsonObj) {
+ try {
+ JSONObject json = new JSONObject(itemJsonObj);
+ CutItem clipItem = CutItem.fromJSONObject(json);
+ if (clipItem == null) {
+ throw new BadRequestException("badREquest");
+ }
+
+ //check collection
+ checkSupportedObjectPid(collectionPid);
+ if (!isModelCollection(collectionPid)) {
+ throw new ForbiddenException("not a collection: " + collectionPid);
+ }
+ User user = this.userProvider.get();
+ if (!permitCollectionEdit(this.rightsResolver, user, collectionPid)) {
+ throw new ForbiddenException("user '%s' is not allowed to modify collection (missing action '%s')", user.getLoginname(), SecuredActions.A_COLLECTIONS_EDIT); //403
+ }
+
+ synchronized (CollectionsResource.class) {
+
+ JSONArray jsonArray = new JSONArray();
+ if (krameriusRepositoryApi.getLowLevelApi().datastreamExists(collectionPid, COLLECTION_CLIPS)) {
+ try(InputStream latestVersionOfDatastream = krameriusRepositoryApi.getLowLevelApi().getLatestVersionOfDatastream(collectionPid, COLLECTION_CLIPS)){
+ jsonArray = new JSONArray( IOUtils.toString(latestVersionOfDatastream, "UTF-8"));
+ }
+ }
+
+ if (clipItem.getUrl() != null) {
+ String url = clipItem.getUrl();
+ String thumbName = clipItem.getThumbnailmd5();
+
+ THUMBS_GENERATOR.forEach(gen-> {
+ if (gen.acceptUrl(url)) {
+ try {
+ BufferedImage thumb = gen.generateThumbnail(url);
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ImageIO.write(thumb, "png", bos);
+
+ krameriusRepositoryApi.getLowLevelApi().updateBinaryDatastream(collectionPid, thumbName, "image/png", bos.toByteArray());
+ } catch (IOException | RepositoryException e) {
+ LOGGER.log(Level.SEVERE,e.getMessage(),e);
+ }
+ }
+ });
+ }
+ jsonArray.put(json);
+
+ krameriusRepositoryApi.getLowLevelApi().updateBinaryDatastream(collectionPid, COLLECTION_CLIPS, "application/json", jsonArray.toString().getBytes(Charset.forName("UTF-8")));
+ Collection collection = fetchCollectionFromRepository(collectionPid, true, true);
+ return Response.ok(collection.toJson()).build();
+ }
+ } catch (WebApplicationException e) {
+ throw e;
+ } catch (Throwable e) {
+ LOGGER.log(Level.SEVERE, e.getMessage(), e);
+ throw new InternalErrorException(e.getMessage());
+ }
+ }
+
+
+
+ // Udelat castecne ulozene na CDK
private Collection fetchCollectionFromRepository(String pid, boolean withContent, boolean withItems) throws
IOException, RepositoryException, SolrServerException {
@@ -711,6 +1011,8 @@ private Collection fetchCollectionFromRepository(String pid, boolean withContent
// index + akubra synchronization problem
}
+
+
collection.created = krameriusRepositoryApi.getLowLevelApi().getPropertyCreated(pid);
collection.modified = krameriusRepositoryApi.getLowLevelApi().getPropertyLastModified(pid);
@@ -773,14 +1075,62 @@ private Collection fetchCollectionFromRepository(String pid, boolean withContent
});
}
}
+ }
+ List subjectXPath = Dom4jUtils.elementsByXpath(mods.getRootElement(), "//mods/subject[@lang]");
+ for (Element s : subjectXPath) {
+ Attribute langAttr = s.attribute("lang");
+ List elms = s.elements();
+ List texts = s.elements().stream().map(Element::getTextTrim).collect(Collectors.toList());
+ if (!collection.keywords.containsKey(langAttr.getStringValue())) {
+ collection.keywords.put(langAttr.getStringValue(), new ArrayList<>());
+ }
+ collection.keywords.get(langAttr.getStringValue()).addAll(texts);
+ }
+
+ Element authorsXPath = Dom4jUtils.firstElementByXpath(mods.getRootElement(), "//mods/name[@type='personal']");
+ if (authorsXPath != null) {
+ String author = authorsXPath.elements().stream().map(Element::getTextTrim).collect(Collectors.joining(" "));
+ if (StringUtils.isAnyString(author)) {
+ collection.author = author;
+ }
}
+
//data from RELS-EXT
Document relsExt = krameriusRepositoryApi.getRelsExt(pid, false);
collection.standalone = Boolean.valueOf(Dom4jUtils.stringOrNullFromFirstElementByXpath(relsExt.getRootElement(), "//standalone"));
- //data from Processing index
+
+ List items = krameriusRepositoryApi.getPidsOfItemsInCollection(pid);
if (withItems) {
- collection.items = krameriusRepositoryApi.getPidsOfItemsInCollection(pid);
+ collection.items = items;
+ }
+
+ List streams = krameriusRepositoryApi.getLowLevelApi().getDatastreamNames(pid);
+ if (streams.contains(cz.kramerius.krameriusRepositoryAccess.KrameriusRepositoryFascade.KnownDatastreams.IMG_THUMB)) {
+ collection.thumbnailInfo = ThumbnailbStateEnum.thumb;
+ } else if (items.size() >0) {
+ collection.thumbnailInfo = ThumbnailbStateEnum.content;
+ } else {
+ collection.thumbnailInfo = ThumbnailbStateEnum.none;
+ }
+
+
+
+ if (krameriusRepositoryApi.getLowLevelApi().datastreamExists(pid, COLLECTION_CLIPS)) {
+
+ try(InputStream latestVersionOfDatastream = krameriusRepositoryApi.getLowLevelApi().getLatestVersionOfDatastream(pid, COLLECTION_CLIPS)) {
+ JSONArray jsonArray = new JSONArray( IOUtils.toString(latestVersionOfDatastream, "UTF-8"));
+ collection.clippingItems = CutItem.fromJSONArray(jsonArray);
+ collection.clippingItems.forEach(cl-> {
+ try {
+ cl.initGeneratedThumbnail(krameriusRepositoryApi.getLowLevelApi(), pid);
+ } catch (NoSuchAlgorithmException | RepositoryException | IOException e) {
+ LOGGER.log(Level.SEVERE,e.getMessage(),e);
+ }
+ });
+ }
+ } else {
+ collection.clippingItems = new ArrayList<>();
}
return collection;
}
diff --git a/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/CutItem.java b/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/CutItem.java
new file mode 100644
index 000000000..6c44c5a24
--- /dev/null
+++ b/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/CutItem.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) Nov 19, 2023 Pavel Stastny
+ *
+ * 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 3 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 cz.incad.kramerius.rest.apiNew.admin.v70.collections;
+
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import cz.incad.kramerius.fedora.om.RepositoryException;
+import cz.incad.kramerius.repository.RepositoryApi;
+
+public class CutItem {
+
+ public static final Logger LOGGER = Logger.getLogger(CutItem.class.getName());
+
+ private boolean generatedThumbnail;
+ private String name;
+ private String description;
+ private String url;
+
+
+ public CutItem() {}
+
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+
+ public void setGeneratedThumbnail(boolean generatedThumbnail) {
+ this.generatedThumbnail = generatedThumbnail;
+ }
+
+ public boolean containsGeneratedThumbnail() {
+ return generatedThumbnail;
+ }
+
+
+
+
+ public String getThumbnailmd5() throws NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ md.update(url.getBytes());
+ byte[] digest = md.digest();
+
+ StringBuilder hexString = new StringBuilder();
+ for (byte b : digest) {
+ hexString.append(String.format("%02x", b));
+ }
+ return "IMG_"+hexString.toString().toUpperCase();
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public static final List fromJSONArray(JSONArray jsonArray) {
+ List items = new ArrayList<>();
+ for (int i = 0; i < jsonArray.length(); i++) {
+ JSONObject clippingDef = jsonArray.getJSONObject(i);
+ items.add(CutItem.fromJSONObject(clippingDef));
+ }
+ return items;
+ }
+
+ public static final CutItem fromJSONObject(JSONObject obj) {
+ CutItem item = new CutItem();
+ if (obj.has("name")) {
+ item.setName(obj.getString("name"));
+ }
+
+ if (obj.has("description")) {
+ item.setDescription(obj.getString("description"));
+ }
+
+ if (obj.has("description")) {
+ item.setDescription(obj.getString("description"));
+ }
+
+ if (obj.has("url")) {
+
+ item.setUrl(obj.getString("url"));
+ }
+
+ return item;
+
+ }
+
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(description, name, url);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ CutItem other = (CutItem) obj;
+ return Objects.equals(description, other.description) && Objects.equals(name, other.name)
+ && Objects.equals(url, other.url);
+ }
+
+ @Override
+ public String toString() {
+ return "ClippingItem [name=" + name + ", description=" + description + ", url=" + url + "]";
+ }
+
+ public JSONObject toJSON() {
+ try {
+ JSONObject object = new JSONObject();
+ object.put("name", this.name);
+ object.put("description", this.description);
+ object.put("url", this.url);
+ if (this.generatedThumbnail) {
+ try {
+ String thumb = getThumbnailmd5();
+ object.put("thumb", thumb);
+ } catch(Exception ex) {
+ LOGGER.log(Level.SEVERE,ex.getMessage(),ex);
+
+ }
+ }
+ return object;
+ } catch (JSONException /*| NoSuchAlgorithmException */e) {
+ LOGGER.log(Level.SEVERE,e.getMessage(),e);
+ return new JSONObject();
+ }
+ }
+
+
+ public void initGeneratedThumbnail(RepositoryApi repoApi, String pid) throws NoSuchAlgorithmException, RepositoryException, IOException {
+ if (repoApi.datastreamExists(pid, getThumbnailmd5())) {
+ this.setGeneratedThumbnail(true);
+ }
+ }
+
+}
diff --git a/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/thumbs/IIFUtils.java b/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/thumbs/IIFUtils.java
new file mode 100644
index 000000000..1e7c6ae2c
--- /dev/null
+++ b/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/thumbs/IIFUtils.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) Nov 20, 2023 Pavel Stastny
+ *
+ * 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 3 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 cz.incad.kramerius.rest.apiNew.admin.v70.collections.thumbs;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+import javax.imageio.IIOImage;
+import javax.imageio.ImageIO;
+
+import org.apache.commons.lang3.tuple.Pair;
+
+public class IIFUtils {
+
+ public static void main(String[] args) throws MalformedURLException, IOException {
+ //String url = "https://k7.inovatika.dev/search/api/client/v7.0/items/uuid:722b67fb-0e53-44b4-b4fe-12b89cca4da7/image/iiif/395,715,780,419/max/0/default.jpg";
+ String url = "https://k7.inovatika.dev/search/api/client/v7.0/items/uuid:e7385d75-fac8-4ac0-b673-9e298d65dd68/image/iiif/360,913,1147,664/max/0/default.jpg";
+
+ Pair,Pair> parseBoundingBox = SimpleIIIFGenerator.parseBoundingBox(url);
+ Pair iiifInfo = SimpleIIIFGenerator.getIIIFDescriptor(url);
+
+ BufferedImage thumb = SimpleIIIFGenerator.getThumb(url);
+
+
+ double widthScaleFactor = (double) thumb.getWidth() / iiifInfo.getLeft();
+ double heightScaleFactor = (double) thumb.getHeight() / iiifInfo.getRight();
+
+ // Vybrání menšího scale factoru, aby zůstal obrázek uvnitř maximálních rozměrů
+ double scaleFactor = Math.min(widthScaleFactor, heightScaleFactor);
+ System.out.println(scaleFactor);
+
+ System.out.println(SimpleIIIFGenerator.scaleBoundingBox(parseBoundingBox, scaleFactor));
+
+ BufferedImage createImate = SimpleIIIFGenerator.createImate(thumb, SimpleIIIFGenerator.scaleBoundingBox(parseBoundingBox, scaleFactor));
+
+ File f = new File(System.getProperty("user.home")+File.separator+"test.png");
+
+ ImageIO.write(createImate, "png", f);
+
+
+
+ }
+}
diff --git a/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/thumbs/SimpleIIIFGenerator.java b/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/thumbs/SimpleIIIFGenerator.java
new file mode 100644
index 000000000..86449f2f9
--- /dev/null
+++ b/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/thumbs/SimpleIIIFGenerator.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) Nov 20, 2023 Pavel Stastny
+ *
+ * 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 3 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 cz.incad.kramerius.rest.apiNew.admin.v70.collections.thumbs;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.imageio.ImageIO;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.log4j.Logger;
+import org.json.JSONObject;
+
+
+import cz.incad.kramerius.utils.RESTHelper;
+import cz.incad.kramerius.utils.imgs.ImageMimeType;
+import cz.incad.kramerius.utils.imgs.KrameriusImageSupport;
+
+public class SimpleIIIFGenerator extends ThumbsGenerator {
+
+ public static final Logger LOGGER = Logger.getLogger(SimpleIIIFGenerator.class.getName());
+
+ public static final String REGEXP_LEGACY = ".+/iiif/(uuid:[a-fA-F0-9-]+)/\\d+,\\d+,\\d+,\\d+/max/0/default\\.jpg";
+
+ public static final String REGEXP = ".+/(uuid:[a-fA-F0-9-]+)(/image/iiif/)\\d+,\\d+,\\d+,\\d+/max/0/default\\.jpg";
+ public static final Pattern REGEXP_PATTERN = Pattern.compile(REGEXP);
+ public static final Pattern REGEXP_LEGACY_PATTERN = Pattern.compile(REGEXP_LEGACY);
+
+ @Override
+ public BufferedImage generateThumbnail(String url) throws IOException {
+ Pair,Pair> parseBoundingBox = parseBoundingBox(url);
+ Pair iiifInfo = getIIIFDescriptor(url);
+ BufferedImage thumb = getThumb(url);
+ double widthScaleFactor = (double) thumb.getWidth() / iiifInfo.getLeft();
+ double heightScaleFactor = (double) thumb.getHeight() / iiifInfo.getRight();
+ double scaleFactor = Math.min(widthScaleFactor, heightScaleFactor);
+ BufferedImage image = SimpleIIIFGenerator.createImate(thumb, scaleBoundingBox(parseBoundingBox, scaleFactor));
+ return image;
+ }
+
+ @Override
+ public boolean acceptUrl(String url) {
+ Matcher matcher = REGEXP_PATTERN.matcher(url);
+ if(!matcher.matches()) {
+ return REGEXP_LEGACY_PATTERN.matcher(url).matches();
+ } else return true;
+ }
+
+
+ static Pair extractBaseUrl(String imageUrl) throws MalformedURLException {
+ Matcher matcher = REGEXP_PATTERN.matcher(imageUrl);
+ Matcher legacyMatcher = REGEXP_LEGACY_PATTERN.matcher(imageUrl);
+ if (matcher.matches()) {
+ String pid = matcher.group(1);
+ String imageIiif = matcher.group(2);
+
+ int startIndex = imageUrl.indexOf(pid);
+ int endIndex = imageUrl.indexOf(imageIiif);
+
+ return Pair.of(imageUrl.substring(0, endIndex), true);
+ } else if (legacyMatcher.matches()) {
+ String pid = legacyMatcher.group(1);
+
+ int startIndex = imageUrl.indexOf(pid);
+ int endIndex = imageUrl.indexOf(pid)+pid.length();
+
+ return Pair.of(imageUrl.substring(0, endIndex),false);
+
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public static BufferedImage getThumb(String url) throws MalformedURLException, IOException {
+ Pair base = SimpleIIIFGenerator.extractBaseUrl(url);
+ if (base.getRight()) {
+ BufferedImage img = KrameriusImageSupport.readImage(new URL( base.getLeft()+"/image/thumb" ), ImageMimeType.JPEG, -1);
+ return img;
+ } else {
+ String baseUrl = base.getLeft();
+ baseUrl = baseUrl.replace("/iiif/", "/api/client/v7.0/items/");
+
+ BufferedImage img = KrameriusImageSupport.readImage(new URL( baseUrl+"/image/thumb" ), ImageMimeType.JPEG, -1);
+ return img;
+ }
+ }
+
+
+ public static String buildInfoJsonUrl(Pair baseUrl) throws MalformedURLException {
+ if (baseUrl.getRight()) {
+ return baseUrl.getLeft() + "/image/iiif/info.json";
+ } else {
+ return baseUrl.getLeft() + "/info.json";
+ }
+ }
+
+ public static Pair, Pair> scaleBoundingBox(
+ Pair, Pair> boundingBox, double scaleFactor) {
+
+ // Extrahování hodnot z bounding boxu
+ int x = boundingBox.getKey().getKey();
+ int y = boundingBox.getKey().getValue();
+ int width = boundingBox.getValue().getKey();
+ int height = boundingBox.getValue().getValue();
+
+
+ int scaledX = (int) (x * scaleFactor);
+ int scaledY = (int) (y * scaleFactor);
+
+ int scaledWidth = (int) (width * scaleFactor);
+ int scaledHeight = (int) (height * scaleFactor);
+
+ Pair scaledOrigin = Pair.of(scaledX, scaledY);
+ Pair scaledSize = Pair.of(scaledWidth, scaledHeight);
+
+ return Pair.of(scaledOrigin, scaledSize);
+ }
+
+ public static Pair getIIIFDescriptor(String url) throws IOException {
+
+ Pair baseUrl = SimpleIIIFGenerator.extractBaseUrl(url);
+ //String itemId = SimpleIIIFGenerator.extractItemId(url);
+ String infoJsonUrl = SimpleIIIFGenerator.buildInfoJsonUrl(baseUrl);
+
+ InputStream inputStream = RESTHelper.inputStream(infoJsonUrl, null, null);
+ JSONObject jsonObject = new JSONObject( IOUtils.toString(inputStream, "UTF-8") );
+ if (jsonObject != null) {
+ int maxWidth = (int) jsonObject.get("width");
+ int maxHeight = (int) jsonObject.get("height");
+
+ return Pair.of(maxWidth, maxHeight);
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public static Pair, Pair> parseBoundingBox(String url) {
+ String regex = "/(\\d+),(\\d+),(\\d+),(\\d+)/";
+ Pattern pattern = Pattern.compile(regex);
+ Matcher matcher = pattern.matcher(url);
+ if (matcher.find()) {
+ int x = Integer.parseInt(matcher.group(1));
+ int y = Integer.parseInt(matcher.group(2));
+ int w = Integer.parseInt(matcher.group(3));
+ int h = Integer.parseInt(matcher.group(4));
+
+ Pair firstPair = Pair.of(x, y);
+ Pair secondPair = Pair.of(w, h);
+ Pair, Pair> resultPair = Pair.of(firstPair, secondPair);
+
+ return resultPair;
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public static BufferedImage createImate(BufferedImage puvodniObrazek, Pair, Pair> range) {
+
+ Graphics graphics = puvodniObrazek.getGraphics();
+ BufferedImage novyObrazek = new BufferedImage(
+ puvodniObrazek.getWidth(), puvodniObrazek.getHeight(), BufferedImage.TYPE_INT_ARGB);
+ for (int y = 0; y < puvodniObrazek.getHeight(); y++) {
+ for (int x = 0; x < puvodniObrazek.getWidth(); x++) {
+ Color novaBarva = null;
+ if (naOkrajiOblasti(x, y, range)) {
+ novaBarva = new Color(0,0,0,255);
+ } else {
+ int alpha = jeVOblasti(x, y, range) ? 0 : 200;
+ novaBarva = new Color(196, 200, 207, alpha);
+ }
+ novyObrazek.setRGB(x, y, novaBarva.getRGB());
+ }
+ }
+
+ graphics.drawImage(novyObrazek, 0, 0, novyObrazek.getWidth(), novyObrazek.getHeight(), 0, 0, novyObrazek.getWidth(), novyObrazek.getHeight(), null);
+ return puvodniObrazek;
+ }
+
+ private static boolean naOkrajiOblasti(int x, int y, Pair, Pair> oblast) {
+ /**
+ * x horni startX - startX + sirka -1
+ * y - startY - startY + 1
+ */
+
+ int startX = oblast.getKey().getKey();
+ int startY = oblast.getKey().getValue();
+ int sirka = oblast.getValue().getKey();
+ int vyska = oblast.getValue().getValue();
+
+ if ((x >= startX && x <= startX + sirka - 1) && (y == startY || y == startY + vyska-1)) {
+ return true;
+ } else if ((y >= startY && y <= startY + vyska - 1) && (x == startX || x == startX + sirka-1)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static boolean jeVOblasti(int x, int y, Pair, Pair> oblast) {
+ int startX = oblast.getKey().getKey();
+ int startY = oblast.getKey().getValue();
+ int sirka = oblast.getValue().getKey();
+ int vyska = oblast.getValue().getValue();
+
+ return x >= startX && x < startX + sirka && y >= startY && y < startY + vyska;
+ }
+
+
+
+}
diff --git a/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/thumbs/ThumbsGenerator.java b/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/thumbs/ThumbsGenerator.java
new file mode 100644
index 000000000..93485253c
--- /dev/null
+++ b/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/collections/thumbs/ThumbsGenerator.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) Nov 27, 2023 Pavel Stastny
+ *
+ * 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 3 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 cz.incad.kramerius.rest.apiNew.admin.v70.collections.thumbs;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+
+public abstract class ThumbsGenerator {
+
+ public abstract BufferedImage generateThumbnail(String url) throws IOException;
+
+ public abstract boolean acceptUrl(String url);
+}
diff --git a/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/processes/ProcessResource.java b/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/processes/ProcessResource.java
index 2c7116f56..1e812f5b1 100644
--- a/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/processes/ProcessResource.java
+++ b/rest/src/main/java/cz/incad/kramerius/rest/apiNew/admin/v70/processes/ProcessResource.java
@@ -259,7 +259,6 @@ public Response getProcessLogsOutByProcessUuid(@PathParam("process_uuid") String
@GET
@Path("by_process_uuid/{process_uuid}/logs/err")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
-
public Response getProcessLogsErrByProcessUuid(@PathParam("process_uuid") String processUuid,
@DefaultValue("err.txt") @QueryParam("fileName") String fileName) {
try {
@@ -776,6 +775,7 @@ private JSONObject lrPRocessToJSONObject(LRProcess lrProcess) throws JSONExcepti
//TODO: I18N
private String buildInitialProcessName(String defId, List params) {
+
try {
switch (defId) {
case "new_process_api_test":
@@ -839,9 +839,22 @@ private String buildInitialProcessName(String defId, List params) {
case "nkplogs": {
return String.format("Generování NKP logů pro období %s - %s", params.get(0), params.get(1));
}
+ case "backup-collections": {
+ return String.format("Vytváření zálohy '%s' pro %s", params.get(0), params.get(1));
+ }
+
+ case "restore-collections": {
+ return String.format("Obnoveni ze zálohy '%s'", params.get(0));
+ }
+
+ case "migrate-collections-from-k5": {
+ return String.format("Migrace sbírek z K5 instance - ('%s')", params.get(0));
+ }
+
case "sdnnt-sync": {
return "Synchronizace se SDNNT";
}
+
case "delete_tree": {
String pid = params.get(0);
String title = params.get(1);
@@ -1064,6 +1077,7 @@ private List paramsToList(String id, JSONObject params, Consumer paramsToList(String id, JSONObject params, Consumer result = new ArrayList<>();
return result;
}
-
+
+ case "backup-collections": {
+ String backupname = extractMandatoryParamString(params, "backupname");
+ List pidlist = extractOptionalParamStringList(params, "pidlist", null);
+
+ String target = "pidlist:" + pidlist.stream().collect(Collectors.joining(";"));
+
+ if (pidlist != null) {
+ pidlist.forEach(p-> {
+ try {
+ ObjectPidsPath[] pidPaths = this.solrAccess.getPidPaths(p);
+ User user = this.userProvider.get();
+ LRProcessDefinition definition = this.definitionManager.getLongRunningProcessDefinition("add_license");
+ boolean permit = SecurityProcessUtils.permitProcessByDefinedActionWithPid(rightsResolver, user, definition, p, pidPaths);
+ consumer.accept(permit);
+ } catch (IOException e) {
+ consumer.accept(false);
+ LOGGER.log(Level.SEVERE,e.getMessage(),e);
+ }
+ });
+ }
+
+ List result = new ArrayList<>();
+ result.add(target);
+ result.add(backupname);
+ return result;
+ }
+
+ case "restore-collections": {
+ String backupname = extractMandatoryParamString(params, "backupname");
+
+ ObjectPidsPath[] pidPaths = new ObjectPidsPath[] {
+ ObjectPidsPath.REPOSITORY_PATH
+ };
+ User user = this.userProvider.get();
+ LRProcessDefinition definition = this.definitionManager.getLongRunningProcessDefinition("add_license");
+ boolean permit = SecurityProcessUtils.permitProcessByDefinedActionWithPid(rightsResolver, user, definition, SpecialObjects.REPOSITORY.getPid(), pidPaths);
+ consumer.accept(permit);
+
+ List result = new ArrayList<>();
+ result.add(backupname);
+ return result;
+ }
+
+ case "migrate-collections-from-k5": {
+ String k5 = extractMandatoryParamString(params, "k5");
+
+ ObjectPidsPath[] pidPaths = new ObjectPidsPath[] {
+ ObjectPidsPath.REPOSITORY_PATH
+ };
+ User user = this.userProvider.get();
+ LRProcessDefinition definition = this.definitionManager.getLongRunningProcessDefinition("add_license");
+ boolean permit = SecurityProcessUtils.permitProcessByDefinedActionWithPid(rightsResolver, user, definition, SpecialObjects.REPOSITORY.getPid(), pidPaths);
+ consumer.accept(permit);
+
+ List result = new ArrayList<>();
+ result.add(k5);
+ return result;
+ }
+
case "delete_tree": {
String pid = extractMandatoryParamWithValuePrefixed(params, "pid", "uuid:");
Boolean ignoreIncostencies = extractOptionalParamBoolean(params, "ignoreIncosistencies", false);
diff --git a/rest/src/main/java/cz/incad/kramerius/rest/apiNew/client/v70/ItemsResource.java b/rest/src/main/java/cz/incad/kramerius/rest/apiNew/client/v70/ItemsResource.java
index 0e7fdba2c..53f3024ee 100644
--- a/rest/src/main/java/cz/incad/kramerius/rest/apiNew/client/v70/ItemsResource.java
+++ b/rest/src/main/java/cz/incad/kramerius/rest/apiNew/client/v70/ItemsResource.java
@@ -15,6 +15,7 @@
import cz.incad.kramerius.repository.RepositoryApi;
import cz.incad.kramerius.repository.utils.Utils;
import cz.incad.kramerius.rest.api.exceptions.ActionNotAllowed;
+import cz.incad.kramerius.rest.apiNew.admin.v70.collections.CutItem;
import cz.incad.kramerius.rest.apiNew.client.v70.epub.EPubFileTypes;
import cz.incad.kramerius.rest.apiNew.client.v70.utils.ProvidedLicensesUtils;
import cz.incad.kramerius.rest.apiNew.exceptions.BadRequestException;
@@ -26,21 +27,30 @@
import cz.incad.kramerius.security.Role;
import cz.incad.kramerius.security.SecuredActions;
import cz.incad.kramerius.security.User;
+import cz.incad.kramerius.service.ReplicateException;
import cz.incad.kramerius.service.replication.FormatType;
+import cz.incad.kramerius.service.replication.ReplicationUtils;
import cz.incad.kramerius.statistics.accesslogs.AggregatedAccessLogs;
import cz.incad.kramerius.utils.ApplicationURL;
import cz.incad.kramerius.utils.Dom4jUtils;
import cz.incad.kramerius.utils.FedoraUtils;
import cz.incad.kramerius.utils.RESTHelper;
+import cz.incad.kramerius.utils.StringUtils;
import cz.incad.kramerius.utils.XMLUtils;
import cz.incad.kramerius.utils.imgs.ImageMimeType;
import cz.incad.kramerius.utils.java.Pair;
+
+import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.solr.client.solrj.SolrServerException;
import org.codehaus.jettison.json.JSONArray;
+import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
+import org.dom4j.Namespace;
import org.dom4j.Node;
+import org.dom4j.QName;
+import org.dom4j.io.DOMWriter;
import org.json.JSONException;
import org.json.JSONObject;
import org.xml.sax.SAXException;
@@ -67,9 +77,12 @@
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.Charset;
+import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -551,15 +564,62 @@ public Response getImgFull(@PathParam("pid") String pid) {
public Response getFoxml(@PathParam("pid") String pid) {
try {
checkSupportedObjectPid(pid);
- //authentication
- // zrusit autentizaci a vratit k
//checkUserIsAllowedToReadObject(pid); //autorizace podle zdroje přístupu, POLICY apod. (by JSESSIONID)
checkObjectExists(pid);
Document foxml = krameriusRepositoryApi.getLowLevelApi().getFoxml(pid);
// remove streams
Document modifiedFoxml = removeSecuredDatastreams(foxml);
+
+
if (modifiedFoxml != null) {
+ String model = model(modifiedFoxml);
+ if (StringUtils.isAnyString(model) && model.equals("info:fedora/model:collection")) {
+
+
+ String streamName = "COLLECTION_CLIPS";
+ String collectionClipsContent = null;
+ // clipping_items
+ try(InputStream cutters = this.krameriusRepositoryApi.getLowLevelApi().getLatestVersionOfDatastream(pid,streamName)) {
+ if (cutters != null) {
+ byte[] content = replaceLocationByBinaryContent(modifiedFoxml, cutters, streamName);
+ collectionClipsContent = new String(content, "UTF-8");
+ }
+ }
+ // thumbs from cutters
+ if (collectionClipsContent!= null) {
+ org.json.JSONArray jsonArray = new org.json.JSONArray(collectionClipsContent);
+
+ List cutItems = CutItem.fromJSONArray(jsonArray);
+ for (CutItem cutItem : cutItems) {
+ try {
+ cutItem.initGeneratedThumbnail(krameriusRepositoryApi.getLowLevelApi(), pid);
+ } catch (NoSuchAlgorithmException | RepositoryException | IOException e) {
+ LOGGER.log(Level.SEVERE,e.getMessage(),e);
+ }
+
+ if (cutItem.containsGeneratedThumbnail()) {
+ String clipThumbName = cutItem.getThumbnailmd5();
+ // clipThumbName
+ try(InputStream cutters = this.krameriusRepositoryApi.getLowLevelApi().getLatestVersionOfDatastream(pid,clipThumbName)) {
+ if (cutters != null) {
+ replaceLocationByBinaryContent(modifiedFoxml, cutters, clipThumbName);
+ }
+ }
+
+ }
+ }
+ }
+
+
+ // thumb
+ try(InputStream imgThumb = this.krameriusRepositoryApi.getImgThumb(pid)) {
+ if (imgThumb != null) {
+ String streamThumbName = "IMG_THUMB";
+ replaceLocationByBinaryContent(modifiedFoxml, imgThumb, streamThumbName);
+ }
+ }
+ }
return Response.ok().entity(modifiedFoxml.asXML()).build();
} else {
throw new InternalErrorException("I cannot return the foxml object => Not all protected datastreams could be removed.");
@@ -573,6 +633,37 @@ public Response getFoxml(@PathParam("pid") String pid) {
}
}
+ private byte[] replaceLocationByBinaryContent(Document modifiedFoxml, InputStream imgThumb, String streamName)
+ throws IOException {
+ String datastreamXPath = String.format("/foxml:digitalObject/foxml:datastream[@ID='%s']/foxml:datastreamVersion", streamName);
+ Node thumbNode = Dom4jUtils.buildXpath(datastreamXPath).selectSingleNode(modifiedFoxml.getRootElement());
+
+ byte[] bytes = IOUtils.toByteArray(imgThumb);
+ if (thumbNode != null) {
+ List contentLocation = Dom4jUtils.buildXpath("//foxml:contentLocation").selectNodes(thumbNode);
+ for (Node node : contentLocation) { node.detach(); }
+ Element thumbElement = (Element) thumbNode;
+ Element binaryContent = thumbElement.addElement("binaryContent", thumbElement.getNamespaceURI());
+ binaryContent.setText(new String(Base64.encodeBase64(bytes)));
+ }
+ return bytes;
+ }
+
+ private String model(Document modifiedFoxml) {
+ Element modelNode = (Element) Dom4jUtils.buildXpath("//model:hasModel").selectSingleNode(modifiedFoxml.getRootElement());
+ if (modelNode != null) {
+ Namespace ns = new Namespace("rdf", Dom4jUtils.NAMESPACE_URIS.get("rdf"));
+ QName qname = new QName("resource", ns);
+ Attribute attribute = modelNode.attribute(qname);
+ if (attribute != null) {
+ return attribute.getStringValue();
+ }
+ }
+ return null;
+ }
+
+
+
private static Document removeSecuredDatastreams(Document foxmlDoc) {
synchronized(foxmlDoc) {
@@ -1107,9 +1198,73 @@ private void reportAccess(String pid, String streamName) {
LOGGER.log(Level.WARNING, "Can't write statistic records for " + pid + ", stream name: " + streamName, e);
}
}
+
+ // =========== Collection specific endpoints
- // =========== EPub specific endpoints
+ @GET
+ @Path("{pid}/collection/cuttings")
+ @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+ public Response getCollectionClips(@PathParam("pid") String pid) {
+ try {
+ checkSupportedObjectPid(pid);
+ checkObjectExists(pid);
+ if(!krameriusRepositoryApi.getLowLevelApi().datastreamExists(pid, "COLLECTION_CLIPS")) {
+ throw new NotFoundException();
+ } else {
+ String mimetype = krameriusRepositoryApi.getLowLevelApi().getDatastreamMimetype(pid, "COLLECTION_CLIPS");
+ org.json.JSONArray outputValue = new org.json.JSONArray();
+ try(InputStream istream = krameriusRepositoryApi.getLowLevelApi().getLatestVersionOfDatastream(pid, "COLLECTION_CLIPS")) {
+ org.json.JSONArray inputValue = new org.json.JSONArray(IOUtils.toString(istream, "UTF-8"));
+
+ List cutItems = CutItem.fromJSONArray(inputValue);
+ cutItems.forEach(cl-> {
+ try {
+ cl.initGeneratedThumbnail(krameriusRepositoryApi.getLowLevelApi(), pid);
+ } catch (NoSuchAlgorithmException | RepositoryException | IOException e) {
+ LOGGER.log(Level.SEVERE,e.getMessage(),e);
+ }
+ outputValue.put(cl.toJSON());
+ });
+ }
+ return Response.ok().entity(outputValue).type(mimetype).build();
+ }
+ } catch (WebApplicationException e) {
+ throw e;
+ } catch (Throwable e) {
+ LOGGER.log(Level.SEVERE, e.getMessage(), e);
+ throw new InternalErrorException(e.getMessage());
+ }
+ }
+
+ @GET
+ @Path("{pid}/collection/cuttings/image/{thumb_id}")
+ public Response getCollectionThumb(@PathParam("pid") String pid, @PathParam("thumb_id") String thumbId) {
+ try {
+ checkSupportedObjectPid(pid);
+ checkObjectExists(pid);
+ if(!krameriusRepositoryApi.getLowLevelApi().datastreamExists(pid, thumbId)) {
+ throw new NotFoundException("no image/thumb %s available for object %s ", thumbId, pid);
+ } else {
+ String mimetype = krameriusRepositoryApi.getLowLevelApi().getDatastreamMimetype(pid, thumbId);
+
+ InputStream istream = krameriusRepositoryApi.getLowLevelApi().getLatestVersionOfDatastream(pid, thumbId);
+ StreamingOutput stream = output -> {
+ IOUtils.copy(istream, output);
+ IOUtils.closeQuietly(istream);
+ };
+ return Response.ok().entity(stream).type(mimetype).build();
+ }
+ } catch (WebApplicationException e) {
+ throw e;
+ } catch (Throwable e) {
+ LOGGER.log(Level.SEVERE, e.getMessage(), e);
+ throw new InternalErrorException(e.getMessage());
+ }
+ }
+
+
+ // =========== EPub specific endpoints
@HEAD
@Path("{pid}/epub")
public Response isEpubAvailable(@PathParam("pid") String pid) {
@@ -1143,6 +1298,9 @@ private boolean isEpubMimeType(String pid, KrameriusRepositoryApi.KnownDatastrea
return epub;
}
+
+
+
@GET
@Path("{pid}/epub/{path: .*}")
public Response getPaths(@PathParam("pid") String pid, @PathParam("path") PathSegment pathSegment,@Context UriInfo info ) {
diff --git a/search/build.gradle b/search/build.gradle
index dcd67ea1d..cbee6f7a9 100644
--- a/search/build.gradle
+++ b/search/build.gradle
@@ -51,6 +51,7 @@ dependencies {
api project(':processes:sdnnt') //TODO: Move
api project(':processes:licenses')
+ api project(':processes:collections-backup')
api 'javax.servlet:jstl:1.2'
api 'taglibs:standard:1.1.2'
diff --git a/search/src/java/cz/incad/Kramerius/AbstractImageServlet.java b/search/src/java/cz/incad/Kramerius/AbstractImageServlet.java
index ead1d448f..5938a7f56 100644
--- a/search/src/java/cz/incad/Kramerius/AbstractImageServlet.java
+++ b/search/src/java/cz/incad/Kramerius/AbstractImageServlet.java
@@ -234,6 +234,7 @@ protected void onResponseReceived(HttpResponse response) throws HttpException, I
resp.setStatus(statusCode);
if (statusCode == 200) {
resp.setContentType(response.getEntity().getContentType().getValue());
+ LOGGER.fine(String.format("Set access-control-header %s ", "Access-Control-Allow-Origin *"));
resp.setHeader("Access-Control-Allow-Origin", "*");
Header cacheControl = response.getLastHeader("Cache-Control");
if (cacheControl != null) resp.setHeader(cacheControl.getName(), cacheControl.getValue());
diff --git a/search/src/java/cz/incad/Kramerius/CORSFilter.java b/search/src/java/cz/incad/Kramerius/CORSFilter.java
index 651711626..377b91021 100644
--- a/search/src/java/cz/incad/Kramerius/CORSFilter.java
+++ b/search/src/java/cz/incad/Kramerius/CORSFilter.java
@@ -3,9 +3,12 @@
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
+import java.util.logging.Logger;
public class CORSFilter implements Filter {
+ public static final Logger LOGGER = Logger.getLogger(CORSFilter.class.getName());
+
@Override
public void init(FilterConfig filterConfig) throws ServletException {
@@ -14,7 +17,10 @@ public void init(FilterConfig filterConfig) throws ServletException {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
- response.setHeader("Access-Control-Allow-Origin", "*");
+ if (!response.containsHeader("Access-Control-Allow-Origin")) {
+ LOGGER.fine(String.format("Set access-control-header %s ", "Access-Control-Allow-Origin *"));
+ response.setHeader("Access-Control-Allow-Origin", "*");
+ }
response.setHeader("Access-Control-Allow-Methods", "OPTIONS, POST, GET, PUT, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with" +
diff --git a/search/src/java/cz/incad/Kramerius/imaging/ImageStreamsServlet.java b/search/src/java/cz/incad/Kramerius/imaging/ImageStreamsServlet.java
index 6b8e579be..07238882b 100644
--- a/search/src/java/cz/incad/Kramerius/imaging/ImageStreamsServlet.java
+++ b/search/src/java/cz/incad/Kramerius/imaging/ImageStreamsServlet.java
@@ -23,6 +23,7 @@
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.logging.Level;
+import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@@ -48,6 +49,8 @@
*/
public class ImageStreamsServlet extends AbstractImageServlet {
+ public static final Logger LOGGER = Logger.getLogger(ImageStreamsServlet.class.getName());
+
/**
* Parameter for stream
*/
@@ -95,7 +98,9 @@ public boolean turnOnIterateScaling(String stream) {
@Override
protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ LOGGER.fine(String.format("Set access-control-header %s ", "Access-Control-Allow-Origin *"));
resp.setHeader("Access-Control-Allow-Origin", "*");
+
resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
String pid = req.getParameter(UUID_PARAMETER);
if (pid == null || pid.trim().equals("")) {
@@ -129,6 +134,7 @@ protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws S
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ LOGGER.fine(String.format("Set access-control-header %s ", "Access-Control-Allow-Origin *"));
resp.setHeader("Access-Control-Allow-Origin", "*");
resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
diff --git a/settings.gradle b/settings.gradle
index 79129c464..26a3db291 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -30,6 +30,8 @@ include 'processes:nkp-logs'
include 'processes:statistics'
include 'processes:sdnnt'
include 'processes:licenses'
+include 'processes:collections-backup'
+
include 'security:authfilters'
include 'security:oauth'
diff --git a/shared/common/build.gradle b/shared/common/build.gradle
index 1177e5b9f..270268836 100644
--- a/shared/common/build.gradle
+++ b/shared/common/build.gradle
@@ -63,6 +63,7 @@ dependencies {
api "com.sun.jersey:jersey-client:${jerseyversion}"
api "com.sun.jersey:jersey-json:${jerseyversion}"
+ api 'com.sun.jersey.contribs:jersey-multipart:1.19.4'
api "com.sun.jersey.contribs:jersey-apache-client:${jerseyversion}"
api "com.sun.jersey.contribs:jersey-guice:${jerseyversion}"
diff --git a/shared/common/src/main/java/cz/incad/kramerius/processes/NextSchedulerTask.java b/shared/common/src/main/java/cz/incad/kramerius/processes/NextSchedulerTask.java
index 47d0b38a0..6125e930f 100644
--- a/shared/common/src/main/java/cz/incad/kramerius/processes/NextSchedulerTask.java
+++ b/shared/common/src/main/java/cz/incad/kramerius/processes/NextSchedulerTask.java
@@ -33,7 +33,7 @@ public void run() {
try {
definitionManager.load();
List plannedProcess = lrProcessManager.getPlannedProcess(allowRunningProcesses());
- if (!plannedProcess.isEmpty()) {
+ if (!plannedProcess.isEmpty() && this.processScheduler.getApplicationLib() != null /* initalized */) {
List longRunningProcesses = lrProcessManager.getLongRunningProcesses(States.RUNNING);
if (longRunningProcesses.size() < allowRunningProcesses()) {
LRProcess lrProcess = plannedProcess.get(0);
@@ -43,7 +43,11 @@ public void run() {
LOGGER.fine("the maximum number of running processes is reached");
}
} else {
- LOGGER.fine("no planned process found");
+ if (this.processScheduler.getApplicationLib() == null) {
+ LOGGER.fine("scheduler is not initialized");
+ } else {
+ LOGGER.fine("no planned process found ");
+ }
}
this.processScheduler.scheduleNextTask();
} catch (Throwable e) {
diff --git a/shared/common/src/main/java/cz/incad/kramerius/processes/new_api/ProcessScheduler.java b/shared/common/src/main/java/cz/incad/kramerius/processes/new_api/ProcessScheduler.java
index 1184fbd19..8b124240a 100644
--- a/shared/common/src/main/java/cz/incad/kramerius/processes/new_api/ProcessScheduler.java
+++ b/shared/common/src/main/java/cz/incad/kramerius/processes/new_api/ProcessScheduler.java
@@ -57,6 +57,17 @@ public static void schedule(String processPayload, String parentProcessAuthToken
}
+
+ public static void scheduleImport(String folder,String parentProcessAuthToken) {
+ JSONObject data = new JSONObject();
+ data.put("defid", "import");
+ JSONObject params = new JSONObject();
+ params.put("inputDataDir", folder);
+ params.put("startIndexer", true);
+ data.put("params", params);
+ schedule(data.toString(), parentProcessAuthToken);
+ }
+
//TODO: cleanup
public static void scheduleIndexation(String pid, String title, boolean includingDescendants, String parentProcessAuthToken) {
JSONObject data = new JSONObject();
diff --git a/shared/common/src/main/java/cz/incad/kramerius/processes/res/lp.st b/shared/common/src/main/java/cz/incad/kramerius/processes/res/lp.st
index e05a1f068..1ec5cb5f1 100644
--- a/shared/common/src/main/java/cz/incad/kramerius/processes/res/lp.st
+++ b/shared/common/src/main/java/cz/incad/kramerius/processes/res/lp.st
@@ -674,4 +674,41 @@
-Xmx4096m -Xms256m -Dfile.encoding=UTF-8
a_generate_nkplogs
+
+
+ backup-collections
+ Collection backup
+ cz.inovatika.collections.Backup
+
+ lrOut
+
+ lrErr
+ -Xmx4096m -Xms256m -Dfile.encoding=UTF-8
+ a_collections_edit
+
+
+
+ restore-collections
+ Restore collections
+ cz.inovatika.collections.Restore
+
+ lrOut
+
+ lrErr
+ -Xmx4096m -Xms256m -Dfile.encoding=UTF-8
+ a_collections_edit
+
+
+
+ migrate-collections-from-k5
+ Collections from K5 instance
+ cz.inovatika.collections.migrations.FromK5Instance
+
+ lrOut
+
+ lrErr
+ -Xmx4096m -Xms256m -Dfile.encoding=UTF-8
+ a_collections_edit
+
+
diff --git a/shared/common/src/main/java/cz/incad/kramerius/repository/KrameriusRepositoryApi.java b/shared/common/src/main/java/cz/incad/kramerius/repository/KrameriusRepositoryApi.java
index 01cb255c7..7b224ded2 100644
--- a/shared/common/src/main/java/cz/incad/kramerius/repository/KrameriusRepositoryApi.java
+++ b/shared/common/src/main/java/cz/incad/kramerius/repository/KrameriusRepositoryApi.java
@@ -46,8 +46,11 @@ enum KnownDatastreams {
MIGRATION("MIGRATION"),
IMG_FULL_ADM("IMG_FULL_ADM"),
AUDIT("AUDIT"),
- TEXT_OCR_ADM("TEXT_OCR_ADM");
+ TEXT_OCR_ADM("TEXT_OCR_ADM"),
+ COLLECTION_CLIPPINGS("COLLECTION_CLIPPINGS");
+
+
private final String value;
KnownDatastreams(String value) {
diff --git a/shared/common/src/main/java/cz/incad/kramerius/repository/RepositoryApi.java b/shared/common/src/main/java/cz/incad/kramerius/repository/RepositoryApi.java
index bf4f93829..53cbb68f1 100644
--- a/shared/common/src/main/java/cz/incad/kramerius/repository/RepositoryApi.java
+++ b/shared/common/src/main/java/cz/incad/kramerius/repository/RepositoryApi.java
@@ -104,6 +104,11 @@ public interface RepositoryApi {
//UPDATE
public void updateInlineXmlDatastream(String pid, String dsId, Document streamDoc, String formatUri) throws RepositoryException, IOException;
+ public void updateBinaryDatastream(String pid, String streamName, String mimeType, byte[] byteArray) throws RepositoryException;
+
+
+ public void deleteDatastream(String pid, String streamName) throws RepositoryException;
+
/**
* @param ds part of FOXML that contains definition of the datastream. I.e. root element datastream with subelement(s) datastreamVersion.
*/
diff --git a/shared/common/src/main/java/cz/incad/kramerius/repository/RepositoryApiImpl.java b/shared/common/src/main/java/cz/incad/kramerius/repository/RepositoryApiImpl.java
index 7f1e988eb..33679dc01 100644
--- a/shared/common/src/main/java/cz/incad/kramerius/repository/RepositoryApiImpl.java
+++ b/shared/common/src/main/java/cz/incad/kramerius/repository/RepositoryApiImpl.java
@@ -3,6 +3,9 @@
import com.google.inject.Inject;
import com.google.inject.name.Named;
import com.qbizm.kramerius.imp.jaxb.DigitalObject;
+
+import cz.incad.kramerius.FedoraAccess;
+import cz.incad.kramerius.fedora.om.Repository;
import cz.incad.kramerius.fedora.om.RepositoryDatastream;
import cz.incad.kramerius.fedora.om.RepositoryException;
import cz.incad.kramerius.fedora.om.RepositoryObject;
@@ -411,27 +414,53 @@ public List getTripletSources(String targetPid) throws RepositoryExcept
});
return triplets;
}
+
@Override
public void updateInlineXmlDatastream(String pid, String dsId, Document streamDoc, String formatUri) throws RepositoryException, IOException {
Lock writeLock = AkubraDOManager.getWriteLock(pid);
try {
RepositoryObject object = akubraRepository.getObject(pid);
-
+
object.deleteStream(dsId);
object.createStream(dsId, "text/xml", new ByteArrayInputStream(streamDoc.asXML().getBytes(Charset.forName("UTF-8"))));
-// appendNewInlineXmlDatastreamVersion(foxml, dsId, streamDoc, formatUri);
-// updateLastModifiedTimestamp(foxml);
-// DigitalObject updatedDigitalObject = foxmlDocToDigitalObject(foxml);
-// akubraRepository.deleteObject(pid, false, false);
-// akubraRepository.ingestObject(updatedDigitalObject);
-// akubraRepository.commitTransaction();
} finally {
writeLock.unlock();
}
}
+ public void updateBinaryDatastream(String pid, String streamName, String mimeType, byte[] byteArray) throws RepositoryException {
+ Lock writeLock = AkubraDOManager.getWriteLock(pid);
+ try {
+ RepositoryObject object = akubraRepository.getObject(pid);
+ if (object != null) {
+ if (object.streamExists(streamName)) {
+ object.deleteStream(streamName);
+ }
+ ByteArrayInputStream bos = new ByteArrayInputStream(byteArray);
+ object.createManagedStream(streamName, mimeType, bos);
+ }
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ public void deleteDatastream(String pid, String streamName) throws RepositoryException {
+ Lock writeLock = AkubraDOManager.getWriteLock(pid);
+ try {
+ RepositoryObject object = akubraRepository.getObject(pid);
+ if (object != null) {
+ if (object.streamExists(streamName)) {
+ object.deleteStream(streamName);
+ }
+ }
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+
@Override
public void setDatastreamXml(String pid, String dsId, Document ds) throws RepositoryException, IOException {
Lock writeLock = AkubraDOManager.getWriteLock(pid);
diff --git a/shared/common/src/main/java/cz/incad/kramerius/utils/Dom4jUtils.java b/shared/common/src/main/java/cz/incad/kramerius/utils/Dom4jUtils.java
index fa40ab93d..47b9586f9 100644
--- a/shared/common/src/main/java/cz/incad/kramerius/utils/Dom4jUtils.java
+++ b/shared/common/src/main/java/cz/incad/kramerius/utils/Dom4jUtils.java
@@ -19,7 +19,7 @@
public class Dom4jUtils {
- private static Map NAMESPACE_URIS = new HashMap<>();
+ public static Map NAMESPACE_URIS = new HashMap<>();
static {
NAMESPACE_URIS.put("xsi", "http://www.w3.org/2001/XMLSchema-instance");
diff --git a/shared/common/src/main/java/res/configuration.properties b/shared/common/src/main/java/res/configuration.properties
index 948df970c..510c4791e 100644
--- a/shared/common/src/main/java/res/configuration.properties
+++ b/shared/common/src/main/java/res/configuration.properties
@@ -536,3 +536,6 @@ keycloak.secret=changeme
#Keycloak for generating json file
keycloak.auth-server-url=http://localhost:8083/auth/
keycloak.realm=kramerius
+
+
+collections.backup.folder=${sys:user.home}/.kramerius4/collection-backups