Best Practices: XWiki Application and Modules Organization
Introduction
This document has the objective of describing how to best write an XWiki Application or Module. It explains:
- The different parts that can make an XWiki Application
- How to organize the pages in spaces and how to name the pages
- Naming conventions for the different pages, the translations, the CSS
- Conventions for the Application documentation
An Application is made of several modules. What we will describe here is how to organize each of these modules.
General Organization
A module should separate its data and its code. Modules should be built to be able to have data in several XWiki spaces, which allows to instanciate the module multiple times.
The only exception would be for applications that would spread multiple spaces on purpose. However, it should be most often possible to handle this by having space level sheets showing only data from the current space and global sheets showing data from the whole Wiki.
Naming Conventions
Module Name
It is best to choose a module name between 5 and 10 characters. Your module name should represent your data well. The wiki module name is usually using capital letters. The module name used in the CSS or Javascript or Translations strings is in lower case.
It is very important to gather most of the logic code in the Code space. For this we should use macros and sheets (included using includeInContext).
Spaces
- Code Space
- Data Space
Classes
The classes should be stored in the code space and should be suffixed by 'Class'. The properties of the classes should be camel case. They should be written in english and represent their meaning as much as possible. The 'title' and 'name' property should be kept for a field that can be used as a title for the document. Examples:
- expireDate
- externalRef
There can be multiple classes per module.
Sheets
The sheets are used for coding the presentation of a document of your class. The template is a sample document that can be used to pre-initialize data in your documents using this class.
There is usually only one sheet per class.
A sheet should be coded only using the XWiki Public API. It should be made to work both in view more or in edit mode (inline). A good example of a sheet that uses many functions available (tooltips and validation) is available in this tutorial.
Templates
There is usally one template per class, but it is possible to have multiple templates, though this requires more user-interface work.
Translations
Translations are used to store strings to translate the user interface of your application.
The translations strings are each on one line and should be prefixed by the module name in lower case and by the sub-module or page name in which it is used:
mymodule.sheet.description=Description
mymodule.sheet.errormessage.cannotsave=The document {0} cannot be saved
Translations strings can contain wiki syntax or HTML which can be interpreted. You have to be extra carefull to not use wiki syntax or HTML without knowing it (for example < > & are special characters). Parameters named {0} {1} {2} can be used in translations.
CSS
CSS should use a similar naming convention as translations. CSS can be stored either in skin files or in wiki pages.
// etc..
}
#mymodule-sheet-title {
// etc..
}
When stored in wiki pages CSS should be surrounded by the pre and slash-pre macros that prevent wiki syntax to be executed in the content.
Javascript
Modules can provide Javascript to extend the functionnality of the wiki.
JS functions should be named by the module:
}
function mymodule-openwindow() {
}
etc..
JS has the same constraints as CSS files and need to be protected by pre and slash-pre when stored in wiki pages.
Macros
Macros should be used extensively to modularize the code in the wiki. Macros can be stored in wiki pages or in skins files.
Macros should be named by the module and the submodule or page that uses it:
#macro(mymodule_sheet_header $param1 $param2)
#end ## end macro sheet_footer
## comment explaining the macro
#macro(mymodule_sheet_footer $param1 $param2)
#end ## end macro sheet_footer
Groovy
Groovy should be used when more logic is needed. There can be one or multiple groovy classes in the module. If possible we should stick to one module. Functions should use the submodule or page in the function names when it makes sense.
Example code is:
def context;
public setContext(context) {
this.context = context;
}
public sheetAdd(param1, param2) {
return param1 + param2;
}
}
Groovy code can be instanciated in velocity using:
$module.setContext($context)
$module.sheetAdd($param1, $param2)
Plugins
Plugins are optional possibilities offered to developers of modules, as an alternative solution to groovy. Plugins should be named using the module name.
Plugins should be instanciated this way:
$module.sheetAdd($param1, $param2)
Comments and Documentation
We should document all pages in the wiki that contain code. For this we should use a standard documentation comment:
* This is page for foo
*
* @author XXX
* @progrights This page requires programming rights
*#
We should document macros:
* This is macro for foo
*
* @param footext Represents the footext
* @param secondargument Represents the second argument
* @author XXX
*#
These comments are modeled after javadoc and parsable by velocidoc. We will have to modify the source code to parse the wiki pages using velocidoc.