Migration Guide to Nested Pages

Last modified by Vincent Massol on 2024/04/11

History

Executive Summary

Up to XWiki 7.1, there has been 2 ways to organize content in XWiki: 

  • Wiki > Space > Page (which includes rights inheritance and administration features) 
  • Page > Child Page > Child Page of child Page > ... (which is used for hierarchical navigation purposes) 

Note that both are independent: a page can have a parent Page in a different Space/Wiki than its own .

While this was working, we've found that it could create confusion for users, leading to questions such as: 

  • Should I create a sub-Wiki or a Space for my team? 
  • Which navigation should I put on my home page: list of Spaces or tree view of Pages? 
  • Can I set rights on a page that is under a space? 

At the same time, for a long time there have been discussions on the list about the future "new model" of XWiki, which would ideally include
Nested Spaces, offering the following features: 

  • Unified way to handle hierarchical navigation (Page > Sub-page > Sub-sub-page >...) 
  • Navigation reflected in URLs (.../PageA/PageB/PageC) 
  • Inheritance of access rights (rights on PageA apply to PageB and PageC, unless defined otherwise. Rights on PageB supersede those on PageA
    and also apply to PageC, and so on.) 

In the end, we took the opportunity to move from the previous way to this new way of organizing pages in XWiki 7.2.

In an ideal world, going from the old model to the new one would imply a total rewrite of the model. However, in order not to break retro-compatibility with many existing features and applications we had to keep the concepts of "Page" and "Space" in XWiki while adapting them to emulate the Nested Pages feature. Here's what we've done to achieve this by default: 

  • We've hidden the parent-child feature

    Even though it's hidden you can still turn it back on if you need it while you migrate your content to use Nested Spaces: set the core.hierarchyMode property to parentchild in the xwiki.properties configuration file.

  • When creating a new page, the system automatically creates Page.WebHome, which creates a Space, though this is hidden in the URL (you only see .../Page/) 
  • For advanced users, we added the ability to create "Terminal Pages" which are pages that are not Spaces (as before). This is meant mostly for Application creators.

Detailed Explanations

Before the introduction of the Nested Pages concept (in 7.2), the content in a wiki was organized like this:

  • we had some Spaces, that you can conceptually consider like file system "folders"
  • inside these spaces, we had Pages, where you were able to write content or store applications entries.

At that time, we couldn't have spaces inside spaces, so all pages were placed inside a top-level space. The pages were identified like this: NameOfTheSpace.NameOfThePage.

Some pages were a bit special. They were the "home" pages of a space. Every time a user wanted to reach a space without specifying a precise page name, he'd be redirected to that space's home page. These pages were called "WebHome" (because at the very beginning of XWiki, "spaces" were called "web").

Parent/Child relationship

The user was able to specify a parent page for each page he had created. This parent could have been any page, for example the page Movies.ItsAWonderfulLife could have Directors.FrankCapra as parent. Then ItsAWonderfulLife was listed as Child of FrankCapra. This relationship was displayed in the breadcrumb at the top of each page. The user was able to navigate through this hierarchy

Some applications used this parent/child relationship to organize their data. For them, this relationship is crucial.

The introduction of the Nested Pages concept

In XWiki 7.2, due to frequent users demands, we have introduced the ability to create spaces under other spaces. It's the Nested Spaces concept. We suddenly had the ability to create a hierarchy of spaces to organize content.

But we have also found some issues. What if a space has the same name as a page? Which entry should we render to the user when he reaches an URL where two entities match? Moreover, does it make sense to have a difference between a space and a page? Why couldn't pages have sub-pages inside of them?

We then have decided to switch to the Nested Pages concept. In theory, a page should now be able to have some sub-pages, because we don't need to have a distinction between a page and a space like we have between a file and a folder.

But this change was not possible to make at the API level because it would have required too much work that we could not afford at this time (and breaking backward-compatibility incidentally since it would have required a complete model change). So the decision was made to stick to the Nested Spaces implementation at the API level (for now), but to propose Nested Pages at the UI level.

How does it work?

Every time a user creates a page from the XWiki UI, he now actually creates a space (without knowing it emoticon_wink) with its WebHome page. It means that all user-created page are called WebHome

Then, when the user wants to create a sub-page, for example FrankCapra inside the page Directors.WebHome, he actually creates a new space inside the space Directors with its WebHome page: Directors.FrankCapra.WebHome. That's how we emulate it.

The notion of space has been removed from the UI. It's not mentioned anywhere, anymore. Everything seems to work as if we were really creating sub-pages, even if under the hood, we create sub-spaces.

That emulation should be temporary. At some point we'd like to refactor the core model of XWiki and discard the concept of spaces to only keep pages. Don't hold your breath though, this may be several years in the coming emoticon_wink

Note that users still have the ability to create pages that are not named WebHome. As a consequence, such pages (that are not the "WebHome" of their space) cannot have sub-pages. They're called Terminal Pages.

Issue with the Parent/Child relationship

Now, it is clear that we have 2 different hierarchies in the wiki. One, made of sub-pages (sub-spaces in the reality), and one made with the old parent/child relationship. Then, clearly, it becomes confusing for the user. Which hierarchy should be displayed in the breadcrumb? What is the parent of page, the "container" page or the parent set in the field?

The choice has been made to drop the notion of parent/child relationship, since we can now really have children pages, implemented as sub-pages (with inherited rights, preferences, etc...).

It means that when the user wants to change the parent of a page, he actually needs to move this page under the new parent.

This concept is good in theory. But, how can you migrate existing content when you upgrade to XWiki 7.2+?

Migration

The idea is to convert your existing pages (most of them are terminal pages) into WebHome pages under their own space. We call this "convert terminal pages to nested pages". This will then allows you to create sub-pages under any converted pages, which is impossible while they are terminal pages.

But that would be a shame to lose the parent/child relationships that the users have created between their pages. The users might want to preserve their carefully-crafted hierarchies. You might still want to have FrankCapra as parent of ItsAWonderfulLife.

To keep this information, the principle is to move all the pages under their parents, so the parent/child links becomes a real hierarchy between pages.

example.svg

To perform this conversion, we have developed an application called the Nested Pages Migrator Application.

Fix the hierarchy

However, your current page hierarchy may be messy. For example, some pages inside the space Proposal could have Main.WebHome as parent instead of Proposal.WebHome which would be more logical. It happens when the user is invited to create pages in the space Proposal directly from the main page.

It's important to identify such cases before applying the migration tool, because the tool won't be able to detect this kind of unwanted hierarchies.

In the previous example, the solution was to put manually the parent of all pages of the Proposal space that had Main.WebHome as parent, to Proposal.WebHome. The following script is an example that can be used to migrate (you'll need to adapt it to your own needs):

#set ($xwql = "where doc.space in ('Proposal', 'Design', 'Improvements') and doc.parent = 'Main.WebHome'")
#foreach($r in $services.query.xwql($xwql).execute())
 #set ($d = $xwiki.getDocument($r))
 #set ($discard = $d.setParent('Proposal.WebHome'))
 #set ($discard = $d.save())
  * $r updated
#end 

Nested Pages Migrator Application

To use the migrator application, you first need to upgrade your XWiki instance to a recent version (7.4.2+). Indeed, the migrator cannot create nested pages in an old XWiki instance where this concept did not exist!

  • We absolutely recommend making a backup of your instance and of your databases. The migrator will modify a lot of existing pages and these changes cannot be reverted. If something goes wrong, the only solution will be to re-install your backup.
  • Users should be invited to not change the wiki content during the use of the migrator.
  • Nothing prevents another administrator to run the migrator at the same time as you, which could have bad consequences (collisions of actions, etc...). You should make sure that you are the only person using this tool during all the time of the process.

Go to the Extension Manager in your wiki, and install the Nested Pages Migrator Application. Then go to the page NestedPagesMigration.WebHome.

You will find the following page:

Migrator1.png

There are 3 actions:

  • compute a plan: the migration first generate a plan of actions to perform. You can see what the migrator proposes to do and disable some actions, or modify the options, for example. 
  • execute the plan: when a plan has been computed and if you agree with its content, you can execute the plan: the pages will be moved as proposed.
  • detect breakages (since 0.7): detect the hierarchies that are broken if you don't run the migrator. Read the migrator page to have more information.

Options

But first, let's talk about the options.

OptionDescriptionDefault value (recommended)
Exclude hidden pagesCheck this option if you want to exclude hidden pages, usually containing technical data, from the migration.true
Exclude pages having a classCheck this option to not convert pages holding an xclass, because it can break applications based on these xclasses.true
Do not move childrenConvert pages into Nested Pages (so they can have children) without moving them under their parent. The old hierarchy will not be preserved, but the rights and the preferences will remain the same.false
Add redirectionAdd a redirection at the old location. So when a user goes to the old URL, he will be automatically redirected to the new one. It's good if you don't want to break bookmarks, for example.true
Convert preferencesBy moving pages under their parents, the inherited preferences might change. If this option is enabled, the migrator will copy the old inherited preferences to the new location.true
Convert rightsBy moving pages under their parents, the inherited rights might change. If this option is enabled, the migrator will try to set the same rights at the new location.

Unfortunately, this operation is complex, and the current algorithm is a bit buggy at the moment. This option should be used for debug reasons.

You can still compute a plan with this option without executing it: the plan will list the locations were some rights will be modified, and then you should be able to find a solution for them manually.
 
false
Exclude classesExclude pages holding some objects.A list of classes that we have identified to be excluded. It's not complete, you should add the xclasses of your own applications!
Exclude pagesExclude a list of pages.A list of pages that we have identified to be excluded.
Excluded spacesExclude a list of spaces.A list of spaces that we have identified to be excluded.
Included spacesOnly pages containing in this list of spaces will be convertedempty

Note: you can select the classes to exclude by clicking on the "excluded classes" field:

excludedClasses.png

Plan

Once the options have been carefully set, you can start the computation of a plan. This operation usually lasts about 10 seconds, but it could be more depending of the number of the pages and the complexity of the preferences, rights, and hierarchies. The logs of the operation are displayed in the page. If error happens, you will have some error entries in the logs.

While the plan is computed, nothing is changed in your wiki. It's safe.

This is what a plan looks like:

plan.png

A plan is a tree of actions to perform in order to convert your pages. The tree is ordered as the resulting hierarchy would look like. If you click on the page name of each action (in bold), you will open the tree and see the children actions. You can close the tree by clicking again the page name.

Each line displays the target document (the new location of the page) and the original page (the current location of the page). It is displayed like this:

NewLocation.WebHome from Old.Location

You can click on the old location to open the page (in a new window). It could be useful to look at the page you are about to move.

Some actions have the "(unchanged)" mention. It means the page will not be moved. They are present in the tree only to display their children when some of them have changed.

Each action has a checkbox. Only checked actions will be performed. So if you notice some changes that you don't want to perform, you can easily uncheck them. When you uncheck an action, it also affects its children. But you can also select the children one by one if you need to.

Other actions are available:

  • exclude page: click on this button to add the page to the exclude list (in the options). The plan might be recomputed.
  • exclude space: click on this button to add the space of that page to the exclude list (in the options). Then plan might be recomputed.
  • set parent: click on this button to manually set a parent for that page. Note: this action will be performed immediately, not when the plan will be executed. It is a shortcut to help you changing the parent without having to navigate to the page.

Preferences

Some actions can contains preference changes. You can check or uncheck any change one by one. If they are checked, the preferences will be set to the new location. The original preference is also mentioned. When a preference line is present, it means that the preferences of the new location are not the same than the preferences of the old location.

You can either let the migrator set the new preferences according to the plan, or manually modify some preferences before or after the migration. In case you modified them before, you should restart the computation of the plan.

Rights

Same as preferences, but for rights. 

Remember, the current application is a bit buggy at the moment and you should not apply these changes!

However, you can use this plan to identify where rights will be changed by the migration. Indeed, even if the proposed actions to fix them are bad, the algorithm is able to identify where rights problems happen. Save this list and you will be able to fix the rights issues by yourself after the migration.

Execute the plan

When you are ready, you can execute the plan.

PlanExecuting.png

  • Be patient. The process could be very long. It will use a lot of server resources, so we recommend to don't let users access the wiki during the process. (For example, you can temporary forbid remote connections to your servlet container and access XWiki through an SSH tunnel for yourself.)
  • Since all logs are displayed in the page, even the browser can became slow. Note that we have experienced better performances using Chrome instead of Firefox.
  • Some OutOfMemory errors could happen. To avoid them, we recommend to give a lot of memory to your XWiki instance during the execution of the migration.
  • To minimize migration time, we recommend that you ensure that the Solr queue is empty before starting the migration. You can check Solr queue on the Wiki administration>>Solr Search Section, see documentation.

We highly recommend to perform migration tests on a staging server before doing it on a production instance.

What else?

Unfortunately, running the migrator is not enough. This is a list of actions that you need to perform:

  • Since the pages have been renamed, pages doing {{include reference="..."/}} or {{display reference="..."/}} might not work. You should identify them and fix them.
  • If you use the "SpaceDocs" or the "Spaces" panels, you should replace them by "Navigation", "Siblings" and "Children Panels". XWiki 16.3.0+ These 2 panels have been removed
  • Some existing code may break slightly and may need to be adapted: code taking some user input and creating Spaces based on that will most likely display "\.", "\:" and "\\" in the UI. Unless the code already cleans the user input and removes ".", ":" and "\"characters. So for example if a user enters a Space name of "my.space":
    • before 7.2: the code would create/display a Space named "my.space"
    • after 7.2: the code will create/display a Space named "my\.space"
  • Various XWiki APIs that used to return a space name have been returning a space reference string (i.e. list of spaces separated by dots and escaped "\.", "\:" and "\\" characters). Code using those API may need to be updated. See XWiki 7.2 release notes for more details (this content could be copied in a section of the migration guide).

Get Connected