Updating links (field type: Page) in AWM

Hi!

I have some trouble renaming pages: Usually XWiki updates links after renaming or removing a XWiki page and it marks broken links via CSS by adding a “?” symbol at the end of the link. Sometimes that does not work and so a have a broken link checker using:

$services.query.xwql("select link.id.link, link.fullName from XWikiLink link where link.id.link not in (select distinct doc.fullName from XWikiDocument doc)")

Renaming a page that is linked within a AWM field of type Page results in:

  1. The link in the field was not updated.
  2. It is correctly marked with the “?” as a broken link.
  3. My query does not list this kind of broken link(s).
  4. Besides: This kind of links in AWS are not listed as backlink under the information section, what would be great. I think this fact is part of the problem?

Any ideas how to fix this? Any ideas for a workaround?

Hi, this is indeed a current limitation: Page type xproperties are not taken into account when saving the links of a document: xwiki-platform/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java at e80dcb59f2f8347b2d3cd24ded3d260ceacf6a3b · xwiki/xwiki-platform · GitHub

(only textarea xproperties are checked).

I think your issue is Loading...

Thanks

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