From 3f3c4d0117aa4a90fc8461dff1218a34b835bc70 Mon Sep 17 00:00:00 2001
From: bourgesl <bourges.laurent@gmail.com>
Date: Tue, 7 May 2019 15:27:17 +0200
Subject: [PATCH] added support for alternative data access URL(s): html + csv
 + landing page

---
 resources/xsl/4.1/doi2landing.xsl             | 24 ++++++++++++
 .../java/fr/osug/doi/GeneratePipeline.java    | 17 +++++++++
 .../java/fr/osug/doi/ProcessUrlPipeline.java  | 37 ++++++++++++++++---
 src/main/java/fr/osug/doi/ProjectConfig.java  | 18 +++++++++
 .../java/fr/osug/doi/domain/DoiCommon.java    | 12 ++++++
 .../java/fr/osug/doi/service/DoiService.java  | 10 ++++-
 .../db/migration/V1_1__altDataAccessUrl.sql   | 10 +++++
 7 files changed, 121 insertions(+), 7 deletions(-)
 create mode 100644 src/main/resources/db/migration/V1_1__altDataAccessUrl.sql

diff --git a/resources/xsl/4.1/doi2landing.xsl b/resources/xsl/4.1/doi2landing.xsl
index dcf94ec..c5711f9 100644
--- a/resources/xsl/4.1/doi2landing.xsl
+++ b/resources/xsl/4.1/doi2landing.xsl
@@ -26,6 +26,9 @@
     <xsl:param name="data_access_html" select="''"/>
     <xsl:param name="data_access_url" select="''"/>
 
+    <xsl:param name="data_access_alt_html" select="''"/>
+    <xsl:param name="data_access_alt_url" select="''"/>
+
     <!-- optional external landing page -->
     <xsl:param name="landing_page_url" select="''"/>
 
@@ -130,6 +133,27 @@
                         </a>
                     </p>
                 </xsl:if>
+                <xsl:if test="$data_access_alt_url">
+                    <xsl:choose>
+                        <xsl:when test="$data_access_alt_html = ''">Missing alternative instructions to access data or services !</xsl:when>
+                        <xsl:otherwise>
+                            <!-- load fragment -->
+                            <xsl:variable name="fragment" select="document($data_access_alt_html)"/>
+
+                            <xsl:choose>
+                                <xsl:when test="count($fragment/xhtml:html/xhtml:body/child::node()) = 0">Missing alternative instructions to access data or services !</xsl:when>
+                                <xsl:otherwise>
+                                    <xsl:copy-of select="$fragment/xhtml:html/xhtml:body/child::node()"/>
+                                </xsl:otherwise>
+                            </xsl:choose>
+                        </xsl:otherwise>
+                    </xsl:choose>
+                    <p>
+                        <a href="{$data_access_alt_url}" target="_blank">
+                            <xsl:value-of select="$data_access_alt_url"/>
+                        </a>
+                    </p>
+                </xsl:if>
             </div>
         </div>
     </xsl:template>
diff --git a/src/main/java/fr/osug/doi/GeneratePipeline.java b/src/main/java/fr/osug/doi/GeneratePipeline.java
index 8695b2e..66c1ab6 100644
--- a/src/main/java/fr/osug/doi/GeneratePipeline.java
+++ b/src/main/java/fr/osug/doi/GeneratePipeline.java
@@ -50,7 +50,9 @@ public final class GeneratePipeline extends AbstractPipeline<PipelineCommonData>
 
     // xslt parameters (doi2landing):
     private final static String XSLT_PARAM_DATA_ACCESS_HTML = "data_access_html";
+    private final static String XSLT_PARAM_DATA_ACCESS_ALT_HTML = "data_access_alt_html";
     private final static String XSLT_PARAM_DATA_ACCESS_URL = "data_access_url";
+    private final static String XSLT_PARAM_DATA_ACCESS_ALT_URL = "data_access_alt_url";
     private final static String XSLT_PARAM_EMBEDDED = "embedded";
     private final static String XSLT_PARAM_EMBEDDED_FULL = "full";
     private final static String XSLT_PARAM_EMBEDDED_HEADER = "header";
@@ -147,11 +149,17 @@ public final class GeneratePipeline extends AbstractPipeline<PipelineCommonData>
         final File dataAccessFileLocation = new File(projectConfig.getProjectConf(), ProjectConfig.CONFIG_ACCESS_INSTRUCTIONS);
         final File dataAccessFile = FileUtils.getFile(dataAccessFileLocation);
 
+        final File dataAccessAltFileLocation = new File(projectConfig.getProjectConf(), ProjectConfig.CONFIG_ACCESS_INSTRUCTIONS_ALT);
+        final File dataAccessAltFile = FileUtils.getFile(dataAccessAltFileLocation);
+
         final Map<String, Object> xslParameters = new HashMap<String, Object>(8);
         xslParameters.put(XSLT_PARAM_DATE, pipeData.now.toString());
         if (dataAccessFile != null) {
             xslParameters.put(XSLT_PARAM_DATA_ACCESS_HTML, dataAccessFile.toURI().toString());
         }
+        if (dataAccessAltFile != null) {
+            xslParameters.put(XSLT_PARAM_DATA_ACCESS_ALT_HTML, dataAccessAltFile.toURI().toString());
+        }
 
         final String projectName = projectConfig.getProjectName();
         final Date now = pipeData.now;
@@ -180,6 +188,15 @@ public final class GeneratePipeline extends AbstractPipeline<PipelineCommonData>
                 xslParameters.remove(XSLT_PARAM_DATA_ACCESS_URL);
             }
 
+            // Get (optional) specific alternative data access URL:
+            final String dataAccessAltUrl = d.getDataAccessAltUrl();
+            if (dataAccessAltUrl != null) {
+                logger.debug("dataAccessAltUrl: {}", dataAccessAltUrl);
+                xslParameters.put(XSLT_PARAM_DATA_ACCESS_ALT_URL, dataAccessAltUrl);
+            } else {
+                xslParameters.remove(XSLT_PARAM_DATA_ACCESS_ALT_URL);
+            }
+
             // Get external Landing page URL:
             final String landingPageUrl = d.getLandingExtUrl();
             if (landingPageUrl != null) {
diff --git a/src/main/java/fr/osug/doi/ProcessUrlPipeline.java b/src/main/java/fr/osug/doi/ProcessUrlPipeline.java
index 91c1cc3..15c3466 100644
--- a/src/main/java/fr/osug/doi/ProcessUrlPipeline.java
+++ b/src/main/java/fr/osug/doi/ProcessUrlPipeline.java
@@ -64,6 +64,15 @@ public final class ProcessUrlPipeline extends AbstractPipeline<PipelineCommonDat
         if (defaultDataAccessUrl == null) {
             logger.warn("Missing default data access url");
         }
+        // optional alternative url:
+        final String defaultDataAccessAltUrl = projectConfig.getProperty(ProjectConfig.CONF_KEY_DATA_ACCESS_ALT_URL);
+        if (defaultDataAccessAltUrl != null) {
+            final File dataAccessAltFileLocation = new File(projectConfig.getProjectConf(), ProjectConfig.CONFIG_ACCESS_INSTRUCTIONS_ALT);
+            final File dataAccessAltFile = FileUtils.getFile(dataAccessAltFileLocation);
+            if (dataAccessAltFile == null) {
+                logger.warn("Missing dataAccessAltFile: {}", dataAccessAltFileLocation);
+            }
+        }
 
         final List<? extends DoiCommon> dList = dbr.findByProject(projectName);
 
@@ -92,6 +101,17 @@ public final class ProcessUrlPipeline extends AbstractPipeline<PipelineCommonDat
                 doiData.addError("Missing data access url");
             } else {
                 checkUrl(doiData, dataAccessUrl, true);
+                logger.debug("dataAccessUrl {}", dataAccessUrl);
+            }
+
+            // Get (optional) specific alternative data access URL:
+            String dataAccessAltUrl = projectConfig.getUrlDataAccessAlt(doiSuffix);
+            if (dataAccessAltUrl == null) {
+                dataAccessAltUrl = defaultDataAccessAltUrl;
+            }
+            if (dataAccessAltUrl != null) {
+                checkUrl(doiData, dataAccessAltUrl, true);
+                logger.debug("dataAccessAltUrl {}", dataAccessAltUrl);
             }
 
             // Get external Landing page URL:
@@ -101,7 +121,7 @@ public final class ProcessUrlPipeline extends AbstractPipeline<PipelineCommonDat
                 logger.debug("landingPageUrl: {}", landingPageExtUrl);
             }
 
-            changed |= doiService.updateStagingDoiUrls(doiSuffix, dataAccessUrl, landingPageExtUrl,
+            changed |= doiService.updateStagingDoiUrls(doiSuffix, dataAccessUrl, dataAccessAltUrl, landingPageExtUrl,
                     doiData.isValid(), doiData.messagesToString(), now);
         }
 
@@ -116,11 +136,16 @@ public final class ProcessUrlPipeline extends AbstractPipeline<PipelineCommonDat
     private void checkUrl(final PipelineCommonDoiData doiData, final String url, final boolean binary) {
         logger.debug("checkUrl: {}", url);
 
-        Integer check = checkUrlCache.get(url);
-        if (check == null) {
-            final String doc = doiConfig.getUrlChecker().getResource(url, binary);
-            check = checkDocument(doc);
-            checkUrlCache.put(url, check);
+        int check = DOC_EMPTY;
+        if (url != null) {
+            final Integer cached = checkUrlCache.get(url);
+            if (cached != null) {
+                check = cached.intValue();
+            } else {
+                final String doc = doiConfig.getUrlChecker().getResource(url, binary);
+                check = checkDocument(doc);
+                checkUrlCache.put(url, check);
+            }
         }
         logger.debug("check: {}", check);
         switch (check) {
diff --git a/src/main/java/fr/osug/doi/ProjectConfig.java b/src/main/java/fr/osug/doi/ProjectConfig.java
index 31389f0..bbac3e8 100644
--- a/src/main/java/fr/osug/doi/ProjectConfig.java
+++ b/src/main/java/fr/osug/doi/ProjectConfig.java
@@ -29,7 +29,10 @@ public final class ProjectConfig {
     public final static String CONF_PROJECT = "project.properties";
 
     public final static String CONFIG_ACCESS_INSTRUCTIONS = "access_instruction.html";
+    public final static String CONFIG_ACCESS_INSTRUCTIONS_ALT = "access_instruction_alt.html";
+    
     public final static String CONF_URL_MAP_DATA_ACCESS = "doi_url_data_access.csv";
+    public final static String CONF_URL_MAP_DATA_ACCESS_ALT = "doi_url_data_access_alt.csv";
     public final static String CONF_URL_MAP_LANDING_PAGE = "doi_url_landing_page.csv";
 
     private final static String CONF_OVERRIDE_NAMES_CSV = "names_override" + Const.FILE_EXT_CSV;
@@ -42,6 +45,8 @@ public final class ProjectConfig {
 
     /** general data access url = "dataAccessUrl" */
     public final static String CONF_KEY_DATA_ACCESS_URL = "dataAccessUrl";
+    /** general data access alternative url = "dataAccessAltUrl" */
+    public final static String CONF_KEY_DATA_ACCESS_ALT_URL = "dataAccessAltUrl";
     /** generate embedded fragments = "generate_embedded" */
     public final static String CONF_KEY_GENERATE_EMBEDDED = "generate_embedded";
 
@@ -52,6 +57,7 @@ public final class ProjectConfig {
     // lazy:
     private Properties properties = null;
     private Map<String, String> urlMapDataAccess = null;
+    private Map<String, String> urlMapDataAccessAlt = null;
     private Map<String, String> urlMapLandingPages = null;
     // Directories:
     private File metadataDir = null;
@@ -236,6 +242,18 @@ public final class ProjectConfig {
         return getUrlMapDataAccess().get(doiSuffix);
     }
 
+    /* DataAccess Alternative Url Mapping */
+    private Map<String, String> getUrlMapDataAccessAlt() throws IOException {
+        if (urlMapDataAccessAlt == null) {
+            urlMapDataAccessAlt = CsvUtil.loadUrlMapping(new File(this.projectConf, CONF_URL_MAP_DATA_ACCESS_ALT));
+        }
+        return urlMapDataAccessAlt;
+    }
+
+    public String getUrlDataAccessAlt(final String doiSuffix) throws IOException {
+        return getUrlMapDataAccessAlt().get(doiSuffix);
+    }
+
     /* LandingPage Url Mapping */
     private Map<String, String> getUrlMapLandingPage() throws IOException {
         if (urlMapLandingPages == null) {
diff --git a/src/main/java/fr/osug/doi/domain/DoiCommon.java b/src/main/java/fr/osug/doi/domain/DoiCommon.java
index 4ed2beb..d2d922a 100644
--- a/src/main/java/fr/osug/doi/domain/DoiCommon.java
+++ b/src/main/java/fr/osug/doi/domain/DoiCommon.java
@@ -52,6 +52,9 @@ public abstract class DoiCommon implements Persistable<Long> {
     /* url fields */
     @Column(name = "data_access_url")
     private String dataAccessUrl;
+    
+    @Column(name = "data_access_alt_url")
+    private String dataAccessAltUrl;
 
     @Column(name = "landing_ext_url")
     private String landingExtUrl;
@@ -116,6 +119,14 @@ public abstract class DoiCommon implements Persistable<Long> {
         this.dataAccessUrl = dataAccessUrl;
     }
 
+    public String getDataAccessAltUrl() {
+        return dataAccessAltUrl;
+    }
+
+    public void setDataAccessAltUrl(String dataAccessAltUrl) {
+        this.dataAccessAltUrl = dataAccessAltUrl;
+    }
+
     public String getLandingExtUrl() {
         return landingExtUrl;
     }
@@ -181,6 +192,7 @@ public abstract class DoiCommon implements Persistable<Long> {
                 + ", updateDate='" + updateDate + "'"
                 + ", metadataMd5='" + metadataMd5 + "'"
                 + ", dataAccessUrl='" + dataAccessUrl + "'"
+                + ", dataAccessAltUrl='" + dataAccessAltUrl + "'"
                 + ", landingExtUrl='" + landingExtUrl + "'"
                 + ", landingLocUrl='" + landingLocUrl + "'"
                 + ", dataciteMetadataMd5='" + dataciteMetadataMd5 + "'"
diff --git a/src/main/java/fr/osug/doi/service/DoiService.java b/src/main/java/fr/osug/doi/service/DoiService.java
index 14c17f2..67474a1 100644
--- a/src/main/java/fr/osug/doi/service/DoiService.java
+++ b/src/main/java/fr/osug/doi/service/DoiService.java
@@ -193,7 +193,8 @@ public class DoiService {
 
     @Transactional
     public boolean updateStagingDoiUrls(final String identifier,
-                                        final String dataAccessUrl, final String landingExtUrl,
+                                        final String dataAccessUrl, final String dataAccessAltUrl, 
+                                        final String landingExtUrl,
                                         final boolean urlValid, final String urlLog,
                                         final Date now) {
 
@@ -211,6 +212,12 @@ public class DoiService {
             doiStaging.setDataAccessUrl(dataAccessUrl);
             changed = true;
         }
+        final String oldDataAccessAltUrl = doiStaging.getDataAccessAltUrl();
+        if ((dataAccessAltUrl != null && !dataAccessAltUrl.equals(oldDataAccessAltUrl))
+                || (oldDataAccessAltUrl != null && !oldDataAccessUrl.equals(dataAccessUrl))) {
+            doiStaging.setDataAccessAltUrl(dataAccessAltUrl);
+            changed = true;
+        }
         final String oldLandingExtUrl = doiStaging.getLandingExtUrl();
         if ((landingExtUrl != null && !landingExtUrl.equals(oldLandingExtUrl))
                 || (oldLandingExtUrl != null && !oldLandingExtUrl.equals(landingExtUrl))) {
@@ -334,6 +341,7 @@ public class DoiService {
         doiPublic.setUpdateDate(now);
         doiPublic.setMetadataMd5(metadataMd5);
         doiPublic.setDataAccessUrl(doiStaging.getDataAccessUrl());
+        doiPublic.setDataAccessAltUrl(doiStaging.getDataAccessAltUrl());
         doiPublic.setLandingExtUrl(doiStaging.getLandingExtUrl());
 
         // TODO: handle external url checks
diff --git a/src/main/resources/db/migration/V1_1__altDataAccessUrl.sql b/src/main/resources/db/migration/V1_1__altDataAccessUrl.sql
new file mode 100644
index 0000000..1f0429b
--- /dev/null
+++ b/src/main/resources/db/migration/V1_1__altDataAccessUrl.sql
@@ -0,0 +1,10 @@
+
+/* DOI STAGING */
+alter table doi_staging
+    add column data_access_alt_url varchar(1024);
+
+/* DOI PUBLIC */
+alter table doi_public
+    add column data_access_alt_url varchar(1024);
+
+/* EOF */
\ No newline at end of file
-- 
GitLab