Hi devs,
I’m opening this brainstorming so that we have an agreed direction for whoever picks it up. I don’t plan to work on it myself.
Note that I’ve drafted it quickly and there’s probably more to specify: it should be considered as a direction, to decide if we want to go there or not.
The problem
XWiki#getUniquePageName(space, name) (and $xwiki.getUniquePageName()) always runs the name through clearName(), which predates the page naming strategies. On a wiki with a naming strategy, code that transforms a name with the strategy and then asks for a unique name gets back a name that doesn’t comply with it.
Concrete case on xwiki.org (kebab case strategy with dots kept between digits, see Allowing dots in URLs on xwik.org and in the kebab-case page naming strategy): creating a blog post titled “XWiki 18.4.6 Released” when Blog.xwiki-18.4.6-released already exists gives Blog.xwiki-1846-released (dots stripped by clearName()).
The bug is tracked in Loading....
Other issues with this API:
- It’s in oldcore, on the
XWikiclass,String-based (space name + page name), and checks existence withspace + "." + pageName, which is wrong for nested spaces or names containing dots. - We have at least 2 other copies of the “append an index until it doesn’t exist” logic (
HeadingNameNamingCriterionandPageIndexNamingCriterionin refactoring). - It’s also misused to generate random ids (
extension.vm,RatingsMacros). - There’s no generic way to check whether an entity exists:
DocumentAccessBridge.exists(DocumentReference),XWiki.exists(...),WikiDescriptorManager.exists(wikiId), each in a different module.
Idea
- A generic existence checker in
xwiki-platform-model-api:@Role EntityReferenceExistenceCheckerwithboolean exists(EntityReference), one implementation per entity type (hint = entity type:document,page,space,attachment,wiki), each living in the module owning that entity (oldcore, wiki-default…).- A default (no hint) implementation dispatching on the reference type.
- Possibly exposed as
$services.model.exists(reference).
- A generic
UniqueEntityReferenceGeneratorinxwiki-platform-model-validation-api, e.g.<T extends EntityReference> T generate(T reference):- Transforms only the last segment’s name with the configured strategy (when transformation is enabled). Parents are kept as is, since
EntityNameValidation#transform(EntityReference)would also rewrite them. - Appends a suffix until the reference doesn’t exist, the suffix also complying with the strategy (e.g.
xwiki-18.4.6-released-1). - Rejects types for which a free-form unique name doesn’t make sense (objects, properties, blocks).
- Transforms only the last segment’s name with the configured strategy (when transformation is enabled). Parents are kept as is, since
- Deprecate
XWiki#getUniquePageName()and make it delegate to the generator. This way all existing callers get the fix without changes (Blog, Forum, Newsletter, Recruitment and about 10 other contrib extensions use it).
Open questions
- Who decides the suffix format (
-1,_1,1)? Maybe a defaultgetUniqueSuffix(int)method onEntityNameValidationthat each strategy can override. - For pages, should the generator produce a terminal (
Space.name) or a nested (Space.name.WebHome) reference? Maybe it just follows the type of the reference given. - Note that a space exists only if it contains at least one document, to be documented on the space checker.
Note that the naming strategy is meant to apply to attachment names too (see Loading...), so the generator should apply it to attachments as soon as that’s implemented. For wikis, I’d say it shouldn’t apply, since wiki ids have their own rules.
WDYT?
Thanks