Hi Team,
we are trying to implement global content find and replace in touch UI(AEMasCS). which was similar functionality in classic UI below. Could you please let us know any help on this.
http://localhost:4502/siteadmin#/content/wknd/us/en/
instead siteadmin interface ,we need this functionality on touch UI(specifically in AEMasCS environment)
i have also gone through this link content find & replace global · Issue #15 · Adobe-Consulting-Services/acs-aem-tools · GitHub
- Kindly let us know,is there any update on this issue?
Views
Replies
Total Likes
Hi @rajat168
If you want this feature to be implemented in Touch UI, please raise it at Adobe Experience Manager Ideas.
Hi @arunpatidar , thanks for your valuable input. i have raised my idea on this Implementation of find and replace content on AEMasCS portal. Kindly let us know,is there any alternate solution of this same?
Hi @rajat168
We have used groovy script to find and replace certain link and content globally. I think you can use the same, otherwise you have to reply on AEM search and manual replacement.
@arunpatidar thanks, im new to groovy, could you please help me how to use this groovy console for find the content using FindReplaceServlet.java in groovy console.
1) how to call the FindReplaceServlet.java
2) how to pass the search content to this servlet?
3) how to perform replace.
Hi @rajat168
You can create a mock request in java to call servlet, more details
You may need to check the implementation of FindReplaceServlet.java using java decompiler to know the exact parameters
Hi @arunpatidar , i have re-used the FindReplaceServlet.java, the issue we face was since we are traversing more than 10000 node it getting impact the server environment.
package com.day.cq.wcm.core.impl.servlets;
import com.day.cq.commons.JSONWriterUtil;
import com.day.cq.commons.servlets.AbstractPredicateServlet;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageManager;
import com.day.cq.xss.XSSProtectionService;
import com.day.text.ISO9075;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.collections.Predicate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferencePolicy;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
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.ValueMap;
import org.apache.sling.api.servlets.HtmlResponse;
import org.apache.sling.commons.json.JSONException;
import org.apache.sling.commons.json.io.JSONWriter;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Deprecated
@Component(metatype = true, label = "%findreplace.name", description = "%findreplace.description")
@Service
@Properties({@Property(name = "service.description", value = {"Find and Replace servlet"}, propertyPrivate = true), @Property(name = "sling.servlet.methods", value = {"GET", "POST"}, propertyPrivate = true), @Property(name = "sling.servlet.resourceTypes", value = {"cq:Page"}, propertyPrivate = true), @Property(name = "sling.servlet.extensions", value = {"json", "html"}, propertyPrivate = true), @Property(name = "sling.servlet.selectors", value = {"find", "replace"}, propertyPrivate = true)})
public class FindReplaceServlet extends AbstractPredicateServlet {
private static final long serialVersionUID = -4122234496210000472L;
private static final Logger log = LoggerFactory.getLogger(FindReplaceServlet.class);
@Reference(policy = ReferencePolicy.STATIC)
private XSSProtectionService xss;
private static final String PARAM_FIND = "f";
private static final String PARAM_REPLACE = "r";
private static final String PARAM_CASE_SENSITIVE = "cs";
private static final String PARAM_WHOLE_WORDS = "wwo";
private static final String PARAM_PATH = "p";
@Property({"jcr:title", "jcr:description", "jcr:text", "text"})
protected static final String CONFIG_SEARCH_SCOPE = "scope";
protected final String[] htmlSpecialCharacters = new String[] { " " };
protected String[] scope;
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response, Predicate predicate) throws ServletException, IOException {
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
Resource resource = request.getResource();
PageManager pm = (PageManager)resource.getResourceResolver().adaptTo(PageManager.class);
String find = request.getParameter("f");
boolean caseSensitive = "true".equalsIgnoreCase(request.getParameter("cs"));
boolean wholeWords = "true".equalsIgnoreCase(request.getParameter("wwo"));
if (find == null || find.length() == 0)
return;
try {
Writer out = response.getWriter();
JSONWriter writer = new JSONWriter(out);
writer.object();
writer.key("matches");
writer.array();
for (String path : findPages(resource, find, caseSensitive, wholeWords)) {
Page p = pm.getPage(path);
if (p != null) {
writer.object();
JSONWriterUtil.write(writer, "title", p.getTitle(), JSONWriterUtil.WriteMode.AVOID_XSS, this.xss);
writer.key("path").value(p.getPath());
writer.endObject();
}
}
writer.endArray();
writer.endObject();
} catch (JSONException e) {
throw new ServletException(e);
}
}
protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
Iterable<String> paths;
Resource resource = request.getResource();
PageManager pm = (PageManager)resource.getResourceResolver().adaptTo(PageManager.class);
String find = request.getParameter("f");
String replace = request.getParameter("r");
boolean caseSensitive = "true".equalsIgnoreCase(request.getParameter("cs"));
boolean wholeWords = "true".equalsIgnoreCase(request.getParameter("wwo"));
if (find == null || find.length() == 0 || replace == null)
return;
if (request.getParameterMap().containsKey("p")) {
paths = new HashSet<>(Arrays.asList(request.getParameterValues("p")));
} else {
paths = findPages(resource, find, caseSensitive, wholeWords);
}
HtmlResponse htmlResp = new HtmlResponse();
Pattern pattern = createPattern(find, caseSensitive);
for (String path : paths) {
Page p = pm.getPage(path);
if (p != null &&
deepReplace(p.getContentResource(), pattern, replace, wholeWords)) {
ModifiableValueMap values = (ModifiableValueMap)p.getContentResource().adaptTo(ModifiableValueMap.class);
values.put("cq:lastModified", Calendar.getInstance());
values.put("cq:lastModifiedBy", ((Session)resource
.getResourceResolver().adaptTo(Session.class)).getUserID());
try {
p.getContentResource().getResourceResolver().commit();
htmlResp.onModified(path);
} catch (PersistenceException e) {
p.getContentResource().getResourceResolver().revert();
}
}
}
htmlResp.send((HttpServletResponse)response, true);
}
protected Iterable<String> findPages(Resource resource, String find, boolean caseSensitive, boolean wholeWords) {
String path = resource.getPath();
String likeExpression = find.replaceAll("'", "''");
likeExpression = likeExpression.replaceAll("\\\\", "\\\\\\\\");
likeExpression = likeExpression.replaceAll("%", "\\\\%");
likeExpression = likeExpression.replaceAll("_", "\\\\_");
if (!caseSensitive)
likeExpression = likeExpression.toLowerCase();
StringBuffer stmt = new StringBuffer("/jcr:root");
stmt.append(ISO9075.encodePath(path));
stmt.append("//*[");
if (wholeWords)
stmt.append("jcr:contains(., '" + likeExpression + "') and ");
stmt.append("(");
String or = "";
for (String prop : this.scope) {
stmt.append(or);
or = " or ";
stmt.append("jcr:like(");
if (!caseSensitive)
stmt.append("fn:lower-case(");
stmt.append("@").append(ISO9075.encode(prop));
if (!caseSensitive)
stmt.append(")");
stmt.append(",'%").append(likeExpression).append("%')");
}
stmt.append(")]");
if (!wholeWords)
stmt.append(" option(traversal ok)");
Iterator<Resource> matches = resource.getResourceResolver().findResources(stmt
.toString(), "xpath");
PageManager pm = (PageManager)resource.getResourceResolver().adaptTo(PageManager.class);
Pattern pattern = createPattern(find, caseSensitive);
Set<String> paths = new HashSet<>();
while (matches.hasNext()) {
Resource r = matches.next();
if (!wholeWords || hasMatchingValues(r, pattern, wholeWords))
paths.add(pm.getContainingPage(r).getPath());
}
return paths;
}
protected boolean hasMatchingValues(Resource resource, Pattern pattern, boolean wholeWords) {
return ((getMatchingValueNames(resource, pattern, wholeWords)).length > 0);
}
protected String[] getMatchingValueNames(Resource resource, Pattern pattern, boolean wholeWords) {
ValueMap values = (ValueMap)resource.adaptTo(ValueMap.class);
List<String> names = new ArrayList<>();
String repl = "";
while (true) {
repl = repl + "x";
if (!repl.equals(pattern.pattern())) {
for (String name : this.scope) {
String v = (String)values.get(name, String.class);
if (v != null &&
!v.equals(replace(v, pattern, repl, wholeWords)))
names.add(name);
}
return names.<String>toArray(new String[names.size()]);
}
}
}
protected boolean deepReplace(Resource resource, Pattern pattern, String repl, boolean wholeWords) {
boolean mod = replaceMatchingValues(resource, pattern, repl, wholeWords);
ResourceResolver resolver = resource.getResourceResolver();
Iterator<Resource> children = resolver.listChildren(resource);
while (children.hasNext()) {
if (deepReplace(children.next(), pattern, repl, wholeWords))
mod = true;
}
return mod;
}
protected boolean replaceMatchingValues(Resource resource, Pattern pattern, String repl, boolean wholeWords) {
boolean mod = false;
ModifiableValueMap values = (ModifiableValueMap)resource.adaptTo(ModifiableValueMap.class);
for (String name : this.scope) {
boolean multiple = false;
Property prop = (Property)values.get(name, Property.class);
try {
if (prop != null && prop.isMultiple())
multiple = true;
} catch (RepositoryException e) {
log.warn("Unable to check if property '{}' of [{}] is multiple, assuming it's single-valued", name, resource.getPath());
}
if (prop != null)
if (multiple) {
String[] v = (String[])values.get(name, String[].class);
boolean multipleMod = false;
for (int i = 0; i < v.length; i++) {
String result = replace(v[i], pattern, repl, wholeWords);
if (!result.equals(v[i])) {
multipleMod = mod = true;
v[i] = result;
}
}
if (multipleMod)
values.put(name, v);
} else {
String v = (String)values.get(name, String.class);
String result = replace(v, pattern, repl, wholeWords);
if (!result.equals(v)) {
mod = true;
values.put(name, result);
}
}
}
try {
resource.getResourceResolver().commit();
} catch (PersistenceException e) {
resource.getResourceResolver().revert();
mod = false;
log.warn("Unable to save replacement changes wihtin [{}]", resource.getPath(), e);
}
return mod;
}
protected Pattern createPattern(String find, boolean caseSensitive) {
StringBuffer regex = new StringBuffer();
for (int i = 0; i < find.length(); i++) {
char c = find.charAt(i);
if (Character.isLetterOrDigit(c)) {
regex.append(c);
} else {
regex.append("\\").append(c);
}
}
int flags = 0;
if (!caseSensitive) {
flags |= 0x2;
flags |= 0x40;
}
return Pattern.compile(regex.toString(), flags);
}
protected String replace(String input, Pattern pattern, String repl, boolean wholeWords) {
StringBuffer sb = new StringBuffer();
repl = repl.replaceAll("\\\\", "\\\\\\\\");
repl = repl.replaceAll("\\$", "\\\\\\$");
Matcher m = pattern.matcher(input);
while (m.find()) {
int s = m.start();
int e = m.end();
if (!wholeWords || (isWordDelim(input, s - 1) && isWordDelim(input, e))) {
m.appendReplacement(sb, repl);
continue;
}
m.appendReplacement(sb, input.substring(s, e));
}
m.appendTail(sb);
return sb.toString();
}
protected final boolean isWordDelim(String input, int idx) {
if (idx == -1 || idx == input.length())
return true;
char c = input.charAt(idx);
if (c == '&')
return isHtmlSpecialCharacter(input, idx);
return (c == '\000' || c == '/' || c == ' ' || c == '\r' || c == '\n' || c == '\t' || c == '.' || c == ';' || c == '(' || c == ')' || c == ',' || c == '<' || c == '>' || c == '"' || c == '\\');
}
protected final boolean isHtmlSpecialCharacter(String input, int start_id) {
int end_id = input.indexOf(';', start_id);
if (end_id == -1)
return false;
String htmlTestString = input.substring(start_id, end_id + 1);
for (String htmlChar : this.htmlSpecialCharacters) {
if (htmlTestString.equalsIgnoreCase(htmlChar))
return true;
}
return false;
}
protected void activate(ComponentContext context) {
this.scope = (String[])context.getProperties().get("scope");
}
protected void bindXss(XSSProtectionService paramXSSProtectionService) {
this.xss = paramXSSProtectionService;
}
protected void unbindXss(XSSProtectionService paramXSSProtectionService) {
if (this.xss == paramXSSProtectionService)
this.xss = null;
}
}
this search query internally using below oak:index(pathReference),could you please let us know how to call this oak:index?, since when we execute the search query it was not able to traverse more than 20000 node(PFB log).
oak.plugins.index.cursor.TraversingCursor Traversed 20000 nodes with filter Filter(
java.lang.Exception: call stack
at org.apache.jackrabbit.oak.plugins.index.cursor.TraversingCursor.fetchNext(TraversingCursor.java:164) [org.apache.jackrabbit.oak-core:1.62.0]
at org.apache.jackrabbit.oak.plugins.index.cursor.TraversingCursor.next(TraversingCursor.java:141) [org.apache.jackrabbit.oak-core:1.62.0]
at org.apache.jackrabbit.oak.plugins.index.cursor.PrefetchCursor.next(PrefetchCursor.java:76) [org.apache.jackrabbit.oak-core:1.62.0]
at org.apache.jackrabbit.oak.query.ast.SelectorImpl.nextInternal(SelectorImpl.java:537) [org.apache.jackrabbit.oak-core:1.62.0]
at org.apache.jackrabbit.oak.query.ast.SelectorImpl.next(SelectorImpl.java:525) [org.apache.jackrabbit.oak-core:1.62.0]
at org.apache.jackrabbit.oak.query.Qu
@rajat168 Did you find the suggestions from users helpful? Please let us know if you require more information. Otherwise, please mark the answer as correct for posterity. If you've discovered a solution yourself, we would appreciate it if you could share it with the community. Thank you!
Views
Replies
Total Likes
Hi @kautuk_sahni , i didnt find any further help on this... kindly help on this...
Views
Replies
Total Likes
Hi Team, please find below my blog post for Global find and replace text.
Views
Likes
Replies
Views
Likes
Replies
Views
Likes
Replies