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')
}
}