Wiki source code of Best Practices: XWiki Application and Modules Organization
Last modified by Pascal Bastien on 2015/02/09 09:29
Show last authors
author | version | line-number | content |
---|---|---|---|
1 | {{box cssClass="floatinginfobox" title="**Contents**"}} | ||
2 | {{toc/}} | ||
3 | {{/box}} | ||
4 | |||
5 | {{warning}} | ||
6 | 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. | ||
7 | {{/warning}} | ||
8 | |||
9 | = Introduction = | ||
10 | |||
11 | This document has the objective of describing how to best write an XWiki Application or Module. It explains: | ||
12 | |||
13 | * The different parts that can make an XWiki Application | ||
14 | * How to organize the pages in spaces and how to name the pages | ||
15 | * Naming conventions for the different pages, the translations, the CSS | ||
16 | * Conventions for the Application documentation | ||
17 | |||
18 | An Application is made of several modules. What we will describe here is how to organize each of these modules. | ||
19 | |||
20 | = General Organization = | ||
21 | |||
22 | 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. | ||
23 | |||
24 | {{warning}} | ||
25 | It is therefore important that an application most often displays data from the current space. | ||
26 | {{/warning}} | ||
27 | |||
28 | 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. | ||
29 | |||
30 | = Naming Conventions = | ||
31 | |||
32 | == Module Name == | ||
33 | |||
34 | 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. | ||
35 | |||
36 | {{info}} | ||
37 | We will use throughout this document the module name 'MyModule' to show the naming conventions. | ||
38 | {{/info}} | ||
39 | |||
40 | 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##). | ||
41 | |||
42 | == Spaces == | ||
43 | |||
44 | * Code Space | ||
45 | ((( | ||
46 | {{info}} | ||
47 | The code space should be names by the module name suffixed by Code. In our example this would be: MyModuleCode | ||
48 | {{/info}} | ||
49 | ))) | ||
50 | * Data Space((( | ||
51 | {{info}} | ||
52 | The default data space should be name by the module name. In our example this would be: MyModule | ||
53 | {{/info}} | ||
54 | ))) | ||
55 | |||
56 | == Classes == | ||
57 | |||
58 | 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: | ||
59 | |||
60 | * expireDate | ||
61 | * externalRef | ||
62 | |||
63 | {{info}} | ||
64 | The class definition should be stored in the Code space: MyModuleCode.MyModuleClass | ||
65 | {{/info}} | ||
66 | |||
67 | There can be multiple classes per module. | ||
68 | |||
69 | == Sheets == | ||
70 | |||
71 | 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. | ||
72 | |||
73 | {{info}} | ||
74 | A sheet page should be stored in the code space and start by the class name followed by the Sheet word: MyModuleCode.MyModuleClassSheet | ||
75 | {{/info}} | ||
76 | |||
77 | There is usually only one sheet per class. | ||
78 | |||
79 | 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>>platform:DevGuide.Creating a form with validation and tooltips]]. | ||
80 | |||
81 | == Templates == | ||
82 | |||
83 | {{info}} | ||
84 | A template page should be stored in the code space and start by the class name followed by the Template word: MyModuleCode.MyModuleClassTemplate | ||
85 | {{/info}} | ||
86 | |||
87 | There is usally one template per class, but it is possible to have multiple templates, though this requires more user-interface work. | ||
88 | |||
89 | == Translations == | ||
90 | |||
91 | Translations are used to store strings to translate the user interface of your application. | ||
92 | |||
93 | {{info}} | ||
94 | Translations should be stored in the code space in a page called Translations: MyModuleCode.Translations. Each language has its own translated wiki page. | ||
95 | {{/info}} | ||
96 | |||
97 | 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: | ||
98 | |||
99 | {{code}} | ||
100 | mymodule.sheet.title=Title | ||
101 | mymodule.sheet.description=Description | ||
102 | mymodule.sheet.errormessage.cannotsave=The document {0} cannot be saved | ||
103 | {{/code}} | ||
104 | |||
105 | 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. | ||
106 | |||
107 | == CSS == | ||
108 | |||
109 | {{warning}} | ||
110 | **[[SkinExtensions>>extensions:Extension.Skin Extension Plugin]] should always be used on XWiki versions where they are available.** | ||
111 | {{/warning}} | ||
112 | |||
113 | CSS should use a similar naming convention as translations. CSS can be stored either in skin files or in wiki pages. | ||
114 | |||
115 | {{code language="css"}} | ||
116 | .mymodule-sheet-p { | ||
117 | // etc.. | ||
118 | } | ||
119 | #mymodule-sheet-title { | ||
120 | // etc.. | ||
121 | } | ||
122 | {{/code}} | ||
123 | |||
124 | 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. | ||
125 | |||
126 | {{info}} | ||
127 | * If stored in Wiki pages the CSS page should be stored in the code space and suffixed with CSS: MyModuleCode.CSS or MyModuleCode.SubModuleCSS | ||
128 | * If stored in skins the CSS page should be named by the module: mymodule.css | ||
129 | {{/info}} | ||
130 | |||
131 | == Javascript == | ||
132 | |||
133 | {{warning}} | ||
134 | **[[SkinExtensions>>extensions:Extension.Skin Extension Plugin]] should always be used on XWiki versions where they are available.** | ||
135 | {{/warning}} | ||
136 | |||
137 | Modules can provide Javascript to extend the functionnality of the wiki. | ||
138 | |||
139 | JS functions should be named by the module: | ||
140 | |||
141 | {{code language="javascript"}} | ||
142 | function mymodule-init() { | ||
143 | } | ||
144 | function mymodule-openwindow() { | ||
145 | } | ||
146 | etc.. | ||
147 | {{/code}} | ||
148 | |||
149 | JS has the same constraints as CSS files and need to be protected by pre and slash-pre when stored in wiki pages. | ||
150 | |||
151 | {{info}} | ||
152 | * If stored in Wiki pages the Javascript page should be stored in the code space and suffixed with JS: MyModuleCode.JS or MyModuleCode.SubModuleJS | ||
153 | * If stored in skins the Javascript page should be named by the module: mymodule.js | ||
154 | {{/info}} | ||
155 | |||
156 | == Macros == | ||
157 | |||
158 | Macros should be used extensively to modularize the code in the wiki. Macros can be stored in wiki pages or in skins files. | ||
159 | |||
160 | {{info}} | ||
161 | * If stored in Wiki pages the macros page should be stored in the code space and suffixed with Macros: MyModuleCode.Macros or MyModuleCode.SubModuleMacros | ||
162 | * If stored in skins the macros page should be named by the module: mymodulemacros.vm | ||
163 | {{/info}} | ||
164 | |||
165 | Macros should be named by the module and the submodule or page that uses it: | ||
166 | |||
167 | {{code language="velocity"}} | ||
168 | ## comment explaining the macro | ||
169 | #macro(mymodule_sheet_header $param1 $param2) | ||
170 | |||
171 | #end ## end macro sheet_footer | ||
172 | |||
173 | ## comment explaining the macro | ||
174 | #macro(mymodule_sheet_footer $param1 $param2) | ||
175 | |||
176 | #end ## end macro sheet_footer | ||
177 | {{/code}} | ||
178 | |||
179 | == Groovy == | ||
180 | |||
181 | 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. | ||
182 | |||
183 | Example code is: | ||
184 | |||
185 | {{code language="java"}} | ||
186 | public class Groovy { | ||
187 | def context; | ||
188 | |||
189 | public setContext(context) { | ||
190 | this.context = context; | ||
191 | } | ||
192 | |||
193 | public sheetAdd(param1, param2) { | ||
194 | return param1 + param2; | ||
195 | } | ||
196 | } | ||
197 | {{/code}} | ||
198 | |||
199 | Groovy code can be instanciated in velocity using: | ||
200 | |||
201 | {{code}} | ||
202 | #set($mymodule = $xwiki.parseGroovyFromPage("MyModuleCode.Groovy")) | ||
203 | $module.setContext($context) | ||
204 | $module.sheetAdd($param1, $param2) | ||
205 | {{/code}} | ||
206 | |||
207 | {{info}} | ||
208 | Groovy code should be stored in the code space and suffixed with Groovy: MyModuleCode.Groovy or MyModuleCode.SubModuleGroovy or MyModuleCode.PageGroovy | ||
209 | {{/info}} | ||
210 | |||
211 | == Plugins == | ||
212 | |||
213 | Plugins are optional possibilities offered to developers of modules, as an alternative solution to groovy. Plugins should be named using the module name. | ||
214 | |||
215 | Plugins should be instanciated this way: | ||
216 | |||
217 | {{code}} | ||
218 | #set($mymodule = $xwiki.mymodule) | ||
219 | $module.sheetAdd($param1, $param2) | ||
220 | {{/code}} | ||
221 | |||
222 | == Comments and Documentation == | ||
223 | |||
224 | We should document all pages in the wiki that contain code. For this we should use a standard documentation comment: | ||
225 | |||
226 | {{code}} | ||
227 | #** | ||
228 | * This is page for foo | ||
229 | * | ||
230 | * @author XXX | ||
231 | * @progrights This page requires programming rights | ||
232 | *# | ||
233 | {{/code}} | ||
234 | |||
235 | We should document macros: | ||
236 | |||
237 | {{code}} | ||
238 | #** | ||
239 | * This is macro for foo | ||
240 | * | ||
241 | * @param footext Represents the footext | ||
242 | * @param secondargument Represents the second argument | ||
243 | * @author XXX | ||
244 | *# | ||
245 | {{/code}} | ||
246 | |||
247 | These comments are modeled after javadoc and parsable by [[velocidoc>>http://sourceforge.net/projects/velocidoc/]]. We will have to modify the source code to parse the wiki pages using velocidoc. |