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>