Sheet Module

Last modified by Sergei Kulagin on 2024/07/05 17:46

cogProvides an API to manage document and class sheets
TypeJAR
Category
Developed by

XWiki Development Team

Rating
0 Votes
LicenseGNU Lesser General Public License 2.1
Bundled With

XWiki Standard

Compatibility

XWiki Enterprise >= 3.2

Description

What is a sheet?

A sheet is an XWiki document that determines how the structured (objects) and non-structured (content) data from other XWiki documents is displayed. Sheets don't control the layout of the page: they specify how your data should be presented but not where to place the output on the page. The Velocity templates are responsible for the page layout. For instance the layout of the view page is specified in view.vm, not in a sheet; the sheet controls what is displayed in the title and the content area of the view page.

When a document is displayed with a sheet, the title and the content of the sheet are rendered in the context of the displayed document (as if the displayed document is the current document).

Class sheets

The most common use case for a sheet is to display the structured data stored in an object attached to an XWiki document. One way to do this is to bind the XWiki class that defines your structured data (the object type) to a sheet by adding an object of type XWiki.ClassSheetBinding to the class document and setting the sheet reference as the value of the 'sheet' property. For instance, by setting the value of the 'sheet' property to 'Example.YourSheet' you're saying: "apply Example.YourSheet whenever a document that has an object of this type is displayed". Obviously, you need edit rights on an XWiki class to be able to bind it to a sheet.

An example of a simple class sheet that displays all the property values of the first object of a specific type attached to the current document is:

{{velocity}}
#set($class = $doc.getObject('Example.YourClass').xWikiClass)
#foreach($property in $class.properties)
 ; $property.prettyName
  : $doc.display($property.getName())
#end
{{/velocity}}

Document sheets

Sometimes you might want to display a document differently than the rest of its type. For instance, you might want to overwrite the class sheet that is automatically applied because your document has an object of type Example.YourClass, which is bound to Example.YourSheet. In this case you can bind your document to a custom sheet by adding an object of type XWiki.DocumentSheetBinding to it and setting the sheet reference as the value of the 'sheet' property. Note that by leaving the 'sheet' property blank you are binding a document to itself. This basically means that the document will control its own display.

Another use case for document sheets is to display document meta data or informations that are not visible otherwise. For instance we can imagine a sheet that displays statistics about the displayed document. A real example of a document sheet is (even though the name is misleading) XWiki.ClassSheet which displays information about the XWiki class defined in the current document. Most of the class documents will have an object of type XWiki.ClassSheetBinding to specify the class sheet (used to display class instances) and an object of type XWiki.DocumentSheetBinding pointing to XWiki.ClassSheet (to display the class document itself).

Request sheet

In case you want to apply a sheet to an XWiki document only for one request you can use the sheet URL query string parameter. For instance /xwiki/bin/view/Space/Page?sheet=Example.Sheet forces Example.Sheet to be used instead of the sheet that would be normally used for viewing the Space.Page document. Obviously, you need view rights on the specified sheet in order to use it.

Note that the sheet specified on the request is applied only to the requested document and it isn't applied recursively. Take for instance the following sheet:

//
// Title
//
Test Sheet Title

//
// Content
//
{{velocity}}
$doc.getDisplayTitle()

$xwiki.getDocument('Space.Page').getDisplayTitle()
{{/velocity}}

{{display reference="Space.Page"/}}

No matter which document this sheet is applied to on the request (including Space.Page), the result is: the sheet title followed by the title and the content of Space.Page displayed using their default sheet.

Before 10.9 the request sheet is not taken into account in the following use cases:

  • the document contains an object XWiki.EditModeClass with a value different from edit
  • the document is xwiki/1.0 syntax and contains an #include

Action sheets

We can perform different actions on an XWiki document (view, edit, etc.) so it should be possible to write a different sheet per action (view sheet, edit sheet, etc.). For simple needs it's enough to write a single sheet that uses Document#display() method whose output depends on the current action. But if there is a big difference between how your documents look/behave in view and (inline form) edit mode (for instance) then it is probably better to write two different sheets instead of checking the current action inside a single sheet. To do this, you can bind your class/document to two/more different sheets and then add an object of type XWiki.SheetDescriptorClass to each of them, filling the 'action' property with the corresponding action.

User/Group sheets

A specific use case is to have different sheets per user or group. You can achieve this by binding your class/document to multiple sheets and then configuring view rights on them. For instance you can make Example.Sheet1 visible for XWiki.Group1 and not visible for XWiki.Group2, while Example.Sheet2 is visible for XWiki.Group2 and not visible for XWiki.Group1. When your document is displayed for a member of the first group Example.Sheet1 will be used.

Sheet resolution algorithm

1. Is there a sheet specified on the current request?
     Yes: Return it if it matches the current action, otherwise continue.
      No: Continue.
2. Is the displayed document explicitly bound to one or more sheets?
     Yes: Return those that match the current action, if there is at least one, otherwise continue.
      No: Continue.
3. For each type of object attached to the displayed page
     Is it bound to one or more sheets?
       Yes: Collect those that match the current action.
        No: Continue.
   Return the collected sheets (possibly an empty list).

The first sheet, from the list of sheets returned by this algorithm, that the current user has the right to view is applied. A sheet matches the current action if one of the following conditions is satisfied:

  • the sheet document doesn't have an object of type XWiki.SheetDescriptorClass
  • the sheet document has an object of type XWiki.SheetDescriptorClass whose 'action' property is blank.
  • the sheet document has an object of type XWiki.SheetDescriptorClass whose 'action' property matches the current action.

For developers

Inline form edit mode

An XWiki document can be edited in multiple modes. The edit mode is normally specified by the 'editor' URL query string parameter. For instance /xwiki/bin/edit/Space/Page?editor=object loads the object editor. When the editor is not specified on the edit URL, i.e. /xwiki/bin/edit/Space/Page, the display module looks for a sheet that matches the edit action. If it finds one then it uses that sheet to display the document in edit mode. This is called the 'Inline form' edit mode. You can force it by setting the 'editor' URL query string parameter to 'inline'.

Sheets and programming rights

Programming rights set on a sheet are preserved when the sheet is rendered in the context of the displayed document. This means that you can write a sheet using programming rights and use it for documents that don't have programming rights. At the same time, a sheet written by someone that doesn't have programming rights won't gain programming rights when used to display a document that has programming rights. This is achieved by rendering the sheet as if its content author is also the content author of the displayed document.

API

When writing XWiki applications it's better to not hard-code XWiki.ClassSheetBinding, XWiki.DocumentSheetBinding or XWiki.SheetDescriptorClass in your code. You can use instead the sheet service. Here's a Velocity example:

{{velocity}}
#set($className = 'Blog.BlogPostClass')
The list of class sheets bound to $className:
#set($classDocument = $xwiki.getDocument($className))
#foreach($sheetReference in $services.sheet.getClassSheets($classDocument))
 * $services.model.serialize($sheetReference)
#end
{{/velocity}}

You can find the full API reference here.

Backward Compatibility

XWiki applications using the old sheet system (based on the include macro and the XWiki.SheetClass/XWiki.EditModeClass) should continue to work side by side with other applications using the new system, without requiring any modifications, in most of the cases. There are a few edge cases though when small changes are needed:

  • $context.display is now 'edit' on edit action (was 'view' before). This affects the behavior of the Document#display() methods when the current action is edit. It shouldn't be a problem because applications that haven't been migrated to the new sheet system are using the inline action, not the edit action.

Migration

Here's a list of things you should do when migrating XWiki applications from the old sheet system to the new one:

  • Remove the object of type XWiki.SheetClass from your sheet documents.
  • Update the code of your sheets to use the edit action instead of the deprecated inline action. Look for:
    old: #if($context.action == 'inline')
    new: #if($context.action == 'edit')

    old: $doc.getURL('inline')
    new: $doc.getURL('edit', 'editor=inline')
  • Bind your sheets to your classes (read the Class sheets section).
  • Update the create forms to use the edit action instead of the inline action.
  • If you have documents that are displayed differently than the rest of their type (e.g. you have a blog post that should not be displayed using the blog post sheet) then you have to bind them to their custom sheet (read the Document sheets section).
  • Optionally, if you want to avoid future confusion, you can go through your data documents (documents having objects of your classes) and remove the include macro call from their content:
    {{include document="AppSpace.SomeClassSheet" /}}
    The sheet doesn't have to be manually included in each document anymore because it is automatically detected based on the type of objects attached to the document.

Get Connected