Wiki source code of InvitationCommon
Last modified by Ecaterina Valica on 2019/03/27 14:32
Show last authors
author | version | line-number | content |
---|---|---|---|
1 | {{velocity}} | ||
2 | #* | ||
3 | * Invitation Application | ||
4 | * This document contains common macros used by all documents in the Invitation Application. | ||
5 | * | ||
6 | * Macros in this script don't rely on any variables except those which are passed to them and the following: | ||
7 | * | ||
8 | * $doc the com.xpn.xwiki.api.Document object representing the document containing this code. | ||
9 | * $msg the internationalization message provider containing a get(String) and a get(String, List) function | ||
10 | * $xcontext the com.xpn.xwiki.Context object for this request | ||
11 | * $xwiki an object of the com.xpn.xwiki.api.XWiki class. | ||
12 | * $escapetool an object of class org.apache.velocity.tools.generic.EscapeTool | ||
13 | * $util used to get a newline character ($util.getNewLine()) | ||
14 | * | ||
15 | * No macros in this script depend on any other macros. | ||
16 | *### | ||
17 | ## | ||
18 | #if($request.getParameter('test') == '1') | ||
19 | {{info}}testLoadInvitationConfig{{/info}} | ||
20 | #testLoadInvitationConfig() | ||
21 | #elseif($doc.documentReference.name == 'InvitationCommon') | ||
22 | {{info}}$services.localization.render('xe.invitation.internalDocument', ["${doc.getSpace()}.WebHome"]){{/info}} | ||
23 | #end | ||
24 | ## | ||
25 | #* | ||
26 | * Display a message for the sender (preview) or for the admin (review sent messages) | ||
27 | * | ||
28 | * $mail (XObject representing email message) the message to view | ||
29 | * | ||
30 | * $recipients (List<String>) (Optional) the list of email addresses to override the list provided by $mail | ||
31 | * used for preview because createMailFromTemplate will exclude addresses | ||
32 | * which are invalid and we want to show the invalid addresses to the | ||
33 | * user to tell them that they need to correct them. | ||
34 | * | ||
35 | * $invalidAddresses (List<String>) (Optional) List of invalid email addresses in recipients. Needed if you want to | ||
36 | * show the user that they made a mistake on some addresses. | ||
37 | * For each email in $invalidEmails, if it is also in $recipients, then it | ||
38 | * will be marked but left in the same order, emails in $invlaidEmails but | ||
39 | * not found in $recipients will end up at the end of the list. | ||
40 | *### | ||
41 | #macro(displayMessage, $mail, $recipients, $invalidAddresses) | ||
42 | |||
43 | {{html wiki=false clean=false}} | ||
44 | <div id="invitation-displaymessage" class="invitation"> | ||
45 | <strong>$services.localization.render('xe.invitation.previewLabel')</strong> | ||
46 | <div class="invitation invitation-preview"> | ||
47 | #set($recips = []) | ||
48 | #set($invalid = []) | ||
49 | ## get the lists of valid and invalid email addresses. | ||
50 | #if("$!invalidAddresses" == '') | ||
51 | #set($invalid = []) | ||
52 | #if("$!recipients" == '') | ||
53 | #set($recips = [$mail.getProperty('recipient').getValue().trim()]) | ||
54 | #else | ||
55 | #set($discard = $recips.addAll($recipients)) | ||
56 | #end | ||
57 | #else | ||
58 | ## Set to local variables to prevent altering input values. | ||
59 | #set($discard = $recips.addAll($recipients)) | ||
60 | #set($discard = $invalid.addAll($invalidAddresses)) | ||
61 | #end | ||
62 | ## Print the email addresses to be sent to. | ||
63 | ## To: | ||
64 | <strong>$services.localization.render('xe.invitation.toLabel')</strong> | ||
65 | <div id="preview-to-field" class="invitation-preview field"> | ||
66 | #foreach($recip in $recips) | ||
67 | #if($invalid.contains($recip)) | ||
68 | <span class="invalid-address">$!escapetool.xml($!recip)</span> ## | ||
69 | #set($discard = $invalid.remove($recip)) | ||
70 | #else | ||
71 | <span class="valid-address">$!escapetool.xml($!recip)</span> ## | ||
72 | #end | ||
73 | #end | ||
74 | #foreach($recip in $invalid) | ||
75 | <span class="invalid-address">$!escapetool.xml($!recip)</span> ## | ||
76 | #end | ||
77 | ## used to make the field the correct size if it's empty. | ||
78 | </div> | ||
79 | ## Tell the user that some of the email addresses are invalid. | ||
80 | #if($invalidAddresses && $invalidAddresses.size() > 0) | ||
81 | <p class="invalid-address-message"> | ||
82 | <span class="errormessage"> | ||
83 | #if($recips.size() == 1) | ||
84 | ## The email address given is invalid and will not be sent to. | ||
85 | $services.localization.render('xe.invitation.displayMessage.theAddressIsInvalid') | ||
86 | #else | ||
87 | #if($invalid.size() > 1) | ||
88 | $services.localization.render('xe.invitation.displayMessage.someAddressesAreInvalid', [$invalidAddresses.size()]) | ||
89 | #else | ||
90 | $services.localization.render('xe.invitation.displayMessage.anAddressesIsInvalid') | ||
91 | #end | ||
92 | #end | ||
93 | </span> | ||
94 | </p> | ||
95 | #end | ||
96 | ## Subject: | ||
97 | <strong>$services.localization.render('xe.invitation.subjectLabel')</strong> | ||
98 | <div id="preview-subjectline-field" class="invitation-preview field"> | ||
99 | $escapetool.xml($mail.getProperty('subjectLine').getValue()) | ||
100 | </div> | ||
101 | ## Message: | ||
102 | <strong>$services.localization.render('xe.invitation.contentLabel')</strong> | ||
103 | <div id="preview-messagebody-field" class="invitation-preview field"> | ||
104 | $mail.getProperty('messageBody').getValue() | ||
105 | </div> | ||
106 | </div> | ||
107 | </div> | ||
108 | {{/html}} | ||
109 | |||
110 | #end | ||
111 | ## | ||
112 | #* | ||
113 | * Load the configuration. | ||
114 | * Only works if the script calling this macro has permission to view InvitationConfig | ||
115 | * (or create it if it doesn't exist) | ||
116 | * | ||
117 | * $config (Map<String, String>) will be populated with invitation configuration. | ||
118 | * | ||
119 | * $configDocName (String) (Optional) will load configuration from this document, if not specified will use | ||
120 | * 'InvitationConfig' in the same space as $doc. | ||
121 | * | ||
122 | * $configClassName (String) (Optional) will load configuration from object of this class, if not specified will use | ||
123 | * 'Invitation' in the same space as $doc. | ||
124 | *### | ||
125 | #macro(loadInvitationConfig, $config, $configDocName, $configClassName) | ||
126 | #define($discard) | ||
127 | #if("$!configDocName" == '') | ||
128 | #set($configDocNameInternal = "${doc.getSpace()}.InvitationConfig") | ||
129 | #else | ||
130 | #set($configDocNameInternal = $configDocName) | ||
131 | #end | ||
132 | #if("$!configClassName" == '') | ||
133 | #set($configClassNameInternal = "${doc.getSpace()}.WebHome") | ||
134 | #else | ||
135 | #set($configClassNameInternal = $configClassName) | ||
136 | #end | ||
137 | ## | ||
138 | ## Load some parameters from the configuration. | ||
139 | #set($configDoc = $xwiki.getDocumentAsAuthor($configDocNameInternal)) | ||
140 | ## | ||
141 | ## If no configuration document exists, create one and save it. | ||
142 | #if($configDoc.isNew()) | ||
143 | ## | ||
144 | ## load the default configuration from this document. | ||
145 | #set($thisDocument = $xwiki.getDocumentAsAuthor("${doc.getSpace()}.InvitationCommon")) | ||
146 | #set($defaultConfigObj = $thisDocument.getObject($configClassNameInternal)) | ||
147 | #foreach($element in $defaultConfigObj.getProperties()) | ||
148 | $config.put($element.getName(), $defaultConfigObj.getProperty($element.getName()).getValue()) | ||
149 | #end | ||
150 | ## | ||
151 | #set($configDocContent = '{{velo' + 'city}}{{info}}$services.localization.render(''xe.invitation.internalDocument'', ["' | ||
152 | + "$!config.get('mainPage')" + '"]){{/info}}{{/velo' + 'city}}') | ||
153 | $configDoc.setContent($configDocContent) | ||
154 | $configDoc.setParent($configClassNameInternal) | ||
155 | #set($configObj = $configDoc.newObject($configClassNameInternal)) | ||
156 | #foreach($key in $config.keySet()) | ||
157 | $configObj.set($key, $config.get($key)) | ||
158 | #end | ||
159 | ## Now create the configurable objects. | ||
160 | #set($cfgable = $configDoc.newObject('XWiki.ConfigurableClass')) | ||
161 | $cfgable.set('displayInSection', 'Invitation') | ||
162 | $cfgable.set('configurationClass', $configClassNameInternal) | ||
163 | $cfgable.set('configureGlobally', 1) | ||
164 | #set($propsToShow = 'subjectLineTemplate|messageBodyTemplate|emailRegex|from_address|allowUsersOfOtherWikis' | ||
165 | + '|usersMayPersonalizeMessage|usersMaySendToMultiple|emailClass|emailContainer') | ||
166 | $cfgable.set('propertiesToShow', $propsToShow) | ||
167 | #set($cfgable = $configDoc.newObject('XWiki.ConfigurableClass')) | ||
168 | $cfgable.set('displayInSection', 'Invitation') | ||
169 | $cfgable.set('heading', '$services.localization.render(''xe.invitation.configuration.smtpHeading'')') | ||
170 | $cfgable.set('configurationClass', $configClassNameInternal) | ||
171 | $cfgable.set('configureGlobally', 1) | ||
172 | $cfgable.set('propertiesToShow', | ||
173 | 'smtp_server_password|smtp_server_username|smtp_port|smtp_server|javamail_extra_props') | ||
174 | $configDoc.saveAsAuthor() | ||
175 | #else | ||
176 | ## load the configuration object... | ||
177 | #set($configObj = $configDoc.getObject($configClassNameInternal)) | ||
178 | #foreach($element in $configObj.getProperties()) | ||
179 | $config.put($element.getName(), $configObj.getProperty($element.getName()).getValue()) | ||
180 | #end | ||
181 | #end | ||
182 | #end## define $discard | ||
183 | ## Now invoke the defined code... | ||
184 | #set($discard = $discard.toString()) | ||
185 | #end | ||
186 | ## | ||
187 | #* | ||
188 | * Basic unit testing of the macro above. | ||
189 | *### | ||
190 | #macro(testLoadInvitationConfig) | ||
191 | #set($configDoc = $xwiki.getDocumentAsAuthor("${doc.getSpace()}.InvitationConfig")) | ||
192 | #if(!$configDoc.isNew()) | ||
193 | $configDoc.deleteAsAuthor() | ||
194 | #end | ||
195 | #set($configClass = $xwiki.getDocumentAsAuthor("${doc.getSpace()}.WebHome")) | ||
196 | #if($configClass.isNew()) | ||
197 | {{error}}Class document [[${doc.getSpace()}.WebHome]] not found. can't run test.{{/error}} | ||
198 | #else | ||
199 | #set($config = {}) | ||
200 | #loadInvitationConfig($config, 'HopefullyNonexistantSpace') | ||
201 | #if($config.size() < 9) | ||
202 | {{error}}Config map too small{{/error}} | ||
203 | #end | ||
204 | #if($config.get('from_address') != 'no-reply@localhost.localdomain') | ||
205 | {{error}}form_address incorrect, expecting "no-reply@localhost.localdomain" got "$config.get('from_address')"{{/error}} | ||
206 | #end | ||
207 | #set($configDoc = $xwiki.getDocumentAsAuthor("${doc.getSpace()}.InvitationConfig")) | ||
208 | #if($configDoc.isNew()) | ||
209 | {{error}}Config document not created{{/error}} | ||
210 | #else | ||
211 | #set($configObj = $configDoc.getObject("${doc.getSpace()}.Invitation")) | ||
212 | $configObj.set('from_address', 'thisisatest@localhost.localdomain') | ||
213 | $configDoc.saveAsAuthor() | ||
214 | $config.clear() | ||
215 | #loadInvitationConfig($config, 'HopefullyNonexistantSpace') | ||
216 | #if($config.get('from_address') != 'thisisatest@localhost.localdomain') | ||
217 | {{error}}altering config parameter failed.{{/error}} | ||
218 | #end | ||
219 | #end | ||
220 | #end | ||
221 | #set($configDoc = $xwiki.getDocumentAsAuthor("${doc.getSpace()}.InvitationConfig")) | ||
222 | #if(!$configDoc.isNew()) | ||
223 | $configDoc.deleteAsAuthor() | ||
224 | #end | ||
225 | #end | ||
226 | ## | ||
227 | #** | ||
228 | * Load mail from mail containing document. | ||
229 | * | ||
230 | * $config (Map<String, String>) will be used to get the name of the email class. | ||
231 | * | ||
232 | * $emailContainer (Document) the document to get the email from. | ||
233 | * | ||
234 | * $mail (Map<String, XObject>) will be populated with email message XObjects by their messageID. | ||
235 | *### | ||
236 | #macro(loadInvitationMail, $config, $emailContainer, $mail) | ||
237 | ## If this doesn't already exist, it's created. | ||
238 | #if($emailContainer.isNew()) | ||
239 | #set($emailContainerContent = '{{velo' + 'city}}{{info}}$services.localization.render(''xe.invitation.internalDocument'', ["' | ||
240 | + "$config.get('emailContainer')" + '"]){{/info}}{{/velo' + 'city}}') | ||
241 | #set($discard = $emailContainer.setContent($emailContainerContent)) | ||
242 | #end | ||
243 | ## | ||
244 | ## Load messages into a Map by messageID. | ||
245 | #foreach($obj in $emailContainer.getObjects($config.get('emailClass'))) | ||
246 | #set($discard = $mail.put($obj.getProperty('messageID').getValue(), $obj)) | ||
247 | #end | ||
248 | #end | ||
249 | ## | ||
250 | #** | ||
251 | * Is a guest allowed to accept an invitation? | ||
252 | * | ||
253 | * $guestActionsDoc (Document) the document which guests will use to register. | ||
254 | *### | ||
255 | #macro(canGuestAcceptInvitation, $guestActionsDoc) | ||
256 | #set($out = 'true') | ||
257 | #if(!$xwiki.hasAccessLevel('register', 'XWiki.XWikiGuest', 'XWiki.XWikiPreferences') | ||
258 | && !$guestActionsDoc.hasProgrammingRights()) | ||
259 | ## | ||
260 | #set($out = 'false') | ||
261 | #end | ||
262 | $out## | ||
263 | #end | ||
264 | ## | ||
265 | #** | ||
266 | * Get the action taken by the user. | ||
267 | * This will interpret actions taken using displayActionConfirmationForm | ||
268 | * | ||
269 | * $parameterMap (Map<String, String>) the parameter map gotten by calling getParameterMap on the servlet request. | ||
270 | * | ||
271 | * $actionOutList (List<String>) will be populated with a single value (the action) | ||
272 | *### | ||
273 | #macro(getUserAction, $parameterMap, $actionOutList) | ||
274 | #set($actionInternal = 0) | ||
275 | #foreach($param in $parameterMap.keySet()) | ||
276 | #if($param.indexOf('doAction_') != -1) | ||
277 | ## Strip 'doAction_' | ||
278 | #set($actionInternal = $param.substring(9)) | ||
279 | #end | ||
280 | #end | ||
281 | #if($actionInternal != 0) | ||
282 | #set($discard = $actionOutList.add($actionInternal)) | ||
283 | #end | ||
284 | #end | ||
285 | ## | ||
286 | #* | ||
287 | * Display a form allowing a user to confirm doing an action. | ||
288 | * | ||
289 | * $messageIDs (List<String>) the unique IDs of the invitations to act upon. | ||
290 | * | ||
291 | * $action (String) the action to do. | ||
292 | * | ||
293 | * $memoLabel (String) what the memo field should be labeled. | ||
294 | * | ||
295 | * $confirmLabel (String) what the confirm button should say. | ||
296 | * | ||
297 | * $additionalParameters (Map<String, String>) these parameters will be xml escaped and placed in hidden input fields. | ||
298 | *### | ||
299 | #macro(displayActionConfirmationForm, $messageIDs, $action, $memoLabel, $confirmLabel, $additionalParameters) | ||
300 | {{html wiki=false clean=false}} | ||
301 | <div class="invitation action-confirm"> | ||
302 | <form action="$doc.getURL()" method="POST"> | ||
303 | ## | ||
304 | #foreach($id in $messageIDs) | ||
305 | <input type="hidden" name="messageID" value="$escapetool.xml($id)" /> | ||
306 | #end | ||
307 | <input type="hidden" name="confirm" value="y" /> | ||
308 | <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" /> | ||
309 | #if($additionalParameters && $additionalParameters.size() > 0) | ||
310 | #foreach($param in $additionalParameters.keySet()) | ||
311 | <input type="hidden" name="$escapetool.xml($param)" value="$escapetool.xml($additionalParameters.get($param))" /> | ||
312 | #end | ||
313 | #end | ||
314 | ## | ||
315 | <dl> | ||
316 | <dt><label for="memo">$memoLabel</label></dt> | ||
317 | <dd><input type="text" size="54" name="memo" /></dd> | ||
318 | </dl> | ||
319 | <div class="bottombuttons"> | ||
320 | <div class="buttons"> | ||
321 | <span class="buttonwrapper"> | ||
322 | <input type="submit" class="button" name="doAction_$escapetool.xml($action)" value="$confirmLabel" /> | ||
323 | </span> | ||
324 | </div> | ||
325 | </div> | ||
326 | </form> | ||
327 | </div> | ||
328 | {{/html}} | ||
329 | #end | ||
330 | ## | ||
331 | #* | ||
332 | * Set the status of a message and log the memo. | ||
333 | * | ||
334 | * $message (Xobject) the message to act on. | ||
335 | * | ||
336 | * $status (String) the status to set the message to. | ||
337 | * | ||
338 | * $memo (String) what to enter in the log. | ||
339 | *### | ||
340 | #macro(setMessageStatus, $message, $status, $memo) | ||
341 | $message.set('status', $status)## | ||
342 | #set($history = $message.getProperty('history').getValue()) | ||
343 | #set($statusWord = "#messageStatusForCode($status)") | ||
344 | ## disallow injection of \n which starts a new line or { which may be used for macros. | ||
345 | #set($log = $memo.replaceAll('\n', ' ').replaceAll('\{', '~{')) | ||
346 | #set($entry = "|$statusWord|[[$xcontext.getUser()]]|$!log") | ||
347 | #if("$!history" != '') | ||
348 | $message.set('history', "$!history$util.getNewline()$entry")## | ||
349 | #else | ||
350 | $message.set('history', $entry)## | ||
351 | #end | ||
352 | #end | ||
353 | ## | ||
354 | #* | ||
355 | * Get the last memo from the message history. | ||
356 | * | ||
357 | * $message (Xobject) the message to act on. | ||
358 | *### | ||
359 | #macro(getLastMemo, $message) | ||
360 | #set($history = "$!message.getProperty('history').getValue()") | ||
361 | #set($indexAfterLastNewline = $mathtool.add($history.lastIndexOf($util.getNewline()), 1)) | ||
362 | #if($indexAfterLastNewline < $history.length()) | ||
363 | #set($entry = "$!history.substring($indexAfterLastNewline)") | ||
364 | #set($indexAfterLastPipe = $mathtool.add($entry.lastIndexOf('|'), 1)) | ||
365 | #if($indexAfterLastPipe < $entry.length()) | ||
366 | $entry.substring($indexAfterLastPipe)## | ||
367 | #end | ||
368 | #end | ||
369 | #end | ||
370 | ## | ||
371 | #* | ||
372 | * Get the status of the current message for it's code. | ||
373 | * unsent - not sent yet. | ||
374 | * pending - sent and awating a response | ||
375 | * accepted - sent and accepted by recipient | ||
376 | * declined - sent and declined by recipient | ||
377 | * canceled - sent then canceled by sender | ||
378 | * reported - as spam (by recipient) | ||
379 | * notSpam - reported as spam and investigated (by admin) | ||
380 | * sendingFailed - failed to send message | ||
381 | * else - unknown status | ||
382 | * | ||
383 | * $status (String) the status of the message. | ||
384 | *### | ||
385 | #macro(messageStatusForCode, $status) | ||
386 | #if($status == 'unsent') | ||
387 | $services.localization.render('xe.invitation.messageStatus.unsent')## | ||
388 | #elseif($status == 'pending') | ||
389 | $services.localization.render('xe.invitation.messageStatus.pending')## | ||
390 | #elseif($status == 'accepted') | ||
391 | $services.localization.render('xe.invitation.messageStatus.accepted')## | ||
392 | #elseif($status == 'declined') | ||
393 | $services.localization.render('xe.invitation.messageStatus.declined')## | ||
394 | #elseif($status == 'canceled') | ||
395 | $services.localization.render('xe.invitation.messageStatus.canceled')## | ||
396 | #elseif($status == 'reported') | ||
397 | $services.localization.render('xe.invitation.messageStatus.reported')## | ||
398 | #elseif($status == 'notSpam') | ||
399 | $services.localization.render('xe.invitation.messageStatus.investigated')## | ||
400 | #elseif($status == 'sendingFailed') | ||
401 | $services.localization.render('xe.invitation.messageStatus.sendingFailed')## | ||
402 | #else | ||
403 | $services.localization.render('xe.invitation.messageStatus.unknown', [$escapetool.xml($status)])## | ||
404 | #end | ||
405 | #end | ||
406 | {{/velocity}} |