I added some code to create a new PageRefRenameListener it
Purpose: on every page rename/move, sweep ALL XClass definitions in the wiki
for Page-type properties, and update any stored value that points at the
old page reference to point at the new one instead.
SETUP create ONE new page (e.g. “XWiki.PageRefRenameListener” or wherever
your other custom components/pages live), then add these XObjects to it:
Object 1 of 1 - XWiki.ComponentClass
Role (Interface) : org.xwiki.observation.EventListener
Role Hint : PageRefRenameListener
Scope : Wiki (current wiki only — change later if you run a farm)
Object 2 of 3 - XWiki.ComponentMethodClass (method: getName)
Method Name : getName
{{groovy}}
xcontext.method.output.value = "PageRefRenameListener"
{{/groovy}}
Object 3 of 3 - XWiki.ComponentMethodClass (method: getEvents)
Method Name : getEvents
{{groovy}}
import org.xwiki.refactoring.event.DocumentRenamedEvent
xcontext.method.output.value = \[new DocumentRenamedEvent()\]
{{/groovy}}
Object 4 of 3 - XWiki.ComponentMethodClass (method: onEvent)
Method Name : onEvent
{{groovy}}
import org.xwiki.model.reference.DocumentReference
import com.xpn.xwiki.objects.classes.PageClass
def event = xcontext.method.input.get(0)
DocumentReference oldRef = event.getSourceReference()
DocumentReference newRef = event.getTargetReference()
if (oldRef == null || newRef == null) {
return
}
def classDocs = services.query.hql(
"select distinct doc.fullName from XWikiDocument as doc where doc.xWikiClassXML <> ''"
).execute()
def pageFieldsByClass = \[:\] // className String -> List<String> propertyNames
for (className in classDocs) {
try {
def classDoc = xwiki.getDocument(className).getDocument() // raw XWikiDocument, needs PR
def xclass = classDoc.getXClass()
def pageProps = \[\]
for (propName in xclass.getPropertyList()) {
def propDef = xclass.get(propName)
if (propDef instanceof PageClass) {
pageProps.add(propName)
}
}
if (!pageProps.isEmpty()) {
pageFieldsByClass\[className\] = pageProps
}
} catch (Exception e) {
// skip any class doc we can't read rather than aborting the whole sweep
}
}
if (pageFieldsByClass.isEmpty()) {
return
}
def terminalName = oldRef.getName()
def effectiveName = terminalName.equalsIgnoreCase("WebHome") ? oldRef.getLastSpaceReference().getName() : terminalName
def likePattern = '%' + effectiveName + '%'
def classNames = pageFieldsByClass.keySet() as List
boolean mightHaveMatch = true
try {
def strHits = services.query.hql(
"select count(\*) from BaseObject as obj, StringProperty as prop " +
"where obj.className in (:cns) and prop.id.id = obj.id and prop.value like :val"
).bindValue('cns', classNames).bindValue('val', likePattern).execute()
long strCount = (strHits\[0\] as Long)
long listCount = 0
try {
def listHits = services.query.hql(
"select count(\*) from BaseObject as obj, DBStringListProperty as prop join prop.list as item " +
"where obj.className in (:cns) and prop.id.id = obj.id and item like :val"
).bindValue('cns', classNames).bindValue('val', likePattern).execute()
listCount = (listHits\[0\] as Long)
} catch (Exception ignoredListQueryError) {
listCount = 1 // can't verify this table on this install — assume a possible match
}
mightHaveMatch = (strCount > 0 || listCount > 0)
} catch (Exception ignoredPreFilterError) {
mightHaveMatch = true // pre-filter itself failed — never silently skip a real rename
}
if (!mightHaveMatch) {
return
}
for (entry in pageFieldsByClass) {
def className = entry.key
def propNames = entry.value
def holderDocs = services.query.hql(
"select distinct doc.fullName from XWikiDocument as doc, BaseObject as obj " +
"where obj.name = doc.fullName and obj.className = :cn"
).bindValue('cn', className).execute()
for (holderFullName in holderDocs) {
try {
def apiDoc = xwiki.getDocument(holderFullName)
def apiObjects = apiDoc.getObjects(className)
if (apiObjects == null) continue
boolean changed = false
for (apiObj in apiObjects) {
if (apiObj == null) continue
def obj = apiObj.getXWikiObject()
if (obj == null) continue
for (propName in propNames) {
def prop = obj.getField(propName)
if (prop == null) continue
def rawValue = prop.getValue()
if (rawValue instanceof List) {
def newList = \[\]
boolean listChanged = false
for (v in rawValue) {
def resolved = services.model.resolveDocument(v as String)
if (resolved != null && resolved.equals(oldRef)) {
newList.add(services.model.serialize(newRef, "compactwiki"))
listChanged = true
} else {
newList.add(v)
}
}
if (listChanged) {
obj.setStringListValue(propName, newList)
changed = true
}
} else if (rawValue instanceof String && rawValue.length() > 0) {
def resolved = services.model.resolveDocument(rawValue as String)
if (resolved != null && resolved.equals(oldRef)) {
obj.setStringValue(propName, services.model.serialize(newRef, "compactwiki"))
changed = true
}
}
}
}
if (changed) {
def rawHolderDoc = apiDoc.getDocument()
rawHolderDoc.setComment("Renamed back-links: updated Page-reference property after rename")
xwiki.getXWiki().saveDocument(rawHolderDoc, rawHolderDoc.getComment(), true, xcontext.getXWikiContext())
}
} catch (Exception e) {
// skip any holder doc we can't update rather than aborting the whole sweep
}
}
}
{{/groovy}}