Working with the Shadow Space
This will be a feature of the Enterprise Theme 5.1
The ShadowSpaceService
provides basic functionality for other parts of the theme to store and retrieve information in the form of pages in the Bitvoodoo Enterprise Theme support space (also known as "the shadow space"). The canonical example for a shadow space client is the section configuration feature which stores customized section content in the shadow space.
Terminology
Term | Description |
---|---|
shadow space | Also known as the "Bitvoodo Enterprise Theme support space", this is the archived space created and maintained by the |
shadow page | A page stored in the shadow space and managed by the |
shadow path | A virtual path used to identify shadow pages. E.g. the customized content for the global gu-sidebar-page section has the shadow path ["sections", ":global:", "gu-sidebar-page"] which, just like a file system path indicates a hierarchical relationship among the path segments from left to right. |
shadow page editor | A customized implementation of the Confluence page editor that is used to edit shadow pages. |
Shadow Paths and organization of Shadow Pages
Since a page ID is only known after the page has been created, the shadow space service uses shadow paths instead to identify pages. The shadow path represents a virtual hierarchical path in the shadow space in order to avoid conflicts. While it would technically be possible to store the shadow pages in the same hierarchical fashion (child pages) than the shadow path concept implies, this would have made the implementation of the shadow page management quite a bit more complex and was deemed unnecessary.
Instead, the shadow pages are all stored "flat" in the shadow space and a stringyfied version of the shadow path is used as the page title. For example, the shadow path with the segments ["sections", ":global:", "gu-sidebar-page"] results in a shadow page with the title "sections_:global:_gu-sidebar-page". The point here is that the implementation detail of how exactly the shadow pages are stored is irrelevant; all that matters is the ShadowPath
which is used as the ID / handle for a shadow page.
Permission Checks
While everybody can view content in the shadow space defined by the permission settings of the space itself, creating and editing shadow pages is a completely different matter. With the custom shadow page editor it is possible for clients of the shadow space service to provide custom permission checkers based on the first segment of the shadow path. For example, the section service registers a custom permission checker for all shadow paths that start with the segment "sections".
Registering a new permission checker is as easy as
ShadowSpacePermissionCheckFactory.register("sections", new SectionPermissionCheck(permissionManager, spaceManager));
and the implementation of the permission checker itself can be as simple or complex as necessary. The section permission checker is quite simple; it doesn't distinguish between create and edit, and it just checks if the user is space admin or global Confluence admin:
public class SectionPermissionCheck implements ShadowSpacePermissionCheck {
private final PermissionManager permissionManager;
private final SpaceManager spaceManager;
public SectionPermissionCheck(PermissionManager permissionManager, SpaceManager spaceManager) {
this.permissionManager = permissionManager;
this.spaceManager = spaceManager;
}
@Override
public boolean hasCreatePermission(User user, ShadowPath path) {
SectionHandle handle = new SectionHandle(path);
if (handle.isGlobal()) {
return permissionManager.isConfluenceAdministrator(user);
} else {
Space space = spaceManager.getSpace(handle.getSpaceKey());
return space != null && permissionManager.hasPermission(user, Permission.ADMINISTER, space);
}
}
@Override
public boolean hasEditPermission(User user, ShadowPath path, Page page) {
return hasCreatePermission(user, path);
}
}
Every feature of the Enterprise Theme that stores content in the shadow space should define its own initial shadow path segment and permission checker.
Content Processors
Another feature of the shadow page editor is its capability to preprocess content before it is stored in the shadow space. This preprocessing is done on the level of Confluence XHTML markup and a processor can theoretically do almost anything to transform the content before it is stored. Again using the one example that currently exists, the AbsoluteAttachmentProcessor goes through the content and making attachment references absolute (explicitly pointing to the current page and the shadow space key) because otherwise the relative attachments would not work when the shadow page content is rendered in a different context (e.g. customized section content that may be rendered in the context of any other page and space).
Creating a new processor is as simple as
Create a new class implementing the
ShadowSpaceContentProcessor
interface.Add the processor to the list of processors in the constructor of the
EditShadowPageAction
class.Implement the processor interface to perform the necessary transformation
Currently, there is only one list of processors for all shadow pages. In the future it might be necessary to refactor this to allow different sets of processors based on the shadow path, similar to the permission checkers.