Hi Friends,
Throughout my project experiences , I have been surrounded with quite challenging tasks. One of that was exploring Groovy Script. By this blog post , I would try to make you familiar with Groovy and I am sure by the end of it, you will be loving this language. Check out all the examples I worked out during my projects and I have tried to explain what magic you can do with Groovy by comments. So go ahead explore and let me know if you have any challenging problems on Groovy.
Installation of Groovy Console :
The first step to learn Groovy would be to install its IDE. Citytechinc provides a Downloadable zip package which you can install in your instances and you are good to go.
Groovy for AEM 6.1 with Intelligence (Preferable)
AEM Groovy Console provides an interface for running Groovy scripts in the AEM (Adobe CQ) container. What I will try to do is make you familiar with the language through various examples and then you can solve any use case as per your clients business need.
AEM examples and Sample Use Cases solved by Groovy
- Hello World
This is the most basic program for any language and its a convention to let you know this. We will jump to more client specific requirements in the subsequent examples.
println 'Hello Groovy World '
- Find Number of Pages, Page Title , Page Names in a Site hierarchy
By this code piece I will try to show you guys how a page is traversed in groovy and how its properties can be retrieved and displayed.
/**@author Hashim Khan */ import javax.jcr.Node /*Flag to count the number of pages*/ noOfPages = 0 /*Pathfield which needs to be iterated for an operation*/ path='/content/geometrixx/en/' findAllPages() /*This method is used to Iterate all the pages under a hierarchy *and get their page title ,path and the overall number of *pages.*/ def findAllPages(){ getPage(path).recurse { page -> println 'Title:'+page.title println 'Path:'+page.path noOfPages ++ } }
- Find all the pages wherein a particular component is being used.
Sometimes there will be a scenario where you have to find (and modify/delete) a particular component from the complete site structure in multiple environments. There is no better solution for that kind of problem , other than Groovy Script.
/** @author Hashim Khan */ /** @author Hashim Khan */ /*This method is used to Query the JCR and find results as per the Query.*/ def buildQuery(page, term) { def queryManager = session.workspace.queryManager; def statement = 'select * from nt:base where jcr:path like \''+page.path+'/%\' and sling:resourceType = \'' + term + '\''; queryManager.createQuery(statement, 'sql'); } /*Defined Content Hierarchy */ final def page = getPage('/content/geometrixx/en/') /*Component ResourceType which is searched in the content hierarchy */ final def query = buildQuery(page, 'foundation/components/text'); final def result = query.execute() count = 0; result.nodes.each { node -> String nodePath = node.path; println nodePath } println 'No Of Pages found :' + result.nodes.size(); /** @author Hashim Khan */
- Find all the pages of a particular Template .
I have depicted to display all the pages using a specific template. In real world you might be asked to modify some of the properties of a template or add, subtract something. All this you can easily do in an instant using groovy script.
/**@author Hashim Khan */ import javax.jcr.Node /*Flag to count the number of pages*/ noOfPages = 0 /*Pathfield which needs to be iterated for an operation*/ path='/content/geometrixx/en/' findAllPagesWidTemplate() /*This method is used to Iterate all the pages under a hierarchy *and find pages with a specific template */ def findAllPagesWidTemplate(){ getPage(path).recurse { page -> def content = page.node def property= content.get('sling:resourceType') if(property=="geometrixx/components/contentpage"){ noOfPages ++ println 'Page Path:'+content.path } } } println 'No Of Pages::'+noOfPages
Alternatively we could have used Query Builder API to find the pages with a particular template. The below method is more robust and user friendly :
SQL QUERY
/** @author Hashim Khan */ /*This method is used to Query the JCR and find results as per the Query.*/ def buildQuery(page, term) { def queryManager = session.workspace.queryManager; def statement = 'select * from nt:base where jcr:path like \''+page.path+'/%\' and sling:resourceType = \'' + term + '\''; /*Here term is the sling:resourceType property value*/ queryManager.createQuery(statement, 'sql'); } /*Defined Content Hierarchy */ final def page = getPage('/content/geometrixx/en/') /*Template which is searched in the content hierarchy */ final def query = buildQuery(page, 'geometrixx/components/contentpage'); final def result = query.execute() println 'No Of pages found = ' + result.nodes.size(); result.nodes.each { node -> println 'nodePath::'+node.path }
XPATH QUERY
/*This method is used to Query the JCR and find results as per the Query.*/ def buildQuery(page, term) { def queryManager = session.workspace.queryManager; def statement = "/jcr:root${page.path}//element(*, cq:Page)[jcr:content/@cq:template = '"+term+"']" /*Here term is the cq:template value*/ def query = queryManager.createQuery(statement, 'xpath') } /*Defined Content Hierarchy */ final def page = getPage('/content/geometrixx/en/') /*Component ResourceType which is searched in the content hierarchy */ final def query = buildQuery(page, '/apps/geometrixx/templates/contentpage'); final def result = query.execute() count = 0; result.nodes.each { node -> String nodePath = node.path; println nodePath } println 'No Of component found :' + result.nodes.size(); result.nodes.each { node -> println 'nodePath::'+node.path }
- Delete all the nodes of a particular type with a specific property.
Deletion of a particular node is quite handy when you have to similar use case and want to modify the content quickly and easily.
/** @author Hashim Khan */ /*This method is used to Query the JCR and find results as per the Query.*/ def buildQuery(page, term) { def queryManager = session.workspace.queryManager; def statement = 'select * from nt:base where jcr:path like \''+page.path+'/%\' and sling:resourceType = \'' + term + '\''; queryManager.createQuery(statement, 'sql'); } /*Defined Content Hierarchy */ final def page = getPage('/content/geometrixx/en/') /*Component ResourceType which is searched in the content hierarchy */ final def query = buildQuery(page, 'foundation/components/flash'); final def result = query.execute() count = 0; result.nodes.each { node -> String nodePath = node.path; if(nodePath.contains('flash') && !nodePath.contains('jcr:versionStorage') ){ count ++; println 'deleting--'+nodePath ; node.remove(); /* Save this session if you are sure the correct nodes are being deleted. Once the session is saved the nodes couldn't be retrieved back. *session.save();*/ } } println 'No Of component found :' + result.nodes.size(); println 'Number of Component Deleted: ' + count;
- Modify a property in a complete site hierarchy as per business logic.
There was a real time problem in one of my project where we have to fill in jcr:title in the Page-title whenever the Page title was a null. Moreover we were having multiple languages sites and have to browse through all of them at once. We used groovy to solve this problem for multiple development environments. Similar to the below example where I am modifying a particular node and its property for the complete hierarchy using Groovy. I have used example for a Geometrixx site (AEM 6.0) so that you can may the results for yourself.
/** @author Hashim Khan */ /*This method is used to Query the JCR and find results as per the Query.*/ def buildQuery(page, term) { def queryManager = session.workspace.queryManager; def statement = 'select * from nt:base where jcr:path like \''+page.path+'/%\' and sling:resourceType = \'' + term + '\''; queryManager.createQuery(statement, 'sql'); } /*Defined Content Hierarchy */ final def page = getPage('/content/geometrixx/en/') /*Component ResourceType which is searched in the content hierarchy */ final def query = buildQuery(page, 'collab/calendar/components/event'); final def result = query.execute() count = 0; result.nodes.each { node -> String nodePath = node.path; if(nodePath.contains('event') && !nodePath.contains('jcr:versionStorage') ){ /*The below iterator is used to fetch the child pages of the parent node */ node.findAll { it.hasNodes() }.each { if(it.name.contains("event")){ count ++; println 'Title--'+it.get('jcr:title') ; println 'Node Path--'+it.path ; it.set('jcr:title','Hashim'); println 'Title--'+it.get('jcr:title') ; session.save() } } } } println 'Number Of Component Found :' + result.nodes.size(); println 'Number of Component Modified:' + count;
- Count Number of Nodes which have more than 1000+ child nodes.
This a common use case wherein you are asked to check whether a particular hierarchy has nodes which has more than 1000 child nodes . You can change the Search Path as per you convenience and list down the nodes under that hierarchy.
/** @author Hashim Khan */ import javax.jcr.NodeIterator def path="/etc/tags" def variable = 1000 println 'Node,COUNT' getNode(path).recurse { node > NodeIterator it = node.getNodes() def count =0 while(it.hasNext()){ def nodetemp = it.nextNode() count++ } if(count>=variable) println node.path + ','+count }
- Delete all the Unused Tags in an application.
In this script the unused tags are counted in an application and deleted with a delay . If the tag count is much more it is advisable to run this script in a more specified path. You have to run this script a few times as it doesn’t delete the tags which has any child nodes.
/** @author Hashim Khan */ import org.apache.sling.api.resource.Resource import com.day.cq.tagging.Tag import com.day.cq.tagging.TagManager import org.apache.sling.api.resource.ResourceResolver import java.lang.Thread.*; import javax.jcr.Node; def tagpath = "/etc/tags"; def delay = 10 ; //in Milliseconds. def query = getAllTags(tagpath) def result = query.execute() def rows = result.rows def unusedTags = 0 rows.each { row > Resource res = resourceResolver.getResource(row.path) if(res!=null){ Tag tag = res.adaptTo(com.day.cq.tagging.Tag) Node tempNode = res.adaptTo(javax.jcr.Node); if(tag.getCount()==0){ if(!tempNode.hasNodes()){ unusedTags++ println "Deleted Tag : " + tag.getPath() tempNode.remove() } } Thread.currentThread().sleep((long)(delay)); } } println "Total Unused Tags :"+unusedTags //session.save() //Uncomment this to make it working. def getAllTags(tagpath) { def queryManager = session.workspace.queryManager def statement = "/jcr:root"+tagpath+"//element(*, cq:Tag)" def query = queryManager.createQuery(statement, "xpath") }
- Merge Duplicate Tags in an Application
This was a requirement in one of my client who asked us to merge the Duplicate Tags . This way you can list out all the duplicate tags and merge all of them into the first Master Tag. All the related references in the Pages will automatically be changed as per the API.
/** @author Hashim Khan */ import org.apache.sling.api.resource.Resource import com.day.cq.tagging.Tag import org.apache.sling.api.resource.ResourceResolver import com.day.cq.tagging.TagManager import javax.jcr.Node; import java.lang.Thread.*; def tagLocation = "/etc/tags" def delay = 10 ; //in Milliseconds. def buildQuery(tagLocation) { def queryManager = session.workspace.queryManager; def statement = "/jcr:root"+tagLocation+ "//element(*, cq:Tag)" def query = queryManager.createQuery(statement, 'xpath') } def findDuplicateTags(tagLocation,tagNodeName) { def queryManager = session.workspace.queryManager; def statement = "/jcr:root"+tagLocation+ "//element(*, cq:Tag) [fn:name() = '" + tagNodeName + "' ]" def query = queryManager.createQuery(statement, 'xpath') } final def query = buildQuery(tagLocation); final def result = query.execute() def tagList = [] result.nodes.each {node-> String nodeTitle = node.name; tagList.add(nodeTitle); } def duplicates = tagList.findAll {tagList.count(it) > 1} def uniqueUsers = duplicates.unique(mutate = false) def count = 0; TagManager tm = resourceResolver.adaptTo(com.day.cq.tagging.TagManager); def mergecount = 0; uniqueUsers.each { def tagquery = findDuplicateTags(tagLocation,it); def pathresult = tagquery.execute() Tag tag , masterTag =null; count = 0; pathresult.nodes.each {node-> Resource r = resourceResolver.getResource(node.path) tag = r.adaptTo(com.day.cq.tagging.Tag) Node tempNode = r.adaptTo(javax.jcr.Node); if(count == 0 ){ masterTag = tag ; }else if(tm!=null && !(tag.getPath()==masterTag.getPath())){ if(!tempNode.hasNodes()){ println 'Merging Tag :: ' + tag.getPath() +' into>> '+ masterTag.getPath() mergecount++ tm.mergeTag(tag,masterTag) } } count++ Thread.currentThread().sleep((long)(delay)); } } println 'Merged tags count ::'+ mergecount
- Create a CSV File for Duplicate Tags List in the Application.
This script can be used to generate a CSV output and store into filesystem . It lists down all the tags which are Duplicate and all the pages where they are being used. It will help to analyse the System Taxonomy.
/** @author Hashim Khan */ import org.apache.sling.api.resource.Resource import com.day.cq.tagging.Tag import org.apache.sling.api.resource.ResourceResolver def filePath = "/opt/adobe/output.csv" def tagLocation = "/etc/tags/geometrixx-media" def buildQuery(tagLocation) { def queryManager = session.workspace.queryManager; def statement = "/jcr:root"+tagLocation+ "//element(*, cq:Tag)" def query = queryManager.createQuery(statement, 'xpath') } def findDuplicateTags(tagLocation,tagNodeName) { def queryManager = session.workspace.queryManager; def statement = "/jcr:root"+tagLocation+ "//element(*, cq:Tag) [fn:name() = '" + tagNodeName + "' ]" def query = queryManager.createQuery(statement, 'xpath') } def findPagesWithTag(tagId, tagPath) { def queryManager = session.workspace.queryManager; def statement = "//element(*, cq:Page)[(jcr:content/@cq:tags = '" + tagId + "' or jcr:content/@cq:tags = '" + tagPath + "' )]" def query = queryManager.createQuery(statement, 'xpath') } final def query = buildQuery(tagLocation); final def result = query.execute() def tagList = [] f = new File(filePath) result.nodes.each {node-> String nodeTitle = node.name; tagList.add(nodeTitle); } def duplicates = tagList.findAll {tagList.count(it) > 1} def uniqueUsers = duplicates.unique(mutate = false) print 'TAGTITLE ,TAGID , Pages , Count'+'\n' f.append('TAGTITLE ,TAGID , Pages , Count'+'\n') uniqueUsers.each { def tagquery = findDuplicateTags(tagLocation,it); def pathresult = tagquery.execute() pathresult.nodes.each {node-> Resource r = resourceResolver.getResource(node.path) Tag t1 = r.adaptTo(com.day.cq.tagging.Tag) print t1.getTitle()+',' f.append(t1.getTitle()+',') def pagequery = findPagesWithTag(t1.getTagID(), node.path); def pageresult = pagequery.execute() print t1.getTagID()+',' f.append(t1.getTagID()+',') count = 0; def totalResults = pageresult.getTotalSize() pageresult.nodes.each { pagenode-> if(count>0){ print ',' f.append(',') } print pagenode.path+',' f.append(pagenode.path+',') if(count==0){ print t1.getCount()+',' f.append(t1.getCount())+',' } count++; if (totalResults != count ){ print '\n' f.append('\n') } print ',' f.append (',') } print '\n' f.append ('\n') } print '\n' f.append ('\n') }
- Fill all the Unused Assets in AEM Content
/* Find all the Assets which are not referenced in the content and could be removed. @author Hashim Khan */ def predicates = [path: "/content/dam/geometrixx", type: "dam:Asset", "orderby.index": "true", "orderby.sort": "desc"] def query = createQuery(predicates) query.hitsPerPage = 500 def result = query.result println "${result.totalMatches} hits, execution time = ${result.executionTime}s\n--" result.hits.each { hit -> def path=hit.node.path Resource res = resourceResolver.getResource(path) if(res!=null){ getAllReferences(path); } } def getAllReferences(assetpath) { def queryManager = session.workspace.queryManager def statement = "/jcr:root" + "/content/geometrixx//*" + "[jcr:contains(., '"+assetpath+"')]" def query = queryManager.createQuery(statement, "xpath") def result = query.execute() def rows = result.getRows().size if(rows==0){ println "Unused Asset: "+assetpath } }
- Fill all the Unused Components in AEM Content
/* Find all the Components which are not used in the content and could be removed. @author Hashim Khan */ def predicates = [path: "/apps/geometrixx/components", type: "cq:Component", "orderby.index": "true", "orderby.sort": "desc"] def query = createQuery(predicates) query.hitsPerPage = 1000 def result = query.result println "${result.totalMatches} hits, execution time = ${result.executionTime}s\n--" result.hits.each { hit -> def path=hit.node.path Resource res = resourceResolver.getResource(path) if(res!=null){ path = path.substring(6,path.length()) getAllReferences(path); } } def getAllReferences(assetpath) { def queryManager = session.workspace.queryManager def statement = "/jcr:root" + "/content/geometrixx//*" + "[@sling:resourceType='"+ assetpath+"']" def query = queryManager.createQuery(statement, "xpath") def result = query.execute() def rows = result.getRows().size if(rows==0){ println "Asset="+assetpath+"; Results="+rows println "********Unused Component*******: " } }
- Validate Dispatcher Security for your website
/* This script could be used to check the Dispatcher security by checking the the below paths as per Security Checklist https://helpx.adobe.com/experience-manager/dispatcher/using/dispatcher-configuration.html#TestingDispatcherSecurity Make sure to Change the protocol, domain and one valid page as per your website. None of the curl response should give a 200 status. @author Hashim Khan */ /*Defines the protocol for th website */ def protocol="https://" /*Defines the main domain URL for the website */ def domain="www.intel.com" /*Defines a sample page in the application to check content grabbing. */ def valid_page="/content/www/us/en/homepage" /*Defines a list of security URLs which are used to verify Dispatcher Configurations. You can add more if you like. Current list is from https://helpx.adobe.com/experience-manager/dispatcher/using/dispatcher-configuration.html#TestingDispatcherSecurity */ def list = [ protocol+domain+"/admin", protocol+domain+"/system/console", protocol+domain+"/dav/crx.default", protocol+domain+"/crx", protocol+domain+"/bin/crxde/logs", protocol+domain+"/jcr:system/jcr:versionStorage.json", protocol+domain+"/_jcr_system/_jcr_versionStorage.json", protocol+domain+"/libs/wcm/core/content/siteadmin.html", protocol+domain+"/libs/collab/core/content/admin.html", protocol+domain+"/libs/cq/ui/content/dumplibs.html", protocol+domain+"/var/linkchecker.html", protocol+domain+"/etc/linkchecker.html", protocol+domain+"/home/users/a/admin/profile.json", protocol+domain+"/home/users/a/admin/profile.xml", protocol+domain+"/libs/cq/core/content/login.json", protocol+domain+"/content/../libs/foundation/components/text/text.jsp", protocol+domain+"/content/.{.}/libs/foundation/components/text/text.jsp", protocol+domain+"/apps/sling/config/org.apache.felix.webconsole.internal.servlet.OsgiManager.config/jcr%3acontent/jcr%3adata", protocol+domain+"/libs/foundation/components/primary/cq/workflow/components/participants/json.GET.servlet", protocol+domain+"/content.pages.json", protocol+domain+"/content.languages.json", protocol+domain+"/content.blueprint.json", protocol+domain+"/content.-1.json", protocol+domain+"/content.10.json", protocol+domain+"/content.infinity.json", protocol+domain+"/content.tidy.json", protocol+domain+"/content.tidy.-1.blubber.json", protocol+domain+"/content/dam.tidy.-100.json", protocol+domain+"/content/content/geometrixx.sitemap.txt", protocol+domain+valid_page+".query.json?statement=//*", protocol+domain+valid_page+".qu%65ry.js%6Fn?statement=//*", protocol+domain+valid_page+".query.json?statement=//*[@transportPassword]/(@transportPassword%20|%20@transportUri%20|%20@transportUser)", protocol+domain+valid_page+"/_jcr_content.json", protocol+domain+valid_page+"/_jcr_content.feed", protocol+domain+valid_page+"/jcr:content.feed", protocol+domain+valid_page+"._jcr_content.feed", protocol+domain+valid_page+".jcr:content.feed", protocol+domain+valid_page+".docview.xml", protocol+domain+valid_page+".docview.json", protocol+domain+valid_page+".sysview.xml", protocol+domain+"/etc.xml", protocol+domain+"/content.feed.xml", protocol+domain+"/content.rss.xml", protocol+domain+"/content.feed.html", protocol+domain+valid_page+".html?debug=layout" ] list.each { checkUrlGetStatus(it) } checkUserGeneratedPath(protocol,domain) checkDispatcherInvalidation(protocol,domain) /*Method to make a GET call for the above list. */ def checkUrlGetStatus(String path) { print path+" , " def process ="curl -s -o /dev/null -w %{http_code} ${path} ".execute().text printf("%2s", "STATUS:") process.each { text-> print "${text}" } println "" } /*Method to make a POST call for the user generated path. */ def checkUserGeneratedPath(String protocol,String domain) { print "POST:"+protocol+domain+"/content/usergenerated/mytestnode ," def process ="curl -X POST -s -o /dev/null -w %{http_code} -u anonymous:anonymous ${protocol}${domain}/content/usergenerated/mytestnode".execute().text printf("%2s", "STATUS:") process.each { text-> print "${text}" } println "" } /*Method to make a Dispatcher invalidation call. */ def checkDispatcherInvalidation(String protocol,String domain) { print "Dispatcher Invalidation: "+protocol+domain+"/dispatcher/invalidate.cache ," def process ="curl -s -o /dev/null -w %{http_code} -H \'CQ-Handle:/content\' -H \'CQ-Path:/content\' ${protocol}${domain}/dispatcher/invalidate.cache".execute().text printf("%2s", "STATUS:") process.each { text-> print "${text}" } } /* Curl Command Syntax to Parse the XML output. def proc = "curl -u admin:admin http://localhost:4502/content/geometrixx/en.html".execute().text def crx = new XmlSlurper().parseText(proc); def packages = crx.response.data.packages def status = crx.response.status; println "status:"+status; packages.package.each { pack-> println "${pack.name}: ${pack.size}" } */
- Validate & Read Sitemap XML paths to create Cache
/* This script could be used to create Cache by reading Sitemap and making a GET call for all the paths present in it. Make sure to Change the protocol and domain as per your website. Note that none of the request should give a 404. @author Hashim Khan */ /*Defines the protocol for the website */ def protocol="https://" /*Defines the main domain URL for the website */ def domain="www.lifetime.life" readSitemapXML(protocol,domain) /*Method to make a GET call for the above list. */ def checkUrlGetStatus(String path) { print path+" , " def process ="curl -s -o /dev/null -w %{http_code} ${path} ".execute().text printf("%2s", "STATUS:") process.each { text-> print "${text}" } println "" } /*Method to make a GET call at Sitemap and read the XML output recursively. */ /* Curl Command Syntax to Parse the XML output. */ def readSitemapXML( String protocol,String domain) { def proc = "curl -X GET ${protocol}${domain}/sitemap.xml".execute().text def output = new XmlSlurper().parseText(proc); def urls = output.url.loc urls.each { path-> def url = "${path}" checkUrlGetStatus(url) } }
So friends as you may have noticed that Groovy could be quite useful in an AEM project where you need to modify some content / property in one go. Do let me know if you face any other challenging issues where I could help you in using Groovy.
There could be million of other things where Groovy is very useful . If you want to explore more on Groovy use the pdf :: Groovy Recipes
Happy Groovying !! :)
Now all the Groovy Scripts are available in the GITHUB Project – https://github.com/hashimkhan786/aem-groovy-scripts
This is really awesome Hashim. Your one of the script helped me too. Thanks for putting them down. Do put more samples, that can be reused.
LikeLiked by 1 person
Thats great… :D Good to know this was helpful to you.
LikeLike
Hi Hasim,
I have a requirement to update smart tags to Assets in AEM tool using csv file alone.
I have tried the option of csv asset importer and Tag maker but both are failed, CSV Asset importer is failing to upload the images and Tag maker is used to add tags to repository and then to AEM assets.
I would like to update the smart tags without adding them to repository. I came to know that this can be achieved using Groovy script.
I have installed Groovy Console in AEM Local instance and now I am in need of a groovy script to read the csv file which will have all the smart tags and once I import this csv file through groovy script. It has to update the images in AEM tool with the smart tags available in csv file.
Please share a solution from your end
LikeLike
Thank you very much! Very helpful code samples
LikeLike
Your welcome. Would post a few more business use cases :)
LikeLike
Hi Hashim,Can u please provide me the link from where i can explore more about Groovy Script.
LikeLike
Hi,
Please use the pdf attached to the post and use the latest build of groovy on aem 6.1 . It has some intelligence too , that would help you.
Regards
Hashim
LikeLike
I just get:
groovy.lang.MissingMethodException: No signature of method: org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.getPage() is applicable for argument types: (java.lang.String) values: [/content/geometrixx/en/] Possible solutions: getAt(java.lang.String), get(java.lang.String), getClass(), eval(java.lang.String)
LikeLike
Hi Tim,
Nice to hear from you after long. Do you remember (Local World :) ).
Which version of Groovy console are you using ? There is a new version which has inbuilt intelligence for AEM 6.1 https://github.com/Citytechinc/cq-groovy-console
This will help you a lot.
Have you imported the necessary classes.
Let me know what code are you using.
Regards
Hashim
LikeLike
Hi Hasim,
I`m trying to remove tags from tag references on deleting tag from ‘Tagging’ section. I`m suceeded in getting list of tag references that tag is being used in using javascript. But unable to trigger the deletetion of those tags on those references.
I only need to do it using javascript. no java code. Can u help me… here is the code I worte so far.
(function () {
if (window.location.pathname !== “/tagging”) {
return;
}
registerShowRefsAlert();
//the query to find tag references (pages and assets)
var CHECK_TAGS_SQL_2_QUERY = “SELECT * from [nt:base] AS t WHERE NAME(t) = ‘jcr:content’ ” +
“AND CONTAINS(t.*, ‘PLACEHOLDER’)”;
function registerShowRefsAlert(){
var tagAdmin = CQ.tagging.TagAdmin,
deleteTagFn = tagAdmin.deleteTag;
//override ootb function to inject the logic showing references alert
tagAdmin.deleteTag = function(){
var tagPath = tagAdmin.getSelectedTag();
if (tagPath == null) {
return;
}
tagPath = tagPath.substring( this.tagsBasePath.length + 1);
var tagInfo = CQ.tagging.parseTag(tagPath, true),
query = encodeURIComponent(CHECK_TAGS_SQL_2_QUERY.replace(“PLACEHOLDER”, tagInfo.getTagID()));
//you may want to replace this crxde lite call with a servlet returning query results
query = “/crx/de/query.jsp?type=JCR-SQL2&showResults=true&stmt=” + query;
//”this” here is tagadmin object, passed as context
$.ajax( { url: query, context: this } ).done(showAlert);
};
function showAlert(data){
if(_.isEmpty(data) || _.isEmpty(data.results)){
deleteTagFn.call(this);
return;
}
var message = “Selected tag is referenced. Click ‘yes’ to proceed deleting, ‘no’ to cancel the operation.”;
_.each(data.results, function(result){
message = message + result.path + “”;
});
CQ.Ext.Msg.show({
“title”: “Delete Tag”,
“msg”: message,
“buttons”: CQ.Ext.Msg.YESNO,
“icon”: CQ.Ext.MessageBox.QUESTION,
“fn”: function (btnId) {
if (btnId == “yes”) {
this.postTagCommand(“deleteTag”, tagAdmin.getSelectedTag());
}
},
“scope”: this
});
}
}
}());
LikeLike
Hi,
I would not recommend to do this from JS . You should write a servlet or jsp to make an ajax call. This should be a server side call where you can use all the Sling and JAVA APIs . Client side queries from JS to server is quite expensive and is not recommended.
LikeLike
Hi,installed the zip, tried verifying and got this :
Error during include of component ‘/apps/groovyconsole/components/console’
Error Message:
org.apache.sling.api.scripting.ScriptEvaluationException:org.apache.sling.scripting.jsp.jasper.JasperException: /apps/groovyconsole/components/console/head.jsp(1,1)
Also, the bundle is in installed state and i am seeing thi =s in imported packages:
com.day.cq.wcm.tags,version=[6.0,7) — Cannot be resolved
Could you please let me know what’d be the issue.
TIA.
LikeLike
Hi Bilal, It seems there is some mismatch bundle version in the Cognifide package. You can check that in /system/console/bundles. Please try to use the older groovy console package.
LikeLike
Hi , This is very useful and i am trying to copy and paste a jcr node via groovy script and getting exception for all the common methods that i am using like , move / copy / clone . Can you help in here if you ever had done this ?
Thanks in Advance.
LikeLike
Hi Vaishali , What code snippet are you using ?
Can you check some sample code here – https://github.com/OlsonDigital/aem-groovy-console/tree/master/src/main/scripts
LikeLike
Hi, Thanks. It worked after trying with workspace.copy
LikeLike
Hello Hashim,
i need to get all the users and update their property based on xls sheet. how do i get those users node in groovy?
LikeLike
Hi Saurabh, You can use a query builder within groovy to search for all nodes of jcr:primaryType as rep:User under the path /home/users and then iterate it and add the properties to it based on your excel sheet.
LikeLike
Hi Hashim,
after running groovy , we have found it doesn’t update all the node which we are getting from a query. Suppose if i have to update 1000 nodes it will update 850-900 in one go & if i run the script again it will update remaining 150-100 nodes. Why it doesn’t work in one go. Is there any delay needs to implement in script while saving session ?
LikeLike
Hi Mohit,
Ideally this shouldnt happen, but its always better to add a delay of 1-2 seconds between every 100-200 node updates.
LikeLike
Hi Hashim, Hope you are doing good. Have you did something like giving inputs in excel sheet to AEM in groovy script? can you share some exampled if you have ?
Thanks in advance.
LikeLike
Hi Vaishali,
You can probably read an excel file from your system using https://stackoverflow.com/questions/22031375/reading-excel-and-writing-to-xml-in-groovy-for-soapui and then create a curl command to execute it in AEM or read the input from Excel and pass on that data to AEM nodes/properties.
LikeLike
while creating csv file you have provided filepath /opt/adobe/output.csv. So where this file will create?
LikeLike
You can give any path based on your system . This was according to my system.
LikeLike
Hi Hashim, I have one requirement, to get the recently activated pages based on the last modified property, Can you help me with the groovy script for the same, if you have any
LikeLike
Hi, You can create a query for such a use-case and then use Xpath Groovy script mentioned above to implement that query. Check this for some query samples : https://hashimkhan.in/2017/10/09/digest-the-query-builder-api-aem-search-tips/
LikeLike
Hi Hashim,
This is really helpful post!
I have a scenario – I do not want to install groovy console on AEM but I want to execute any of above scripts on my AEM (say, removal of nodes with specific properties). Means, if Groovy as an outside entity have to change nodes/properties in AEM. How can I do it?
Regards,
Abhey
LikeLike
Hi,
You can try to use the Script Console http://experience-aem.blogspot.com/2015/11/aem-61-setup-groovy-scripts-debugging-in-idea-for-felix-script-console.html if you dont want to install Groovy Console.
Or else if you don’t want to install anything on your AEM instance, search for the nodes / properties which you need to modify in your local AEM instance . Create a script where you initiate a cURL command to modify those nodes in your JCR of Prod or other environments.
You can also explore these – http://experience-aem.blogspot.com/2015/11/aem-61-setup-groovy-scripts-debugging-in-idea-for-felix-script-console.html & https://stackoverflow.com/questions/13594772/running-groovy-script-from-any-user-directory-on-linux
LikeLike
Hi Hashim
I have a groovy scripts which update around 150 nodes, best possible way to replicate those nodes in publish environment.
LikeLike
Hi Ritesh,
There could be a number of ways to achieve this. You can create a package for the necessary nodes and activate the package using Replicator. Or you can activate each node one by one using a sleep in between.
I would prefer the first approach.
Create a Package:
https://github.com/OlsonDigital/aem-groovy-console/blob/develop/src/main/scripts/CreatePackage.groovy
Activate a Page/Package:
https://stackoverflow.com/questions/32958010/adobe-cq-aem-groovy-to-activate-a-page
LikeLike
Hi Hashim,
I need to implement some functionality like link checker, the groovy script traverse through all the pages in my site and find the broken links. basically we are looking for some external crawler. It would be a great help if you can guide over this.
Thanks,
Ashish
LikeLike
Hi Ashish,
You can explore various Crawlers which use Groovy to browse through the site. Check this link – https://jolorenz.wordpress.com/2014/08/11/create-a-webcrawler-with-less-than-100-lines-of-code/
There is also a nice way to check broken links – https://gist.github.com/trekawek/72b3515a6641ca5f4b29 where properties are verified in content and checked using Resource Resolver.
https://forums.adobe.com/thread/2331233
But what is the use-case for this request? Is AEM Link checker not working as per expectations?
LikeLike
Hi Hashim,
I have a requirement of accessing the template used for a page ( the path will be in cq:Template property) and replacing it with another path. Will it be possible to do this with groovy script ?
Thanks
Shruti
LikeLike
Yes sure. It’s quite straightforward with Groovy. You can try to use this script: “Modify a property in a complete site hierarchy as per business logic. ”
First search for all pages using a Query for cq:Template and then change the property as per need.
LikeLike
Hi Hashim – When listing out the list of pages provided a path, can we get data like lastModified & LastReplicated by ? I am sure that we can get last modified by but not sure abt replicated by. And how to we convert the GregorianCalendar object to a normal date in Groovy ? Thanks for your inputs.
LikeLike
Hi,
Yes sure . You can do something like this –
getPage(path).recurse
{ page ->
def content = page.node
def property= content.get(‘sling:resourceType’) /*Change to any property name for that node.*/
}
For calendar you can try this –
https://github.com/Hubbitus/groovy-test-examples/blob/master/Structures-Patterns/Date/GregorianCalendar.groovy
https://stackoverflow.com/questions/22848000/how-to-create-new-date-in-groovy-at-specific-date-and-time
LikeLike
Hi Hashim – Thanks for the above option, it worked for me. I have one more request, can I read the page content as well in the form of json (jus like the oob pagepath.json).
LikeLike
Yes, you can read to any depth of properties of a page. Try ..json . If you want to read the complete hierarchy use .infinity.json or .-1.json
LikeLike
Is it recommended and good to use Groovy on Prod instances…?
LikeLike
Hi,
It’s not ideal to make changes on Prod environment using Groovy. Ideally, make changes in the lower environment and then after proper testing, deploy the change to Prod.
LikeLike
Hi Hashim, this post saved me numerous of times. Some great examples there.
I however wasn’t able to follow your examples to write output in a csv.
def filePath = “/content/corporate/reports/output.csv”
File output = new File(filePath)
output.append(‘Hello world!’)
I however end up getting FileNotFoundException. I manually created the file to make sure. What am I missing here?
LikeLike
Are you creating the file in AEM or in the server? I think you need to create it in the server location defined by you and not in AEM.
LikeLike
Create a file where your AEM instance is located and use that location.
LikeLike
Hi Hashim ,
Is there a simple way of removing 2000 users from everyone group in AEM 6.3 who have been accidently added during site migration .
Please let me know .
Thanks
Soumya
LikeLike
Hi Soumya, In AEM you can automate any process – as its all REST API call. Your first goal is to get the list of users who need to be removed. Then write a groovy / curl to POST a call to remove user.
You can create your curl command by monitoring the Browser Console – by deleting 1 user from everyone group. and then run that command for the complete list of users.
LikeLike
thanks Hashim for the response .
Actually i am looking for curl command which can delete users by there login name ..
i don’t want to fetch the path of all 2000 users…. i just have the login name’s and want to delete that way.
LikeLike
No worries i found the paths using aem-acs-commons.
Will delete the users by putting all the individual curl commands in a shell script
LikeLike
I need to delete a certain number of AEM users using curl .
The list of AEM users are in a excel sheet
Every time the curl command executes , it need to fetch the user data from certain line from the excel sheet .
To be precise ,
when the curl command executes for the 1st time, it should fetch the user data mentioned in the 1st row of the excel sheet
when the curl command executes for the 2nd time, it should fetch the user data mentioned in the 2nd row of the excel sheet
when the curl command executes for the 50th time, it should fetch the user data mentioned in the 50th row of the excel sheet
when the curl command executes for the 500th time, it should fetch the user data mentioned in the 500th row of the excel sheet
The platform is Linux .
Any help on how to achive this would be really helpful. Thanks in advance .
LikeLike
Hi Hashim,
Thank you for this useful information regarding groovy console. I was trying to run the script for deleting all the un-used tags and getting this error.
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Script1.groovy: 18: expecting ‘}’, found ‘res’ @ line 18, column 11.
Resource res = resourceResolver.getResource(row.path)
LikeLike
Hi Hashim ,
Currently we faced an issue where we have to create package with 200 different filter path , that were provided to us in a text file . Can you guide me how to automate it without adding each content path in filter during package creation.
LikeLike
Can we automate groovy script running?
LikeLike
Hi, Yes I think you should be able to do so. After installing the Groovy console in your AEM instance, try to replicate the post.json call which has Script data in Formdata.
curl ‘http://localhost:4502/bin/groovyconsole/post.json’ –data $’script=’
For more details look for this call in Browser Console.
LikeLike
Can we modify the jcr:system protected node property values using groovy script. Can you please share an example.
LikeLike
.Hi Hashim I am new to AEM I am getting this error while adding mixinType mix:commerceAsset Properties could you please help me out what does it mean.
javax.jcr.nodetype.ConstraintViolationException: Unable to perform operation. Node is protected
LikeLike
Hi Hashim Khan,
i can iterate all pages in a given path based on below example. How can i restrict that to iterate only first level children??
def findAllPages(){
getPage(path).recurse
{ page ->
}
}
LikeLike
Hi Hashim Khan,
How i can get all the unused dam assets in aem..I want to display all the dam assets on a page along all its data like path,name,type which status is unpublished.. and i want to delete all that unused dam assets with a one click.. please tell me the code to achieve it..
LikeLike
Hi Hashim,
Your content has been the most helpful and insightful. I am hoping I would find a solution for my project.
I work on AEM 6.1 SP2 and I am trying to get a Groovy console that works for this 6.1 version. I tried the package mentioned above for 6.1 but it would give me an error “jcr_root” not found.
My objective is to come up with a script to capture all the edited/modified assets from the previous day and capture the comments in those assets.
Any tips and suggestions will be greatly appreciated.
Many Thanks
Raja
LikeLike
Hi hashim,
I m trying to configure Groovy console version 11.3.0 in aem 6.5. I did upload and install part through crx package manager and then tried to open groovy console but i got this error.
org.apache.sling.api.SlingException: Cannot get DefaultSlingScript: org.apache.sling.api.SlingException: Cannot get DefaultSlingScript: Compilation errors in org/apache/sling/scripting/sightly/apps/groovyconsole/components/console/body_html.java:
Line 88, column 5039 : com.icfolson.aem.groovy.console.components.Body cannot be resolved to a type
LikeLike
What’s the difference between using .recurse versus .each to iterate through the pages?
LikeLike
HI Hashim,
i have a requirement to fetch a list of all the activated, deactivated and unused images from DAM asset with their respective sizes. how to go on with this?
LikeLike