Groovy DocumentsInfoScript
The DocumentsInfoScript
The following updater script logs information about documents that are selected by the given XPath query.
Description
Find documents and output their name, type, path and publication workflow properties.
XPath query
Preferably set a query for documents, making sure only one type of the variant is selected to get correct count.
E.g. published documents below 'myroot':
content/documents/myroot//element(*, hippo:document)[@hippostd:stateSummary = 'live' and @hippostd:state = 'published']
E.g. unpublished and new documents below 'myroot':
content/documents/myroot//element(*, hippo:document)[@hippostd:stateSummary != 'live' and @hippostd:state = 'unpublished']
Alternatively, query for handles, where the published document variant will be taken, with fallback to the unpublished.
content/documents/myroot//element(*, hippo:handle)
..or, select documents' parents using /..
content/documents/myroot//element(*, hippo:document)/..
Parameters
The script outputs INFO logging on 3 levels based on the parameter verbosity:
{"verbosity": 0} 0 (default) outputs only a summary of numbers of documents, per type 1 per document the name, type, path, plus the summary 2 per document the name, type, path, publication workflow properties, plus the summary
Script
package org.hippoecm.frontend.plugins.cms.admin.updater import org.hippoecm.repository.HippoStdNodeType import org.hippoecm.repository.HippoStdPubWfNodeType import org.hippoecm.repository.api.HippoNodeType import org.onehippo.repository.update.BaseNodeUpdateVisitor import org.onehippo.repository.util.JcrConstants import javax.jcr.Node import javax.jcr.NodeIterator import javax.jcr.Session /** * Find documents and output their type, name, path and publication workflow properties. * * Parameter: * verbosity: 0 (default) only a summary of numbers of documents, per type * 1 per document the name, type, path, plus the summary * 2 per document the name, type, path, publication workflow properties, plus the summary * * XPath: * 1) preferably query for documents, making sure only one type of the variant is selected to get correct count. * * E.g. published documents below myroot * content/documents/myroot//element(*, hippo:document)[@hippostd:stateSummary = 'live' and @hippostd:state = 'published'] * E.g. unpublished/new documents below myroot * content/documents/myroot//element(*, hippo:document)[@hippostd:stateSummary != 'live' and @hippostd:state = 'unpublished'] * * 2) alternatively query for handle, where the published document variant will be taken, with fallback to the unpublished. * content/documents/myroot//element(*, hippo:handle) * or * content/documents/myroot//element(*, hippo:document)/.. */ class DocumentsInfoScript extends BaseNodeUpdateVisitor { Integer verbosity = 0 List<DocumentInfo> documents = new ArrayList<DocumentInfo>() Map<String,Integer> documentTypes = new HashMap<>() void initialize(Session session) { String v = parametersMap.get("verbosity") if (v != null) { verbosity = Integer.parseInt(v) } log.info "DocumentsInfoScript initialized with parameter verbosity=${verbosity}" } boolean doUpdate(Node node) { log.debug "Visiting node ${node.path} of type ${node.primaryNodeType.name}" Node handle Node document if (node.isNodeType(HippoNodeType.NT_DOCUMENT) && !node.isNodeType(HippoStdNodeType.NT_FOLDER)) { document = node handle = document.parent } else if (node.isNodeType(HippoNodeType.NT_HANDLE)) { handle = node document = getDocument(handle) } if ((handle == null) || (document == null)) { log.debug "No handle/document can be determined from node ${node.path} of type ${node.primaryNodeType.name}" return false } String documentName = handle.name if (handle.isNodeType(HippoNodeType.NT_NAMED)) { documentName = handle.getProperty(HippoNodeType.HIPPO_NAME).string } if (document != null) { String primaryType = document.getProperty(JcrConstants.JCR_PRIMARY_TYPE).string String creator = "unknown" String creationDate = "unknown" String lastModifiedBy = "unknown" String lastModificationDate = "unknown" String publicationDate = "unknown" if (document.hasProperty(HippoStdPubWfNodeType.HIPPOSTDPUBWF_CREATED_BY)) { creator = document.getProperty(HippoStdPubWfNodeType.HIPPOSTDPUBWF_CREATED_BY).string } if (document.hasProperty(HippoStdPubWfNodeType.HIPPOSTDPUBWF_CREATION_DATE)) { creationDate = document.getProperty(HippoStdPubWfNodeType.HIPPOSTDPUBWF_CREATION_DATE).string } if (document.hasProperty(HippoStdPubWfNodeType.HIPPOSTDPUBWF_LAST_MODIFIED_BY)) { lastModifiedBy = document.getProperty(HippoStdPubWfNodeType.HIPPOSTDPUBWF_LAST_MODIFIED_BY).string } if (document.hasProperty(HippoStdPubWfNodeType.HIPPOSTDPUBWF_LAST_MODIFIED_DATE)) { lastModificationDate = document.getProperty(HippoStdPubWfNodeType.HIPPOSTDPUBWF_LAST_MODIFIED_DATE).string } if (document.hasProperty(HippoStdPubWfNodeType.HIPPOSTDPUBWF_PUBLICATION_DATE)) { publicationDate = document.getProperty(HippoStdPubWfNodeType.HIPPOSTDPUBWF_PUBLICATION_DATE).string } documents.add(new DocumentInfo(primaryType, documentName, handle.path, creator, creationDate, lastModifiedBy, lastModificationDate, publicationDate)) increaseDocumentTypeCount(primaryType) return true } return false } Node getDocument(final Node handle) { Node published = null Node unpublished = null NodeIterator it = handle.getNodes(handle.name) while (it.hasNext()) { Node variant = it.nextNode() if (variant.hasProperty(HippoStdNodeType.HIPPOSTD_STATE)) { String state = variant.getProperty(HippoStdNodeType.HIPPOSTD_STATE).getString() if (HippoStdNodeType.PUBLISHED == state) { published = variant } else if (HippoStdNodeType.UNPUBLISHED == state) { unpublished = variant } } } return (published != null) ? published : unpublished } void increaseDocumentTypeCount(final String docType) { Integer count = documentTypes.get(docType) documentTypes.put(docType, (count == null) ? 1 : ++count) } void logOutput() { final StringBuilder sb = new StringBuilder() if ((verbosity > 0) && (documents.size() > 0)) { if (verbosity > 1) { sb.append("Detailed document information (name, type, creator, creation date, last modified by, last modification date, publication date, path):\n") for (DocumentInfo info : documents) { sb.append(" ").append(info.name).append(", ") sb.append(info.primaryType).append(", ") sb.append(info.creator).append(", ") sb.append(info.creationDate).append(", ") sb.append(info.lastModifiedBy).append(", ") sb.append(info.lastModificationDate).append(", ") sb.append(info.publicationDate).append(", ") sb.append(info.path).append("\n") } } else { sb.append("Document information (name, type, path):\n") for (DocumentInfo info : documents) { sb.append(" ").append(info.name).append(", ") sb.append(info.primaryType).append(", ") sb.append(info.path).append("\n") } } } sb.append("DocumentsInfoScript summary: ").append(documents.size()).append(" documents found.\n") Iterator types = documentTypes.keySet().iterator() while (types.hasNext()) { String type = types.next() sb.append(" ").append(documentTypes.get(type)).append(" documents of type ").append(type).append("\n") } log.info(sb.toString()) } void destroy() { logOutput() } /** * Information about a found document. */ class DocumentInfo { String primaryType String name String creator String creationDate String lastModifiedBy String lastModificationDate String publicationDate String path DocumentInfo(final String primaryType, final String name, final String path, final String creator, final String creationDate, final String lastModifiedBy, final String lastModificationDate, final String publicationDate) { this.primaryType = primaryType this.name = name this.path = path this.creator = creator this.creationDate = creationDate this.lastModifiedBy = lastModifiedBy this.lastModificationDate = lastModificationDate this.publicationDate = publicationDate } } boolean undoUpdate(Node node) { throw new UnsupportedOperationException('Updater does not implement undoUpdate method') } }