You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by dk...@apache.org on 2018/12/22 03:23:09 UTC

[sling-org-apache-sling-app-cms] 01/01: adding initial commit of metadata loading

This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch feature/file-metadata-loaded
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-app-cms.git

commit 38ff875db23f0cd7b4148f5a77098257901b9f49
Author: Dan Klco <dk...@apache.org>
AuthorDate: Fri Dec 21 22:22:34 2018 -0500

    adding initial commit of metadata loading
---
 api/src/main/java/org/apache/sling/cms/File.java   |   7 +
 core/pom.xml                                       |   6 +
 .../internal/listeners/FileMetadataExtractor.java  | 149 +++++++++++++++++++++
 .../sling/cms/core/internal/models/FileImpl.java   |  26 ++++
 .../resources/SLING-INF/nodetypes/nodetypes.cnd    |   1 +
 ui/src/main/resources/jcr_root/conf/global.json    |   6 +
 .../components/caconfig/fileeditor/fileeditor.jsp  |   2 +-
 .../components/cms/references/references.jsp       |   6 +-
 .../components/editor/fields/filemetadata.json     |   6 +
 .../editor/fields/filemetadata/edit.json           |   5 +
 .../fields/filemetadata/filemetadata.jsp}          |  28 ++--
 .../editor/fields/references/references.jsp        |   6 +-
 12 files changed, 230 insertions(+), 18 deletions(-)

diff --git a/api/src/main/java/org/apache/sling/cms/File.java b/api/src/main/java/org/apache/sling/cms/File.java
index d08c331..f207909 100644
--- a/api/src/main/java/org/apache/sling/cms/File.java
+++ b/api/src/main/java/org/apache/sling/cms/File.java
@@ -88,6 +88,13 @@ public interface File {
     String getLastModifiedBy();
 
     /**
+     * Retrieves the metadata extracted from the file.
+     * 
+     * @return the metadata extracted from the file
+     */
+    ValueMap getMetadata();
+
+    /**
      * Gets the name of the Sling Resource backing the file
      * 
      * @return the name of the file resource
diff --git a/core/pom.xml b/core/pom.xml
index 955c181..f960cdf 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -227,5 +227,11 @@
             <groupId>org.apache.httpcomponents</groupId>
             <artifactId>httpclient</artifactId>
         </dependency>
+        <dependency>
+            <artifactId>tika-core</artifactId>
+            <version>1.19.1</version>
+            <groupId>org.apache.tika</groupId>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
 </project>
\ No newline at end of file
diff --git a/core/src/main/java/org/apache/sling/cms/core/internal/listeners/FileMetadataExtractor.java b/core/src/main/java/org/apache/sling/cms/core/internal/listeners/FileMetadataExtractor.java
new file mode 100644
index 0000000..a2e80a7
--- /dev/null
+++ b/core/src/main/java/org/apache/sling/cms/core/internal/listeners/FileMetadataExtractor.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.cms.core.internal.listeners;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.util.Text;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.ModifiableValueMap;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.api.resource.observation.ExternalResourceChangeListener;
+import org.apache.sling.api.resource.observation.ResourceChange;
+import org.apache.sling.api.resource.observation.ResourceChangeListener;
+import org.apache.sling.cms.File;
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.metadata.Property;
+import org.apache.tika.parser.AutoDetectParser;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.parser.Parser;
+import org.apache.tika.sax.BodyContentHandler;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.SAXException;
+
+/**
+ * A Resource Change Listener which extracts the metadata from sling:Files when
+ * they are uploaded.
+ */
+@Component(service = { FileMetadataExtractor.class, ResourceChangeListener.class,
+        ExternalResourceChangeListener.class }, property = { ResourceChangeListener.CHANGES + "=ADDED",
+                ResourceChangeListener.PATHS + "=/content",
+                ResourceChangeListener.PATHS + "=/static" }, immediate = true)
+public class FileMetadataExtractor implements ResourceChangeListener, ExternalResourceChangeListener {
+
+    public static final String NN_METADATA = "metadata";
+    public static final String PN_X_PARSED_BY = "X-Parsed-By";
+
+    @Reference
+    private ResourceResolverFactory factory;
+
+    private static final Logger log = LoggerFactory.getLogger(FileMetadataExtractor.class);
+
+    public void extractMetadata(File file) {
+        extractMetadata(file.getResource());
+    }
+
+    public void extractMetadata(Resource resource) {
+        try {
+            log.info("Extracting metadata from {}", resource.getPath());
+            ResourceResolver resolver = resource.getResourceResolver();
+            InputStream is = resource.adaptTo(InputStream.class);
+            Resource content = resource.getChild(JcrConstants.JCR_CONTENT);
+            if (content == null) {
+                log.warn("Content resource is null");
+                return;
+            }
+            Map<String, Object> properties = new HashMap<>();
+            Resource metadata = content.getChild(NN_METADATA);
+            if (metadata != null) {
+                properties = metadata.adaptTo(ModifiableValueMap.class);
+            } else {
+                properties.put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED);
+            }
+            Parser parser = new AutoDetectParser();
+            BodyContentHandler handler = new BodyContentHandler();
+            Metadata md = new Metadata();
+            ParseContext context = new ParseContext();
+            parser.parse(is, handler, md, context);
+            for (String name : md.names()) {
+                updateProperty(properties, name, md);
+            }
+            if (metadata == null) {
+                resolver.create(content, NN_METADATA, properties);
+            }
+            resolver.commit();
+            log.info("Metadata extracted from {}", resource.getPath());
+        } catch (Throwable e) { // Sonar, I don't care if the Pope raises the exception, don't throw it
+            log.warn("Exception extracting metadata from: " + resource.getPath(), e);
+        }
+    }
+
+    private void updateProperty(Map<String, Object> properties, String name, Metadata metadata) {
+        log.trace("Updating property: {}", name);
+        String filtered = Text.escapeIllegalJcrChars(name);
+        Property property = Property.get(name);
+        if (property != null) {
+            if (metadata.isMultiValued(property)) {
+                properties.put(filtered, metadata.getValues(property));
+            } else if (metadata.getDate(property) != null) {
+                Calendar cal = Calendar.getInstance();
+                cal.setTime(metadata.getDate(property));
+                properties.put(filtered, cal);
+            } else if (metadata.getInt(property) != null) {
+                properties.put(filtered, metadata.getInt(property));
+            } else {
+                properties.put(filtered, metadata.get(property));
+            }
+        } else {
+            properties.put(filtered, metadata.get(name));
+        }
+    }
+
+    @Override
+    public void onChange(List<ResourceChange> changes) {
+        Map<String, Object> serviceParams = new HashMap<>();
+        serviceParams.put(ResourceResolverFactory.SUBSERVICE, "sling-cms-metadata");
+        ResourceResolver serviceResolver = null;
+        try {
+            serviceResolver = factory.getServiceResourceResolver(serviceParams);
+            for (ResourceChange rc : changes) {
+                Resource changed = serviceResolver.getResource(rc.getPath());
+                extractMetadata(changed);
+            }
+        } catch (LoginException e) {
+            log.error("Exception getting service user", e);
+        } finally {
+            if (serviceResolver != null) {
+                serviceResolver.close();
+            }
+        }
+
+    }
+
+}
diff --git a/core/src/main/java/org/apache/sling/cms/core/internal/models/FileImpl.java b/core/src/main/java/org/apache/sling/cms/core/internal/models/FileImpl.java
index 2e2e21f..78e8a48 100644
--- a/core/src/main/java/org/apache/sling/cms/core/internal/models/FileImpl.java
+++ b/core/src/main/java/org/apache/sling/cms/core/internal/models/FileImpl.java
@@ -17,18 +17,25 @@
 package org.apache.sling.cms.core.internal.models;
 
 import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Map;
 
 import javax.inject.Inject;
 import javax.inject.Named;
 
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.util.Text;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
 import org.apache.sling.cms.File;
 import org.apache.sling.cms.Site;
 import org.apache.sling.cms.SiteManager;
+import org.apache.sling.cms.core.internal.listeners.FileMetadataExtractor;
 import org.apache.sling.models.annotations.Default;
 import org.apache.sling.models.annotations.Model;
 import org.apache.sling.models.annotations.Optional;
+import org.apache.sling.models.annotations.injectorspecific.OSGiService;
 
 /**
  * A model representing a file
@@ -55,6 +62,9 @@ public class FileImpl implements File {
     @Named("jcr:content/jcr:createdBy")
     private String createdBy;
 
+    @OSGiService
+    private FileMetadataExtractor extractor;
+
     @Inject
     @Optional
     @Named("jcr:content/jcr:lastModified")
@@ -111,6 +121,22 @@ public class FileImpl implements File {
     }
 
     @Override
+    public ValueMap getMetadata() {
+        Resource metadata = this.getContentResource().getChild(FileMetadataExtractor.NN_METADATA);
+        if (metadata == null || !metadata.getValueMap().containsKey(FileMetadataExtractor.PN_X_PARSED_BY)) {
+            extractor.extractMetadata(this);
+            metadata = this.getContentResource().getChild(FileMetadataExtractor.NN_METADATA);
+        }
+        Map<String, Object> data = new HashMap<>();
+        if (metadata != null) {
+            metadata.getValueMap().entrySet()
+                    .forEach(e -> data.put(Text.unescapeIllegalJcrChars(e.getKey()), e.getValue()));
+        }
+        data.remove(JcrConstants.JCR_PRIMARYTYPE);
+        return new ValueMapDecorator(data);
+    }
+
+    @Override
     public String getName() {
         return resource.getName();
     }
diff --git a/ui/src/main/resources/SLING-INF/nodetypes/nodetypes.cnd b/ui/src/main/resources/SLING-INF/nodetypes/nodetypes.cnd
index 2b33cde..0f030c5 100644
--- a/ui/src/main/resources/SLING-INF/nodetypes/nodetypes.cnd
+++ b/ui/src/main/resources/SLING-INF/nodetypes/nodetypes.cnd
@@ -47,6 +47,7 @@
 [sling:FileContent] > nt:resource
     - * (undefined) copy
     - * (undefined) copy multiple
+    + metadata (nt:unstructured) = nt:unstructured copy primary
 
 [sling:Page] > nt:hierarchyNode
     orderable
diff --git a/ui/src/main/resources/jcr_root/conf/global.json b/ui/src/main/resources/jcr_root/conf/global.json
index 3dd673b..98ed0d0 100644
--- a/ui/src/main/resources/jcr_root/conf/global.json
+++ b/ui/src/main/resources/jcr_root/conf/global.json
@@ -70,6 +70,12 @@
                         "name": "jcr:content/published@TypeHint",
                         "value": "Boolean",
                         "sling:resourceType": "sling-cms/components/editor/fields/hidden"
+                    },
+                    "filemetadata": {
+                        "jcr:primaryType": "nt:unstructured",
+                        "name": "jcr:content/filemetadata",
+                        "label": "File Metadata",
+                        "sling:resourceType": "sling-cms/components/editor/fields/filemetadata"
                     }
                 }
             }
diff --git a/ui/src/main/resources/jcr_root/libs/sling-cms/components/caconfig/fileeditor/fileeditor.jsp b/ui/src/main/resources/jcr_root/libs/sling-cms/components/caconfig/fileeditor/fileeditor.jsp
index 4b2b340..9fe38da 100644
--- a/ui/src/main/resources/jcr_root/libs/sling-cms/components/caconfig/fileeditor/fileeditor.jsp
+++ b/ui/src/main/resources/jcr_root/libs/sling-cms/components/caconfig/fileeditor/fileeditor.jsp
@@ -22,7 +22,7 @@
 
 <c:set var="oldAvailableTypes" value="${availableTypes}" />
 <c:set var="availableTypes" value="SlingCMS-FileEditor" scope="request" />
-<sling:include path="${slingRequest.requestPathInfo.suffix}" resourceType="sling-cms/components/cms/fileeditorconfig/config" />
+<sling:include path="${slingRequest.requestPathInfo.suffix}" resourceType="sling-cms/components/caconfig/fileeditor/config" />
 <c:set var="availableTypes" value="${oldAvailableTypes}" scope="request" />
 
 <sling:call script="/libs/sling-cms/components/editor/scripts/finalize.jsp" />
diff --git a/ui/src/main/resources/jcr_root/libs/sling-cms/components/cms/references/references.jsp b/ui/src/main/resources/jcr_root/libs/sling-cms/components/cms/references/references.jsp
index 80cf2c1..c70211d 100644
--- a/ui/src/main/resources/jcr_root/libs/sling-cms/components/cms/references/references.jsp
+++ b/ui/src/main/resources/jcr_root/libs/sling-cms/components/cms/references/references.jsp
@@ -32,12 +32,12 @@
                 <c:choose>
                     <c:when test="${ref.page}">
                         <td>
-                            <span class="icon">
+                            <span class="icon" title="Page">
                                 <span class="jam jam-document"></span>
                             </span>
                         </td>
                         <td title="${ref.containingPage.path}">
-                            <a href="/cms/site/content.html${ref.containingPage.parent.path}">
+                            <a href="/cms/site/content.html${ref.containingPage.parent.path}" target="_blank">
                                 ${sling:encode(ref.containingPage.title,'HTML')}
                             </a>
                         </td>
@@ -50,7 +50,7 @@
                     </c:when>
                     <c:otherwise>
                         <td>
-                            <span class="icon">
+                            <span class="icon" title="Other">
                                 <span class="jam jam-file"></span>
                             </span>
                         </td>
diff --git a/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/filemetadata.json b/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/filemetadata.json
new file mode 100644
index 0000000..709dc6e
--- /dev/null
+++ b/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/filemetadata.json
@@ -0,0 +1,6 @@
+{
+	"jcr:primaryType": "sling:Component",
+	"sling:resourceSuperType": "sling-cms/components/editor/fields/base",
+	"componentType": "SlingCMS-FieldConfig",
+	"jcr:title": "Sling CMS - File Metadata"
+}
\ No newline at end of file
diff --git a/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/filemetadata/edit.json b/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/filemetadata/edit.json
new file mode 100644
index 0000000..ae6c823
--- /dev/null
+++ b/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/filemetadata/edit.json
@@ -0,0 +1,5 @@
+{
+    "jcr:primaryType": "nt:unstructured",
+    "sling:resourceType": "sling-cms/components/editor/slingform",
+    "button": "No Need to Edit"
+}
\ No newline at end of file
diff --git a/ui/src/main/resources/jcr_root/libs/sling-cms/components/caconfig/fileeditor/fileeditor.jsp b/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/filemetadata/filemetadata.jsp
similarity index 54%
copy from ui/src/main/resources/jcr_root/libs/sling-cms/components/caconfig/fileeditor/fileeditor.jsp
copy to ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/filemetadata/filemetadata.jsp
index 4b2b340..203f723 100644
--- a/ui/src/main/resources/jcr_root/libs/sling-cms/components/caconfig/fileeditor/fileeditor.jsp
+++ b/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/filemetadata/filemetadata.jsp
@@ -16,14 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */ --%>
- <%@include file="/libs/sling-cms/global.jsp"%>
-<c:set var="cmsEditEnabled" value="true" scope="request" />
-<sling:call script="/libs/sling-cms/components/editor/scripts/init.jsp" />
-
-<c:set var="oldAvailableTypes" value="${availableTypes}" />
-<c:set var="availableTypes" value="SlingCMS-FileEditor" scope="request" />
-<sling:include path="${slingRequest.requestPathInfo.suffix}" resourceType="sling-cms/components/cms/fileeditorconfig/config" />
-<c:set var="availableTypes" value="${oldAvailableTypes}" scope="request" />
-
-<sling:call script="/libs/sling-cms/components/editor/scripts/finalize.jsp" />
-<c:set var="cmsEditEnabled" value="false" scope="request" />
\ No newline at end of file
+<%@include file="/libs/sling-cms/global.jsp"%>
+<c:set var="file" value="${sling:adaptTo(slingRequest.requestPathInfo.suffixResource,'org.apache.sling.cms.File')}" />
+<div class="field">
+    <c:if test="${not empty properties.label}">
+        <label class="label">
+            <sling:encode value="${properties.label}" mode="HTML" />
+        </label>
+    </c:if>
+    <div class="control">
+        <dl class="reference-list">
+        <c:forEach var="element" items="${file.metadata}">
+            <dt><sling:encode value="${element.key}" mode="HTML" /></dt>
+            <dd><sling:encode value="${element.value}" mode="HTML" /></dd>
+        </c:forEach>
+        </dl>
+    </div>
+</div>
diff --git a/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/references/references.jsp b/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/references/references.jsp
index ec6709d..2a96f85 100644
--- a/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/references/references.jsp
+++ b/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/references/references.jsp
@@ -46,12 +46,12 @@
                             <c:choose>
                                 <c:when test="${ref.page}">
                                     <td>
-                                        <span class="icon">
+                                        <span class="icon" title="Page">
                                             <span class="jam jam-document"></span>
                                         </span>
                                     </td>
                                     <td title="${ref.containingPage.path}">
-                                        <a href="/cms/site/content.html${ref.containingPage.parent.path}">
+                                        <a href="/cms/site/content.html${ref.containingPage.parent.path}" target="_blank">
                                             ${sling:encode(ref.containingPage.title,'HTML')}
                                         </a>
                                     </td>
@@ -64,7 +64,7 @@
                                 </c:when>
                                 <c:otherwise>
                                     <td>
-                                        <span class="icon">
+                                        <span class="icon"  title="Other">
                                             <span class="jam jam-file"></span>
                                         </span>
                                     </td>