Wiki source code of Best Practices

Last modified by Vincent Massol on 2017/09/05

Hide last authors
Manuel Smeria 6.2 1 {{box cssClass="floatinginfobox" title="**Contents**"}}
Silvia Macovei 3.2 2 {{toc/}}
Manuel Smeria 6.2 3 {{/box}}
4
Vincent Massol 6.1 5 = Where to put code? =
Vincent Massol 1.1 6
Manuel Smeria 6.2 7 Since xwiki allows you to put code both in wiki pages and in Java you might wonder where you should put your code. Here are some general guidelines:
8
Vincent Massol 6.1 9 * Don't put "business logic" code in wiki pages. Use Java for that. This gives you nice IDEs, the ability to easily debug the code and the ability to write automated unit tests. Generally speaking it makes it easy on maintenance.
Vincent Massol 8.3 10 * In general put the minimum amount of scripts in your wiki pages since that makes them harder to maintain.
11 * The only scripts that you should put in wiki pages (and not in Java code!) are "presentation logic" scripts, i.e. scripts in charge of presenting the data retrieved by using the Java/REST APIs.
Vincent Massol 6.1 12
Vincent Massol 8.3 13 Said differently you should use the [[MVC>>http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller]] pattern by separating your Model (what we called "business logic" above) from your View (what we called "presentation logic" above).
Vincent Massol 6.1 14
Silvia Macovei 3.2 15 = XWiki Application Organization =
Vincent Massol 1.2 16
Vincent Massol 8.1 17 * [[XWiki Development Team best practices>>dev:Community.ApplicationDevelopmentBestPractices]]
Ecaterina Moraru (Valica) 11.1 18 * [[Best Practices XWiki Application Organization>>dev:Drafts.Best Practices XWiki Application Organization]] (proposed by Ludovic Dubost)
Ludovic Dubost 2.1 19
Silvia Macovei 3.2 20 = Check for Object existence in Class Sheets documents =
Vincent Massol 1.1 21
Vincent Massol 1.3 22 Class sheet documents should be written using the following construct (this is an example for displaying documents containing XWiki.XWikiUsers objects):
Vincent Massol 1.1 23
Eduard Moraru 5.1 24 {{code language="velocity"}}
Vincent Massol 1.5 25 #set($obj = $doc.getObject("XWiki.XWikiUsers"))
Vincent Massol 1.4 26 #if(!$obj)
27 1 User Sheet
Vincent Massol 1.3 28 This stylesheet must be applied on a document containing a XWiki.XWikiUsers object.
Vincent Massol 1.4 29 #else
30 1 $msg.get("userProfile", [$xwiki.getUserName($doc.fullName, false)])
Vincent Massol 1.3 31 ...
Vincent Massol 1.4 32 #end
Silvia Macovei 3.1 33 {{/code}}
Vincent Massol 1.1 34
Eduard Moraru 5.1 35 {{info}}
Eduard Moraru 8.2 36 The 'if' tests first for the non existence. This is so that XWiki can extract the title from the //1 User Sheet//, which is a proper title to display when viewing the sheet page, instead of the computed name which will usually display something wrong.
Eduard Moraru 5.1 37 {{/info}}
Eduard Moraru 4.1 38
39 = Handling errors when using xredirect for non-Javascript UIs =
40
41 When writing an UI with JavaScript, AJAX takes care of forwarding your action to a background service replying with success or with an error that is then displayed to the user in the same page.
42
43 Without JavaScript, we usually use the xredirect query parameter to specify the current page (and state) to which we want to come back after performing an action (by pressing a button, link, submitting a form, etc.).
44
45 One common problem when writing UIs without JavaScript in this way is error handling. In other words, how do you handle the situation when the service that you use to perform your action throws an error?
46
47 A simplified code for this in the background service that produces the error is:
48
Eduard Moraru 5.1 49 {{code language="velocity"}}
Eduard Moraru 4.1 50 #handleRequest($success)
51 #if ($success)
Eduard Moraru 5.2 52 #if ($request.action == 'get' || $request.xpage == 'plain')
Eduard Moraru 4.1 53 ## JavaScript call here.
54 Action was successful.
55 #elseif ("$!request.xredirect" != '')
56 ## No-JavaScript here. Redirect.
57 $response.sendRedirect($request.xredirect)
58 #end
59 #else
60 #if ($request.action == 'get' || $request.xpage== 'plain')
61 ## JavaScript call here.
62 Action was NOT successful.
63 $response.setStatus(403)
64 #elseif ("$!request.xredirect" != '')
65 ## No-JavaScript here. What now!? Redirect?
66 #handleErrorHere($request.xredirect)
67 #end
Denis Gervalle 8.4 68 #end
Eduard Moraru 4.1 69 {{/code}}
70
71 The idea is that you want to pass the error message to the UI but you don`t have a clear way of doing it, like you have for AJAX calls (response code and response text). A solution is to use the Session in order to pass your error message. You set the error in the service and, in the UI, you read and remove it so that it is only displayed once.
72
73 For the background service, it translates to:
74
Eduard Moraru 5.1 75 {{code language="velocity"}}
Eduard Moraru 4.1 76 ...
77 #elseif ("$!request.xredirect" != '')
78 ## No-JavaScript here. Redirect and forward error message.
Eduard Moraru 5.2 79 #set ($errorMessageKeyPrefix = "myModule.error.")
Eduard Moraru 4.1 80 $request.session.setAttribute("${errorMessageKeyPrefix}${request.xredirect}", 'Action was NOT successful')
81 $response.sendRedirect($request.xredirect)
82 #end
83 ...
84 {{/code}}
85
86 On the UI side:
87
Eduard Moraru 5.1 88 {{code language="velocity"}}
Eduard Moraru 4.1 89 ...
90 #set ($xredirect = $doc.getURL($context.action, $!{request.queryString}))
Eduard Moraru 5.2 91 #set ($errorMessageKeyPrefix = "myModule.error.")
Eduard Moraru 4.1 92 #set ($errorMessage = $request.session.getAttribute("${errorMessageKeyPrefix}${xredirect}"))
93 #if ("$!errorMessage" != '')
94 ## Clean the error and display the message.
95 #set ($discard = $request.session.removeAttribute("${errorMessageKeyPrefix}${xredirect}"))
96 {{error}}$errorMessage{{/error}}
97 #end
98 ...
99 {{/code}}
100
101 Note that using xredirect's value as session key (prefixed or not) is a good idea because:
102
Eduard Moraru 5.1 103 1. it's already there in both the UI (for sending it as parameter) and the background service (received as parameter)
104 1. it acts like a namespace, ensuring that the error will only be displayed for the current page/request.
105
Eduard Moraru 5.2 106 Using a prefix as above allows you to have multiple components (wiki macros, gadgets, etc.) in the same page using the same mechanism without collisions.
107
Eduard Moraru 4.1 108 This method works together with the whole purpose for which we are doing the redirect in the first place (so that the user can refresh the page without re-sending the action or re-posting a form), ensuring that after the first display, on a refresh, the error goes away.

Get Connected