Best Practices: XWiki Application and Modules Organization

Last modified by Pascal Bastien on 2015/02/09 09:29

This document is still a draft. Note that the current content also reflects the view of its authors and NOT the view of the XWiki development team. We'll need to agree with the content before we can consider any of these best practices.

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. 

It is therefore important that an application most often displays data from the current space.

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.

We will use throughout this document the module name 'MyModule' to show the naming conventions.

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

    The code space should be names by the module name suffixed by Code. In our example this would be: MyModuleCode

  • Data Space

    The default data space should be name by the module name. In our example this would be: MyModule

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

The class definition should be stored in the Code space: MyModuleCode.MyModuleClass

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.

A sheet page should be stored in the code space and start by the class name followed by the Sheet word: MyModuleCode.MyModuleClassSheet

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

A template page should be stored in the code space and start by the class name followed by the Template word: MyModuleCode.MyModuleClassTemplate

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. 

Translations should be stored in the code space in a page called Translations: MyModuleCode.Translations. Each language has its own translated wiki page.

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.title=Title
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

SkinExtensions should always be used on XWiki versions where they are available.

CSS should use a similar naming convention as translations. CSS can be stored either in skin files or in wiki pages.

.mymodule-sheet-p {
// 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.

  • If stored in Wiki pages the CSS page should be stored in the code space and suffixed with CSS: MyModuleCode.CSS or MyModuleCode.SubModuleCSS
  • If stored in skins the CSS page should be named by the module: mymodule.css

Javascript

SkinExtensions should always be used on XWiki versions where they are available.

Modules can provide Javascript to extend the functionnality of the wiki.

JS functions should be named by the module:

function mymodule-init() {
}
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.

  • If stored in Wiki pages the Javascript page should be stored in the code space and suffixed with JS: MyModuleCode.JS or MyModuleCode.SubModuleJS
  • If stored in skins the Javascript page should be named by the module: mymodule.js

Macros

Macros should be used extensively to modularize the code in the wiki. Macros can be stored in wiki pages or in skins files. 

  • If stored in Wiki pages the macros page should be stored in the code space and suffixed with Macros: MyModuleCode.Macros or MyModuleCode.SubModuleMacros
  • If stored in skins the macros page should be named by the module: mymodulemacros.vm

Macros should be named by the module and the submodule or page that uses it:

## comment explaining the macro
#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:

public class Groovy {
 def context;

public setContext(context) {
 this.context = context;
}

public sheetAdd(param1, param2) {
  return param1 + param2;
}
}

Groovy code can be instanciated in velocity using:

#set($mymodule = $xwiki.parseGroovyFromPage("MyModuleCode.Groovy"))
$module.setContext($context)
$module.sheetAdd($param1, $param2)

Groovy code should be stored in the code space and suffixed with Groovy: MyModuleCode.Groovy or MyModuleCode.SubModuleGroovy or MyModuleCode.PageGroovy

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:

#set($mymodule = $xwiki.mymodule)
$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.

Get Connected