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:08 UTC

[sling-org-apache-sling-app-cms] branch feature/file-metadata-loaded created (now 38ff875)

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

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


      at 38ff875  adding initial commit of metadata loading

This branch includes the following new commits:

     new 38ff875  adding initial commit of metadata loading

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



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

Posted by dk...@apache.org.
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>