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/03/05 15:47:39 UTC

[sling-whiteboard] branch master updated: Adding the option to update references on delete and move operations

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-whiteboard.git


The following commit(s) were added to refs/heads/master by this push:
     new 9aa3205  Adding the option to update references on delete and move operations
9aa3205 is described below

commit 9aa32050e25fa39058f5ffce2fb6eadf6db52a99
Author: Dan Klco <da...@gmail.com>
AuthorDate: Mon Mar 5 10:47:31 2018 -0500

    Adding the option to update references on delete and move operations
---
 .../sling/cms/core/models/ReferenceOperation.java  |  93 +++++++++++
 .../apache/sling/cms/core/models/References.java   |  47 ++++++
 .../cms/core/operations/BulkReplaceOperation.java  | 172 +++++++++++++++++++++
 .../operations/UpdateReferencesPostOperation.java  |  83 ++++++++++
 cms/ui/src/main/frontend/src/js/scripts.js         |  16 +-
 .../editor/fields/references/references.jsp        |  52 +++++++
 .../movecopy.json => admin/bulkreplace.json}       |  54 ++++---
 .../libs/sling-cms/content/shared/delete.json      |   9 +-
 .../libs/sling-cms/content/shared/movecopy.json    |   7 +
 9 files changed, 509 insertions(+), 24 deletions(-)

diff --git a/cms/core/src/main/java/org/apache/sling/cms/core/models/ReferenceOperation.java b/cms/core/src/main/java/org/apache/sling/cms/core/models/ReferenceOperation.java
new file mode 100644
index 0000000..8d86cd6
--- /dev/null
+++ b/cms/core/src/main/java/org/apache/sling/cms/core/models/ReferenceOperation.java
@@ -0,0 +1,93 @@
+/*
+ * 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.models;
+
+import java.util.Iterator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.jcr.query.Query;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.cms.CMSConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract operation for Reference operations.
+ */
+public abstract class ReferenceOperation {
+
+	private static final Logger log = LoggerFactory.getLogger(ReferenceOperation.class);
+
+	private Pattern regex = null;
+
+	private Resource resource = null;
+
+	public ReferenceOperation(Resource resource) {
+		String path = resource.getPath().replace("/", "\\/");
+		if (CMSConstants.NT_PAGE.equals(resource.getResourceType())) {
+			regex = Pattern.compile(
+					"(^" + path + "($|\\/)|(\\'|\\\")" + path + "(\\.html|\\'|\\\"|\\/))");
+		} else {
+			regex = Pattern.compile("(^" + path + "($|\\/)|(\\'|\\\")" + path + "(\\'|\\\"|\\/))");
+		}
+		this.resource = resource;
+	}
+
+	public void init() {
+		log.debug("Finding references to {}", resource.getPath());
+
+		String query = "SELECT * FROM [nt:base] AS s WHERE CONTAINS(s.*, '" + resource.getPath() + "')";
+		Iterator<Resource> resources = resource.getResourceResolver().findResources(query, Query.JCR_SQL2);
+		log.debug("Checking for references with: {}", query);
+		while (resources.hasNext()) {
+			Resource r = resources.next();
+			log.debug("Checking for references in resource {}", r);
+			ValueMap properties = r.getValueMap();
+			for (String k : properties.keySet()) {
+				if (properties.get(k) instanceof String) {
+					Matcher matcher = regex.matcher(properties.get(k, String.class));
+					if (matcher.find()) {
+						log.trace("Found reference in property {}@{}", r.getPath(), k);
+						doProcess(r, k);
+					}
+				} else if (properties.get(k) instanceof String[]) {
+					boolean matches = false;
+					for (String v : properties.get(k, String[].class)) {
+						Matcher matcher = regex.matcher(v);
+						if (matcher.find()) {
+							matches = true;
+							break;
+						}
+					}
+					if (matches) {
+						log.trace("Found reference in property {}@{}", r.getPath(), k);
+						doProcess(r, k);
+					}
+				}
+			}
+		}
+	}
+
+	public abstract void doProcess(Resource resource, String matchingKey);
+
+	public Pattern getRegex() {
+		return regex;
+	}
+}
diff --git a/cms/core/src/main/java/org/apache/sling/cms/core/models/References.java b/cms/core/src/main/java/org/apache/sling/cms/core/models/References.java
new file mode 100644
index 0000000..9be4882
--- /dev/null
+++ b/cms/core/src/main/java/org/apache/sling/cms/core/models/References.java
@@ -0,0 +1,47 @@
+/*
+ * 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.models;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+
+/**
+ * Model for finding the references to a Resource
+ */
+@Model(adaptables = Resource.class)
+public class References extends ReferenceOperation {
+
+	private List<String> references = new ArrayList<String>();
+
+	public References(Resource resource) {
+		super(resource);
+	}
+	
+
+	public List<String> getReferences() {
+		init();
+		return references;
+	}
+
+	@Override
+	public void doProcess(Resource r, String matchingKey) {
+		references.add(r.getPath() + "@" + matchingKey);
+	}
+}
diff --git a/cms/core/src/main/java/org/apache/sling/cms/core/operations/BulkReplaceOperation.java b/cms/core/src/main/java/org/apache/sling/cms/core/operations/BulkReplaceOperation.java
new file mode 100644
index 0000000..e9d12e0
--- /dev/null
+++ b/cms/core/src/main/java/org/apache/sling/cms/core/operations/BulkReplaceOperation.java
@@ -0,0 +1,172 @@
+/*
+ * 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.operations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.ModifiableValueMap;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.servlets.post.Modification;
+import org.apache.sling.servlets.post.PostOperation;
+import org.apache.sling.servlets.post.PostResponse;
+import org.apache.sling.servlets.post.SlingPostProcessor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The <code>BulkReplaceOperation</code> class will update all of the properties
+ * applicable to the supplied parameters under the resource for the operation,
+ * replacing the find string with the replacement value.
+ */
+@Component(immediate = true)
+@Service
+@Property(name = PostOperation.PROP_OPERATION_NAME, value = "bulkreplace")
+public class BulkReplaceOperation implements PostOperation {
+
+	private static final Logger log = LoggerFactory.getLogger(BulkReplaceOperation.class);
+
+	public static final String PN_UPDATE_PROPERTIES = "updateProperties";
+	public static final String MODE_REGEX = "regex";
+	public static final String PN_FIND = "find";
+	public static final String PN_REPLACE = "replace";
+	public static final String PN_MODE = "mode";
+
+	@Override
+	public void run(SlingHttpServletRequest request, PostResponse response, SlingPostProcessor[] processors) {
+
+		try {
+			// calculate the paths
+			String path = request.getResource().getPath();
+			response.setPath(path);
+
+			// perform the bulk replacement
+			Pattern updateProperties = Pattern.compile(request.getParameter(PN_UPDATE_PROPERTIES));
+			log.debug("Updating properties matching: {}", updateProperties.pattern());
+			Pattern rfind = null;
+			String find = request.getParameter(PN_FIND);
+			if (MODE_REGEX.equals(request.getParameter(PN_MODE))) {
+				log.debug("Using regular expressions to search for {}", find);
+				rfind = Pattern.compile(find);
+			} else {
+				log.debug("Searching for {}", find);
+			}
+			String replace = request.getParameter(PN_REPLACE);
+			log.debug("Replacing with {}", replace);
+
+			final List<Modification> changes = new ArrayList<Modification>();
+			updateProperties(request.getResource(), updateProperties, rfind, find, replace, response, changes);
+
+			// invoke processors
+			if (processors != null) {
+				for (SlingPostProcessor processor : processors) {
+					processor.process(request, changes);
+				}
+			}
+
+			// check modifications for remaining postfix and store the base path
+			final Map<String, String> modificationSourcesContainingPostfix = new HashMap<>();
+			final Set<String> allModificationSources = new HashSet<>(changes.size());
+			for (final Modification modification : changes) {
+				final String source = modification.getSource();
+				if (source != null) {
+					allModificationSources.add(source);
+					final int atIndex = source.indexOf('@');
+					if (atIndex > 0) {
+						modificationSourcesContainingPostfix.put(source.substring(0, atIndex), source);
+					}
+				}
+			}
+			request.getResourceResolver().commit();
+
+		} catch (Exception e) {
+
+			log.error("Exception during response processing.", e);
+			response.setError(e);
+
+		}
+	}
+
+	private void updateProperties(Resource resource, Pattern updateProperties, Pattern rfind, String find,
+			String replace, PostResponse response, List<Modification> changes) {
+		ModifiableValueMap properties = resource.adaptTo(ModifiableValueMap.class);
+		boolean updated = false;
+		for (String key : properties.keySet()) {
+
+			if (updateProperties.matcher(key).matches()) {
+				log.trace("Checking property {}@{}", resource.getPath(), key);
+				if (properties.get(key) instanceof String) {
+					String value = properties.get(key, String.class);
+					if (rfind == null && (value.contains(find) || value.equals(find))) {
+						value = value.replace(find, replace);
+						log.trace("Value after replacement: {}", value);
+						properties.put(key, value);
+						updated = true;
+					} else if (rfind != null) {
+						Matcher m = rfind.matcher(value);
+						if (m.find()) {
+							value = rfind.matcher(value).replaceAll(replace);
+							log.trace("Value after replacement: {}", value);
+							properties.put(key, value);
+							updated = true;
+						}
+					}
+				} else if (properties.get(key) instanceof String[]) {
+					log.trace("Found array value");
+					boolean arrUpdated = false;
+					String[] v = properties.get(key, String[].class);
+					for (int i = 0; i < v.length; i++) {
+						String value = v[i];
+						if (rfind == null && (value.contains(find) || value.equals(find))) {
+							v[i] = value.replace(find, replace);
+							arrUpdated = true;
+						} else if (rfind != null) {
+							Matcher m = rfind.matcher(value);
+							if (m.find()) {
+								v[i] = rfind.matcher(value).replaceAll(replace);
+								arrUpdated = true;
+							}
+						}
+					}
+					if (arrUpdated) {
+						log.trace("Value after replacement: {}", Arrays.toString(v));
+						properties.put(key, v);
+						updated = true;
+					}
+				}
+			}
+		}
+		if (updated) {
+			response.onModified(resource.getPath());
+			changes.add(Modification.onModified(resource.getPath()));
+		}
+		for (Resource child : resource.getChildren()) {
+			updateProperties(child, updateProperties, rfind, find, replace, response, changes);
+		}
+	}
+}
diff --git a/cms/core/src/main/java/org/apache/sling/cms/core/operations/UpdateReferencesPostOperation.java b/cms/core/src/main/java/org/apache/sling/cms/core/operations/UpdateReferencesPostOperation.java
new file mode 100644
index 0000000..bd061d5
--- /dev/null
+++ b/cms/core/src/main/java/org/apache/sling/cms/core/operations/UpdateReferencesPostOperation.java
@@ -0,0 +1,83 @@
+/*
+ * 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.operations;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.ModifiableValueMap;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.cms.core.models.ReferenceOperation;
+import org.apache.sling.servlets.post.Modification;
+import org.apache.sling.servlets.post.SlingPostConstants;
+import org.apache.sling.servlets.post.SlingPostProcessor;
+import org.osgi.framework.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The <code>UpdateReferencesPostOperation</code> class will update all of the
+ * references from a resource which is being moved or deleted to another
+ * resource.
+ */
+@Component(immediate = true)
+@Service
+@Property(name = Constants.SERVICE_RANKING, intValue = -1)
+public class UpdateReferencesPostOperation implements SlingPostProcessor {
+
+	public static final String RP_UPDATE_REFERENCES = SlingPostConstants.RP_PREFIX + "updateReferences";
+
+	private static final Logger log = LoggerFactory.getLogger(UpdateReferencesPostOperation.class);
+
+	@Override
+	public void process(SlingHttpServletRequest request, List<Modification> changes) throws Exception {
+		if ((SlingPostConstants.OPERATION_DELETE.equals(request.getParameter(SlingPostConstants.RP_OPERATION))
+				|| SlingPostConstants.OPERATION_MOVE.equals(request.getParameter(SlingPostConstants.RP_OPERATION)))
+				&& "true".equalsIgnoreCase(request.getParameter(RP_UPDATE_REFERENCES))) {
+
+			final String find = request.getResource().getPath();
+			final String destination = request.getParameter(SlingPostConstants.RP_DEST);
+			log.debug("Using destination: {}", destination);
+			ReferenceOperation ro = new ReferenceOperation(request.getResource()) {
+				@Override
+				public void doProcess(Resource resource, String matchingKey) {
+					ModifiableValueMap properties = resource.adaptTo(ModifiableValueMap.class);
+					log.trace("Updating references in property {}@{}", resource.getPath(), matchingKey);
+					if (properties.get(matchingKey) instanceof String) {
+						String value = properties.get(matchingKey, String.class).replace(find, destination);
+						properties.put(matchingKey, value);
+						log.trace("Updated value {}", value);
+					} else if (properties.get(matchingKey) instanceof String[]) {
+						String[] values = properties.get(matchingKey, String[].class);
+						for (int i = 0; i < values.length; i++) {
+							values[i] = values[i].replace(find, destination);
+						}
+						properties.put(matchingKey, values);
+						log.trace("Updated values {}", Arrays.toString(values));
+					}
+					changes.add(Modification.onModified(resource.getPath()));
+				}
+			};
+			ro.init();
+		}
+	}
+}
diff --git a/cms/ui/src/main/frontend/src/js/scripts.js b/cms/ui/src/main/frontend/src/js/scripts.js
index 425ac27..9637b9c 100644
--- a/cms/ui/src/main/frontend/src/js/scripts.js
+++ b/cms/ui/src/main/frontend/src/js/scripts.js
@@ -347,7 +347,6 @@ Sling.CMS = {
 					$span.find('input').val(val);
 					var title = $ctx.find('option[value="'+val+'"]').text();
 					
-					
 					if(title !== ''){
 						$span.find('.taxonomy__title').text(title);
 						Sling.CMS.decorate($span);
@@ -370,6 +369,21 @@ Sling.CMS = {
 			});
 		}
 	}
+	
+	Sling.CMS.ext['toggle-value'] = {
+		decorate: function($ctx) {
+			$ctx.find('.toggle-value').each(function(){
+				var $tog = $(this);
+				$('input[name="'+$tog.data('toggle-source')+'"], select[name="'+$tog.data('toggle-source')+'"]').change(function(){
+					if($(this).val() !== $tog.data('toggle-value')){
+						$tog.addClass('hide');
+					} else {
+						$tog.removeClass('hide');
+					}
+				});
+			})
+		}
+	}
 
 	$(document).ready(function() {
 		Sling.CMS.init();
diff --git a/cms/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/references/references.jsp b/cms/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/references/references.jsp
new file mode 100644
index 0000000..2b9026a
--- /dev/null
+++ b/cms/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/references/references.jsp
@@ -0,0 +1,52 @@
+<%-- /*
+ * 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.
+ */ --%>
+ <%@include file="/libs/sling-cms/global.jsp"%>
+<c:if test="${slingRequest.requestPathInfo.suffix != null}">
+	<sling:getResource path="${slingRequest.requestPathInfo.suffix}" var="editedResource" />
+	<c:set var="editProperties" value="${sling:adaptTo(editedResource,'org.apache.sling.api.resource.ValueMap')}" scope="request"/>
+</c:if>
+<div class="Field-Group ${properties.toggle ? 'hide toggle-value' : ''}" data-toggle-source=":operation" data-toggle-value="move">
+	<c:if test="${not empty properties.label}">
+		<label for="${properties.name}">
+			<sling:encode value="${properties.label}" mode="HTML" />
+		</label>
+	</c:if>
+	
+	<div class="Field-Input">
+		<input type="checkbox" name="${properties.name}" value="true" />
+		
+		<c:if test="${properties.includeDestination}">
+			<div class="Field-Group">
+				<label for=":dest">
+					Replacement Path
+				</label>
+				<div class="Field-Input">
+					<input type="text" name=":dest" />
+				</div>
+			</div>
+		</c:if>
+		
+		<sling:adaptTo var="references" adaptable="${slingRequest.requestPathInfo.suffixResource}" adaptTo="org.apache.sling.cms.core.models.References" />
+		<ul style="max-height: 200px; overflow:auto">
+			<c:forEach var="ref" items="${references.references}">
+				<li>${ref}</li>
+			</c:forEach>
+		</ul>
+	</div>
+</div>
\ No newline at end of file
diff --git a/cms/ui/src/main/resources/jcr_root/libs/sling-cms/content/shared/movecopy.json b/cms/ui/src/main/resources/jcr_root/libs/sling-cms/content/admin/bulkreplace.json
similarity index 51%
copy from cms/ui/src/main/resources/jcr_root/libs/sling-cms/content/shared/movecopy.json
copy to cms/ui/src/main/resources/jcr_root/libs/sling-cms/content/admin/bulkreplace.json
index 7a737a0..83130bd 100644
--- a/cms/ui/src/main/resources/jcr_root/libs/sling-cms/content/shared/movecopy.json
+++ b/cms/ui/src/main/resources/jcr_root/libs/sling-cms/content/admin/bulkreplace.json
@@ -2,7 +2,7 @@
 	"jcr:primaryType": "sling:Page",
 	"jcr:content": {
 		"sling:resourceType": "sling-cms/components/pages/base",
-		"jcr:title": "Move/Copy Content",
+		"jcr:title": "Bulk Replace",
 		"jcr:primaryType": "nt:unstructured",
 		"container": {
 			"jcr:primaryType": "nt:unstructured",
@@ -10,44 +10,54 @@
 			"richtext": {
 				"jcr:primaryType": "nt:unstructured",
 				"sling:resourceType": "sling-cms/components/general/richtext",
-				"text": "<h3>Move/Copy Content</h3>"
+				"text": "<h3>Bulk Replace</h3>"
 			},
 			"slingform": {
 				"jcr:primaryType": "nt:unstructured",
 				"sling:resourceType": "sling-cms/components/editor/slingform",
-				"button": "Move/Copy",
+				"button": "Replace All",
 				"fields": {
 					"jcr:primaryType": "nt:unstructured",
 					"sling:resourceType": "sling-cms/components/general/container",
-					"path": {
+					"operation": {
 						"jcr:primaryType": "nt:unstructured",
-						"sling:resourceType" : "sling-cms/components/editor/fields/suffixlabel",
-						"label": "Existing Path:"
+						"sling:resourceType": "sling-cms/components/editor/fields/hidden",
+						"name": ":operation",
+						"value": "bulkreplace"
 					},
-					"destination": {
+					"updateProperties": {
 						"jcr:primaryType": "nt:unstructured",
-						"sling:resourceType" : "sling-cms/components/editor/fields/text",
-						"label": "Destination:",
-						"name": ":dest"
+						"sling:resourceType": "sling-cms/components/editor/fields/text",
+						"label": "Update Properties",
+						"name": "updateProperties"
 					},
-					"operation": {
+					"mode": {
 						"jcr:primaryType": "nt:unstructured",
 						"sling:resourceType": "sling-cms/components/editor/fields/select",
-						"label": "Operation",
-						"name": ":operation",
+						"label": "Mode",
+						"name": "mode",
 						"options": {
-							"jcr:primaryType": "nt:unstructured",
-							"copy":{
-								"jcr:primaryType": "nt:unstructured",
-								"label": "Copy",
-								"value": "copy"
+							"exact": {
+								"label": "Exact Match",
+								"value": "exact"
 							},
-							"move":{
-								"jcr:primaryType": "nt:unstructured",
-								"label": "Move",
-								"value": "move"
+							"regex": {
+								"label": "Regular Expression",
+								"value": "regex"
 							}
 						}
+					},
+					"find": {
+						"jcr:primaryType": "nt:unstructured",
+						"sling:resourceType": "sling-cms/components/editor/fields/text",
+						"label": "Find",
+						"name": "find"
+					},
+					"replace": {
+						"jcr:primaryType": "nt:unstructured",
+						"sling:resourceType": "sling-cms/components/editor/fields/text",
+						"label": "Replace",
+						"name": "replace"
 					}
 				}
 			}
diff --git a/cms/ui/src/main/resources/jcr_root/libs/sling-cms/content/shared/delete.json b/cms/ui/src/main/resources/jcr_root/libs/sling-cms/content/shared/delete.json
index 3076f30..29fd916 100644
--- a/cms/ui/src/main/resources/jcr_root/libs/sling-cms/content/shared/delete.json
+++ b/cms/ui/src/main/resources/jcr_root/libs/sling-cms/content/shared/delete.json
@@ -24,11 +24,18 @@
 						"sling:resourceType" : "sling-cms/components/editor/fields/suffixlabel",
 						"label": "Do you want to delete:"
 					},
-					"primaryType": {
+					"operation": {
 						"jcr:primaryType": "nt:unstructured",
 						"sling:resourceType": "sling-cms/components/editor/fields/hidden",
 						"name": ":operation",
 						"value": "delete"
+					},
+					"references": {
+						"jcr:primaryType": "nt:unstructured",
+						"sling:resourceType": "sling-cms/components/editor/fields/references",
+						"includeDestination": true,
+						"label": "The following content references this path, update references?",
+						"name": ":updateReferences"
 					}
 				}
 			}
diff --git a/cms/ui/src/main/resources/jcr_root/libs/sling-cms/content/shared/movecopy.json b/cms/ui/src/main/resources/jcr_root/libs/sling-cms/content/shared/movecopy.json
index 7a737a0..e685e1b 100644
--- a/cms/ui/src/main/resources/jcr_root/libs/sling-cms/content/shared/movecopy.json
+++ b/cms/ui/src/main/resources/jcr_root/libs/sling-cms/content/shared/movecopy.json
@@ -48,6 +48,13 @@
 								"value": "move"
 							}
 						}
+					},
+					"references": {
+						"jcr:primaryType": "nt:unstructured",
+						"sling:resourceType": "sling-cms/components/editor/fields/references",
+						"label": "Update References?",
+						"name": ":updateReferences",
+						"toggle": true
 					}
 				}
 			}

-- 
To stop receiving notification emails like this one, please contact
dklco@apache.org.