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 2019/01/31 18:06:43 UTC
[sling-org-apache-sling-app-cms] branch master updated: Adding a
service to auto-version pages
This is an automated email from the ASF dual-hosted git repository.
dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-app-cms.git
The following commit(s) were added to refs/heads/master by this push:
new fbf700c Adding a service to auto-version pages
new dfd3258 Merge branch 'master' of github.com:apache/sling-org-apache-sling-app-cms
fbf700c is described below
commit fbf700ce9dff15c7ec522253dc1605372f7accdf
Author: Dan Klco <dk...@apache.org>
AuthorDate: Thu Jan 31 13:06:24 2019 -0500
Adding a service to auto-version pages
---
.../main/java/org/apache/sling/cms/CMSUtils.java | 16 +-
builder/src/main/provisioning/cms.txt | 7 +-
.../internal/listeners/AutoVersioningListener.java | 179 +++++++++++++++++++++
.../listeners/AutoVersioningListenerConfig.java | 34 ++++
.../apache/sling/cms/core/models/VersionInfo.java | 22 ++-
.../main/resources/OSGI-INF/l10n/bundle.properties | 15 ++
...eUserMapperImpl.amended-sling-cms-metadata.json | 6 -
...serMapperImpl.amended-sling-cms-versionmgr.json | 6 +
8 files changed, 261 insertions(+), 24 deletions(-)
diff --git a/api/src/main/java/org/apache/sling/cms/CMSUtils.java b/api/src/main/java/org/apache/sling/cms/CMSUtils.java
index 639467c..5a3f18b 100644
--- a/api/src/main/java/org/apache/sling/cms/CMSUtils.java
+++ b/api/src/main/java/org/apache/sling/cms/CMSUtils.java
@@ -41,7 +41,7 @@ public class CMSUtils {
*
* @param resources the collection of resources to adapt
* @param type the type to which to adapt the resources
- * @param <T> the type to which the resources are adapted
+ * @param <T> the type to which the resources are adapted
* @return the list of adapted classes
*/
@NotNull
@@ -64,7 +64,7 @@ public class CMSUtils {
*
* @param resources the array of resources to adapt
* @param type the type to which to adapt the resources
- * @param <T> the type to which the resources are adapted
+ * @param <T> the type to which the resources are adapted
* @return the list of adapted classes
*/
@NotNull
@@ -100,11 +100,13 @@ public class CMSUtils {
*/
@Nullable
public static final Resource findPublishableParent(Resource resource) {
- String type = resource.getValueMap().get(JcrConstants.JCR_PRIMARYTYPE, String.class);
- if (ArrayUtils.contains(CMSConstants.PUBLISHABLE_TYPES, type)) {
- return resource;
- } else if (resource.getParent() != null) {
- return findPublishableParent(resource.getParent());
+ if (resource != null) {
+ String type = resource.getValueMap().get(JcrConstants.JCR_PRIMARYTYPE, String.class);
+ if (ArrayUtils.contains(CMSConstants.PUBLISHABLE_TYPES, type)) {
+ return resource;
+ } else if (resource.getParent() != null) {
+ return findPublishableParent(resource.getParent());
+ }
}
return null;
}
diff --git a/builder/src/main/provisioning/cms.txt b/builder/src/main/provisioning/cms.txt
index 4a12bfc..4b8f6d1 100644
--- a/builder/src/main/provisioning/cms.txt
+++ b/builder/src/main/provisioning/cms.txt
@@ -92,9 +92,8 @@
set ACL for sling-ugc
allow jcr:write on /etc/usergenerated
end
- create service user sling-cms-metadata
- set ACL for sling-cms-metadata
- allow jcr:all on /content
- allow jcr:all on /static
+ create service user sling-cms-versionmgr
+ set ACL for sling-cms-versionmgr
+ allow jcr:write,jcr:nodeTypeManagement,jcr:versionManagement on /content
end
diff --git a/core/src/main/java/org/apache/sling/cms/core/internal/listeners/AutoVersioningListener.java b/core/src/main/java/org/apache/sling/cms/core/internal/listeners/AutoVersioningListener.java
new file mode 100644
index 0000000..07f8017
--- /dev/null
+++ b/core/src/main/java/org/apache/sling/cms/core/internal/listeners/AutoVersioningListener.java
@@ -0,0 +1,179 @@
+/*
+ * 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.util.Calendar;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.version.Version;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.ModifiableValueMap;
+import org.apache.sling.api.resource.PersistenceException;
+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.CMSConstants;
+import org.apache.sling.cms.CMSUtils;
+import org.apache.sling.cms.Page;
+import org.apache.sling.cms.core.models.VersionInfo;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Modified;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.metatype.annotations.Designate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A Resource Change Listener will automatically version pages when a page is
+ * published / unpublished or an interval passes since the page was last saved.
+ */
+@Component(service = { AutoVersioningListener.class, ResourceChangeListener.class,
+ ExternalResourceChangeListener.class }, property = { ResourceChangeListener.CHANGES + "=ADDED",
+ ResourceChangeListener.CHANGES + "=CHANGED", ResourceChangeListener.CHANGES + "=REMOVED",
+ ResourceChangeListener.PATHS + "=/content" }, immediate = true)
+@Designate(ocd = AutoVersioningListenerConfig.class)
+public class AutoVersioningListener implements ResourceChangeListener, ExternalResourceChangeListener {
+
+ private static final Logger log = LoggerFactory.getLogger(AutoVersioningListener.class);
+ public static final String NN_METADATA = "metadata";
+
+ public static final String PN_X_PARSED_BY = "X-Parsed-By";
+
+ private int cutoff;
+ private boolean enabled;
+
+ @Activate
+ @Modified
+ public void activate(AutoVersioningListenerConfig config) {
+ this.cutoff = config.cutoff();
+ this.enabled = config.enabled();
+ }
+
+ @Reference
+ private ResourceResolverFactory factory;
+
+ @Override
+ public void onChange(List<ResourceChange> changes) {
+ if (enabled) {
+ log.trace("onChange");
+ Map<String, Object> serviceParams = new HashMap<>();
+ serviceParams.put(ResourceResolverFactory.SUBSERVICE, "sling-cms-versionmgr");
+ ResourceResolver serviceResolver = null;
+
+ try {
+ serviceResolver = factory.getServiceResourceResolver(serviceParams);
+ Set<String> pages = new HashSet<>();
+ for (ResourceChange rc : changes) {
+ Resource changed = serviceResolver.getResource(rc.getPath());
+ Resource page = CMSUtils.findPublishableParent(changed);
+ if (changed != null && page != null && CMSConstants.NT_PAGE.equals(page.getResourceType())
+ && !pages.contains(page.getPath())) {
+ log.debug("Evaluating the changes to {}", page.getPath());
+ String user = changed.getValueMap().get(JcrConstants.JCR_LASTMODIFIED + "By", String.class);
+ if (pastLastModified(page)) {
+ log.debug("Page {} needs to be versioned", page.getPath());
+ versionPage(page, user);
+ } else {
+ log.trace("Page {} does not need to be versioned", page.getPath());
+ }
+ pages.add(page.getPath());
+ } else {
+ log.trace("Not versioning {}", page);
+ }
+ }
+ } catch (LoginException e) {
+ log.error("Exception getting service user", e);
+ } finally {
+ if (serviceResolver != null) {
+ serviceResolver.close();
+ }
+ }
+ }
+ }
+
+ private boolean pastLastModified(Resource pageResource) {
+ try {
+ Page page = pageResource.adaptTo(Page.class);
+ if (page != null && page.isPublished()) {
+ Version latestVersion = Optional.ofNullable(pageResource.adaptTo(VersionInfo.class))
+ .map(VersionInfo::getVersions).map(vs -> !vs.isEmpty() ? vs.get(vs.size() - 1) : null)
+ .orElse(null);
+ if (latestVersion != null) {
+ if (latestVersion.hasProperty("jcr:frozenNode/jcr:content/jcr:lastModified")) {
+ Calendar lMod = latestVersion.getProperty("jcr:frozenNode/jcr:content/jcr:lastModified")
+ .getDate();
+ Calendar co = Calendar.getInstance();
+ co.add(Calendar.SECOND, cutoff * -1);
+ if (lMod.before(co)) {
+ log.trace("Page should be versioned");
+ return true;
+ } else {
+ log.trace("Page should not be versioned");
+ }
+ } else {
+ log.trace("No last modified found for version");
+ }
+ } else {
+ return true;
+ }
+ } else {
+ log.trace("Page is not published");
+ }
+ } catch (RepositoryException e) {
+ log.error("Failed to check if modified date outside cutoff", e);
+ }
+ return false;
+ }
+
+ private void versionPage(Resource page, String user) {
+ log.debug("Versioning page {}", page);
+ ModifiableValueMap mvm = null;
+ Resource content = page.getChild(JcrConstants.JCR_CONTENT);
+ if (content != null) {
+ mvm = content.adaptTo(ModifiableValueMap.class);
+ }
+ Node node = page.adaptTo(Node.class);
+
+ if (mvm != null && node != null) {
+ try {
+ mvm.put(JcrConstants.JCR_LASTMODIFIED + "By", user);
+ mvm.put(JcrConstants.JCR_LASTMODIFIED, Calendar.getInstance());
+ node.addMixin(JcrConstants.MIX_VERSIONABLE);
+ page.getResourceResolver().commit();
+ node.getSession().getWorkspace().getVersionManager().checkpoint(node.getPath());
+ } catch (PersistenceException e) {
+ log.warn("Failed to save modification date on page: " + page, e);
+ } catch (RepositoryException e) {
+ log.warn("Failed to version page: " + page, e);
+ }
+ }
+ }
+
+}
diff --git a/core/src/main/java/org/apache/sling/cms/core/internal/listeners/AutoVersioningListenerConfig.java b/core/src/main/java/org/apache/sling/cms/core/internal/listeners/AutoVersioningListenerConfig.java
new file mode 100644
index 0000000..1dbc76d
--- /dev/null
+++ b/core/src/main/java/org/apache/sling/cms/core/internal/listeners/AutoVersioningListenerConfig.java
@@ -0,0 +1,34 @@
+/*
+ * 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 org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+
+/**
+ * Configuration for the auto versioning listener
+ */
+@ObjectClassDefinition(name = "%cms.autoversioning.name", description = "%cms.autoversioning.description", localization = "OSGI-INF/l10n/bundle")
+public @interface AutoVersioningListenerConfig {
+
+ @AttributeDefinition(name = "%cms.autoversioning.enabled.name", description = "%cms.autoversioning.enabled.description")
+ boolean enabled() default true;
+
+ @AttributeDefinition(name = "%cms.autoversioning.cutoff.name", description = "%cms.autoversioning.cutoff.description")
+ int cutoff() default 86400;
+
+}
diff --git a/core/src/main/java/org/apache/sling/cms/core/models/VersionInfo.java b/core/src/main/java/org/apache/sling/cms/core/models/VersionInfo.java
index bc394d4..29c767d 100644
--- a/core/src/main/java/org/apache/sling/cms/core/models/VersionInfo.java
+++ b/core/src/main/java/org/apache/sling/cms/core/models/VersionInfo.java
@@ -30,6 +30,8 @@ import javax.jcr.version.VersionIterator;
import org.apache.jackrabbit.JcrConstants;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Returns all of the versions for a Resource. At the moment only JCR nodes are
@@ -38,22 +40,28 @@ import org.apache.sling.models.annotations.Model;
@Model(adaptables = { Resource.class })
public class VersionInfo {
+ private static final Logger log = LoggerFactory.getLogger(VersionInfo.class);
+
private Resource resource;
public VersionInfo(Resource resource) {
this.resource = resource;
}
- @SuppressWarnings("deprecation")
- public List<Version> getVersions() throws RepositoryException {
+ public List<Version> getVersions() {
List<Version> versions = new ArrayList<>();
final Node node = resource.adaptTo(Node.class);
- if (node != null && node.isNodeType(JcrConstants.MIX_VERSIONABLE)) {
- final VersionHistory history = node.getVersionHistory();
- for (final VersionIterator it = history.getAllVersions(); it.hasNext();) {
- final Version v = it.nextVersion();
- versions.add(v);
+ try {
+ if (node != null && node.isNodeType(JcrConstants.MIX_VERSIONABLE)) {
+ final VersionHistory history = node.getSession().getWorkspace().getVersionManager()
+ .getVersionHistory(node.getPath());
+ for (final VersionIterator it = history.getAllVersions(); it.hasNext();) {
+ final Version v = it.nextVersion();
+ versions.add(v);
+ }
}
+ } catch (RepositoryException e) {
+ log.warn("Exception retrieving version history for: " + resource, e);
}
return versions;
}
diff --git a/core/src/main/resources/OSGI-INF/l10n/bundle.properties b/core/src/main/resources/OSGI-INF/l10n/bundle.properties
index c1d3250..8fd82d1 100644
--- a/core/src/main/resources/OSGI-INF/l10n/bundle.properties
+++ b/core/src/main/resources/OSGI-INF/l10n/bundle.properties
@@ -78,6 +78,21 @@ under the bucket. This will be sliced off the UUID, so if you had a UUID of 123
a bucket of bob and a path depth of 1, this would yield a path like: bob/1/123. \
This can be overridden by the path depth in the UGCBucketConfig.
+#Versioning Listener
+cms.autoversioning.name=Auto-Versioning
+cms.autoversioning.description=A Resource Change listener to automatically version pages \
+which are published, unpublished or updated having not been versioned in a defined time \
+range
+
+cms.autoversioning.enabled.name=Enabled
+cms.autoversioning.enabled.description=Enable or disable the AutoVersioning, if not \
+enabled the autoversioning will not occur
+
+cms.autoversioning.max.name=Cutoff
+cms.autoversioning.cutoff.description=The cutoff for determine if the page should be \
+auto-versioned. If the page has was last versioned before the cutoff, the page will be \
+versioned.
+
# Path Suggestion Servlet
pathsuggestionservlet.name=Apache Sling CMS Path Suggestion Servlet
pathsuggestionservlet.description=A servlet for providing suggested paths based \
diff --git a/ui/src/main/resources/jcr_root/libs/sling-cms/install/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-sling-cms-metadata.json b/ui/src/main/resources/jcr_root/libs/sling-cms/install/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-sling-cms-metadata.json
deleted file mode 100644
index 458a8de..0000000
--- a/ui/src/main/resources/jcr_root/libs/sling-cms/install/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-sling-cms-metadata.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "jcr:primaryType": "sling:OsgiConfig",
- "user.mapping": [
- "org.apache.sling.cms.core:sling-cms-metadata=sling-cms-metadata"
- ]
-}
\ No newline at end of file
diff --git a/ui/src/main/resources/jcr_root/libs/sling-cms/install/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-sling-cms-versionmgr.json b/ui/src/main/resources/jcr_root/libs/sling-cms/install/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-sling-cms-versionmgr.json
new file mode 100644
index 0000000..7256259
--- /dev/null
+++ b/ui/src/main/resources/jcr_root/libs/sling-cms/install/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-sling-cms-versionmgr.json
@@ -0,0 +1,6 @@
+{
+ "jcr:primaryType": "sling:OsgiConfig",
+ "user.mapping": [
+ "org.apache.sling.cms.core:sling-cms-versionmgr=sling-cms-versionmgr"
+ ]
+}
\ No newline at end of file