Wiki source code of Registration

Version 5.1 by teamwire004 on 2025/02/04 08:16

Show last authors
1 {{template name="register_macros.vm"/}}
2
3 {{velocity}}
4 ## The registration is enabled:
5 ## - on the main wiki
6 ## - on a subwiki if there is no service "$services.wiki.user"
7 ## - on a subwiki where the user scope allows local users
8 #if($xcontext.isMainWiki() || "$!services.wiki.user" == '' || $services.wiki.user.getUserScope() != "GLOBAL_ONLY")
9 ## These are defined in other places around XWiki, changing them here will result in undefined behavior.
10 #set($redirectParam = 'xredirect')
11 #set($userSpace = 'XWiki.')
12 #set($loginPage = 'XWiki.XWikiLogin')
13 #set($loginAction = 'loginsubmit')
14 ##
15 #set($documentName = 'XWiki.Registration')
16 ##
17 ## Security measure:
18 ## If this document is changed such that it must have programming permission in order to run, change this to false.
19 #set($sandbox = true)
20 ##
21 #set ($registrationConfig = $NULL)
22 #_loadConfig($registrationConfig)
23 ##
24 #*
25 * You may include this document in other documents using {{include reference="XWiki.Registration"/}}
26 * To specify that the user is invited and should be allowed to register even if Guest does not have permission to
27 * register, set $invited to true. NOTE: The including script must have programming permission to do this.
28 *
29 * To specify some code which should run after registration is successfully completed, set
30 * $doAfterRegistration to a define block of velocity code like so:
31 * #define($doAfterRegistration)
32 * some code
33 * #end
34 * Output from running this code will not be printed.
35 *
36 * The fields which will be seen on the registration page are defined here.
37 * $fields is an array and each field is a Map. The names shown below are Map keys.
38 *
39 * Each field must have:
40 * name - this is the name of the field, it will be the value for "name" and "id"
41 *
42 * Each field may have:
43 * label - this String will be written above the field.
44 *
45 * tag - the HTML tag which will be created, default is <input>, may also be a non form tag such as <img>
46 *
47 * params - a Map, each key value pair will be in the html tag. eg: {"size" : "30"} becomes <input size=30...
48 *
49 * validate a Map describing how to validate the field, validation is done in javascript then redone in velocity
50 * | for security and because not everyone has javascript.
51 * |
52 * +-mandatory (Optional) - Will fail if the field is not filled in.
53 * | +-failureMessage (Required) - The message to display if the field is not filled in.
54 * | +-noscript (Optional) - will not be checked by javascript
55 * |
56 * +-regex (Optional) - Will validate the field using a regular expression.
57 * | | because of character escaping, you must provide a different expression for the
58 * | | javascript validation and the server side validation. Both javascript and server side
59 * | | validation are optional, but if you provide neither, then your field will not be validated.
60 * | |
61 * | +-failureMessage (Optional) - The message to display if the regex evaluation returns false.
62 * | +-jsFailureMessage (Optional) - The message for Javascript to display if regex fails.
63 * | | If jsFailureMessage is not defined Javascript uses failureMessage.
64 * | | NOTE: Javascript injects the failure message using createTextNode so &lt; will
65 * | | be displayed as &lt;
66 * | |
67 * | +-pattern (Optional) - The regular expression to test the input at the server side, it's important to use
68 * | | this if you need to validate the field for security reasons, also it is good because not
69 * | | all browsers use javascript or have it enabled.
70 * | |
71 * | +-jsPattern (Optional) - The regular expression to use for client side, you can use escaped characters to avoid
72 * | | them being parsed as HTML or javascript. To get javascript to unescape characters use:
73 * | | {"jsPattern" : "'+unescape('%5E%5B%24')+'"}
74 * | | NOTE: If no jsPattern is specified, the jsValidator will try to validate
75 * | | using the server pattern.
76 * | |
77 * | +-noscript (Optional) - will not be checked by javascript
78 * |
79 * +-mustMatch (Optional) - Will fail if the entry into the field is not the same as the entry in another field.
80 * | | Good for password confirmation.
81 * | |
82 * | +-failureMessage (Required) - The message to display if the field doesn't match the named field.
83 * | +-name (Required) - The name of the field which this field must match.
84 * | +-noscript (Optional) - will not be checked by javascript
85 * |
86 * +-programmaticValidation (Optional) - This form of validation executes a piece of code which you give it and
87 * | | if the code returns the word "failed" then it gives the error message.
88 * | | Remember to put the code in singel quotes ('') because you want the value
89 * | | of 'code' to equal the literal code, not the output from running it.
90 * | |
91 * | +-code (Required) - The code which will be executed to test whether the field is filled in correctly.
92 * | +-failureMessage (Required) - The message which will be displayed if evaluating the code returns "false"
93 * |
94 * +-fieldOkayMessage (Optional) - The message which is displayed by LiveValidation when a field is validated as okay.
95 * If not specified, will be $defaultFieldOkayMessage
96 *
97 * noReturn - If this is specified, the field will not be filled in if there is an error and the user has to fix their
98 * registration information. If you don't want a password to be passed back in html then set this true
99 * for the password fields. Used for the captcha because it makes no sense to pass back a captcha answer.
100 *
101 * doAfterRegistration - Some Velocity code which will be executed after a successfull registration.
102 * This is used in the favorite color example.
103 * Remember to put the code in singel quotes ('') because you want the 'code' entry to equal the literal
104 * code, not the output from running it.
105 *
106 * Each field may not have: (reserved names)
107 * error - This is used to pass back any error message from the server side code.
108 *
109 * NOTE: This template uses a registration method which requires:
110 * * register_first_name
111 * * register_last_name
112 * * xwikiname
113 * * register_password
114 * * register2_password
115 * * register_email
116 * * template
117 * Removing or renaming any of these fields will result in undefined behavior.
118 *
119 *###
120 #set($mainFields = [])
121 ##
122 ## The user name field, mandatory and programmatically checked to make sure the username doesn't exist.
123 #set($field =
124 {'name' : 'xwikiname',
125 'label' : $services.localization.render('core.register.username'),
126 'params' : {
127 'type' : 'text',
128 'onfocus' : 'prepareName(document.forms.register);',
129 'size' : '60',
130 'autocomplete' : 'username'
131 },
132 'validate' : {
133 'mandatory' : {
134 'failureMessage' : $services.localization.render('core.validation.required.message')
135 },
136 'programmaticValidation' : {
137 'code' : '#nameAvailable($request.get("xwikiname"))',
138 'failureMessage' : $services.localization.render('core.register.userAlreadyExists')
139 }
140 }
141 })
142 #set($discard = $mainFields.add($field))
143 ## Make sure the chosen user name is not already taken
144 ## This macro is called by programmaticValidation for xwikiname (above)
145 #macro (nameAvailable, $name)
146 #if ($xwiki.exists("$userSpace$name"))
147 failed
148 #end
149 #end
150 ##
151 ##The password field, mandatory and must be at least 6 characters long.
152 ##The confirm password field, mandatory, must match password field, and must also be 6+ characters long.
153 #definePasswordFields($mainFields, 'register_password', 'register2_password', $registrationConfig.passwordOptions)
154 ##
155 ## The email address field, regex checked with an email pattern. Mandatory if registration uses email verification
156 #set($field =
157 {'name' : 'register_email',
158 'label' : $services.localization.render('core.register.email'),
159 'params' : {
160 'type' : 'text',
161 'size' : '60',
162 'autocomplete' : 'email'
163 },
164 'validate' : {
165 'regex' : {
166 'pattern' : '/^([^@\s]+)@((?:[-a-zA-Z0-9]+\.)+[a-zA-Z]{2,})$/',
167 'failureMessage' : $services.localization.render('xe.admin.registration.invalidEmail')
168 }
169 }
170 })
171 #if($registrationConfig.useEmailVerification)
172 #set($field.validate.mandatory = {'failureMessage' : $services.localization.render('core.validation.required.message')})
173 #end
174 #set($discard = $mainFields.add($field))
175 ##
176 #*********
177 ## Uncomment this code to see an example of how you can easily add a field to the registration page
178 ## NOTE: In order to save the favorite color in the "doAfterRegistration" hook, this page must be
179 ## saved by an administrator and can not self sandboxing.
180 #set($sandbox = false)
181 #set($field =
182 {'name' : 'favorite_color',
183 'label' : 'What is your favorite color',
184 'params' : {
185 'type' : 'text',
186 'size' : '60'
187 },
188 'validate' : {
189 'mandatory' : {
190 'failureMessage' : $services.localization.render('core.validation.required.message')
191 },
192 'regex' : {
193 'pattern' : '/^green$/',
194 'failureMessage' : 'You are not cool enough to register here.'
195 },
196 'fieldOkayMessage' : 'You are awesome.'
197 },
198 'doAfterRegistration' : '#saveFavoriteColor()'
199 })
200 #set($discard = $mainFields.add($field))
201 ## Save the user's favorite color on their user page.
202 #macro(saveFavoriteColor)
203 #set($xwikiname = $request.get('xwikiname'))
204 #set($userDoc = $xwiki.getDocument("$userSpace$xwikiname"))
205 $userDoc.setContent("$userDoc.getContent() ${xwikiname}'s favorite color is $request.get('favorite_color')!")
206 ## The user (who is not yet logged in) can't save documents so saveWithProgrammingRights
207 ## will save the document as long as the user who last saved this registration page has programming rights.
208 $userDoc.saveWithProgrammingRights("Saved favorite color from registration form.")
209 #end
210 *********###
211 #set($aboutYouFields = [])
212 ##
213 ## The first name field, no checking.
214 #set($field =
215 {'name' : 'register_first_name',
216 'label' : $services.localization.render('core.register.firstName'),
217 'params' : {
218 'type' : 'text',
219 'size' : '60',
220 'autocomplete' : 'given-name'
221 }
222 })
223 #set($discard = $aboutYouFields.add($field))
224 ##
225 ## The last name field, no checking.
226 #set($field =
227 {'name' : 'register_last_name',
228 'label' : $services.localization.render('core.register.lastName'),
229 'params' : {
230 'type' : 'text',
231 'size' : '60',
232 'autocomplete' : 'family-name'
233 }
234 })
235 #set($discard = $aboutYouFields.add($field))
236 ##
237 ## To disable the CAPTCHA on this page, comment out the next entry.
238 ## The CAPTCHA, not really an input field but still defined the same way.
239 #if($services.captcha
240 && !$invited
241 && $xcontext.getUser() == "XWiki.XWikiGuest"
242 && $registrationConfig.requireCaptcha)
243 ## The CAPTCHA field, programmatically checked to make sure the CAPTCHA is right.
244 ## Not checked by javascript because javascript can't check the CAPTCHA and the Ok message because it passes the
245 ## mandatory test is misleading.
246 ## Also, not filled back in if there is an error ('noReturn').
247 #set($field =
248 {'name' : 'captcha_placeholder',
249 'label' : $services.localization.render('core.captcha.label'),
250 'skipLabelFor' : true,
251 'type' : 'html',
252 'html' : "<span class='xHint'>$escapetool.xml($services.localization.render('core.captcha.instruction'))
253 </span> $!{services.captcha.default.display()}",
254 'validate' : {
255 'programmaticValidation' : {
256 'code' : '#if (!$services.captcha.default.isValid())failed#end',
257 'failureMessage' : $services.localization.render('core.captcha.captchaAnswerIsWrong')
258 }
259 },
260 'noReturn' : true
261 })
262 #set($discard = $aboutYouFields.add($field))
263 #end
264 ## Pass the redirect parameter on so that the login page may redirect to the right place.
265 ## Not necessary in Firefox 3.0.10 or Opera 9.64, I don't know about IE or Safari.
266 #set($field =
267 {'name' : $redirectParam,
268 'params' : {
269 'type' : 'hidden'
270 }
271 })
272 #set($discard = $aboutYouFields.add($field))
273 #set($fields = [])
274 #set($discard = $fields.addAll($mainFields))
275 #set($discard = $fields.addAll($aboutYouFields))
276 ##
277 #######################################################################
278 ## The Code.
279 #######################################################################
280 ##
281 ## This application's HTML is dynamically generated and editing in WYSIWYG would not work
282 #if($xcontext.getAction() == 'edit')
283 $response.sendRedirect("$xwiki.getURL($doc.getFullName(), 'edit')?editor=wiki")
284 #end
285 ##
286 ## If this document has PR and is not included from another document then it's author should be set to Guest
287 ## for the duration of it's execution in order to improve security.
288 ## Note we compare document ids because
289 #if($sandbox
290 && $xcontext.hasProgrammingRights()
291 && $xcontext.getDoc().getDocumentReference().equals($xwiki.getDocument($documentName).getDocumentReference()))
292 ##
293 $xcontext.dropPermissions()##
294 #end
295 ##
296 ## Access level to register must be explicitly checked because it is only checked in XWiki.prepareDocuments
297 ## and this page is accessible through view action.
298 #if(!$xcontext.hasAccessLevel('register', 'XWiki.XWikiPreferences'))
299 ## Make an exception if another document with programming permission (Invitation app) has included this
300 ## document and set $invited to true.
301 #if(!$invited || !$xcontext.hasProgrammingRights())
302 $response.sendRedirect("$xwiki.getURL($doc.getFullName(), 'login')")
303 #end
304 #end
305 ##
306 ## Display the heading
307 $registrationConfig.heading
308 ## If the submit button has been pressed, then we test the input and maybe create the user.
309 #if($request.getParameter('xwikiname'))
310 ## Do server side validation of input fields.
311 ## This will output messages if something goes wrong, nothing if everything is alright.
312 ## We need to trim the output so that we can keep indentations in the validation script.
313 #set ($validationText = $stringtool.trim("#validateFields($fields, $request)"))
314 $validationText##
315 ## If server side validation was successful, create the user
316 #if($allFieldsValid)
317 #createUser($fields, $request, $response, $doAfterRegistration)
318 #end
319 #end
320 ## If the registration was not successful or if the user hasn't submitted the info yet
321 ## Then we display the registration form.
322 #if(!$registrationDone)
323 $registrationConfig.welcomeMessage
324
325 {{html clean="false"}}
326 <form id="register" action="$xwiki.relativeRequestURL" method="post" class="xform half">
327 <div class="hidden">
328 #if ($request.xpage == 'registerinline')
329 #skinExtensionHooks
330 #end
331 #set ($userDirectoryReference = $services.model.createDocumentReference('', 'Main', 'UserDirectory'))
332 #if ($xwiki.exists($userDirectoryReference))
333 <input type="hidden" name="parent" value="$!{services.model.serialize($userDirectoryReference, 'default')}" />
334 #end
335 </div>
336 ## Note that the macro inject the form_token field.
337 #generateHtml($mainFields, $request, 'false')
338 <h2>$services.localization.render('core.register.aboutYou')</h2>
339 #generateHtml($aboutYouFields, $request, 'false')
340 <input type="hidden" name="form_token" value="$services.csrf.getToken()" />
341 #generateJavascript($fields)
342 <p class="buttons">
343 <span class="buttonwrapper">
344 <input type="submit" value="$services.localization.render('core.register.submit')" class="button"/>
345 </span>
346 </p>
347 </form>
348 {{/html}}
349
350 ##
351 ## Allow permitted users to configure this application.
352 #if($xcontext.getUser() != 'XWiki.XWikiGuest' && $xcontext.hasAccessLevel("edit", $documentName))
353 [[{{translation key="xe.admin.registration.youCanConfigureRegistrationHere"/}}>>XWiki.XWikiPreferences?section=Registration&editor=globaladmin#HCustomizeXWikiRegistration]]
354 {{html}}<a href="$xwiki.getURL($documentName, 'edit', 'editor=wiki')">$services.localization.render('xe.admin.registration.youCanConfigureRegistrationFieldsHere')</a>{{/html}}
355 #end
356 #end
357 #else
358 ## The registration is not allowed on the subwiki
359 ## Redirecting to main wiki's registration page since local user registration is not allowed.
360 #set($mainWikiRegisterPageReference = $services.model.createDocumentReference($services.wiki.mainWikiId, 'XWiki', 'Register'))
361 #set($temp = $response.sendRedirect($xwiki.getURL($mainWikiRegisterPageReference, 'register', $request.queryString)))
362 #end
363 ##
364 #*
365 * Create the user.
366 * Calls $xwiki.createUser to create a new user.
367 *
368 * @param $request An XWikiRequest object which made the register request.
369 * @param $response The XWikiResponse object to send any redirects to.
370 * @param $doAfterRegistration code block to run after registration completes successfully.
371 *###
372 #macro(createUser, $fields, $request, $response, $doAfterRegistration)
373 ## CSRF check
374 #if(${services.csrf.isTokenValid("$!{request.getParameter('form_token')}")})
375 ## See if email verification is required and register the user.
376 #if($xwiki.getXWikiPreferenceAsInt('use_email_verification', 0) == 1)
377 #set($reg = $xwiki.createUser(true))
378 #else
379 #set($reg = $xwiki.createUser(false))
380 #end
381 #else
382 $response.sendRedirect("$!{services.csrf.getResubmissionURL()}")
383 #end
384 ##
385 ## Handle output from the registration.
386 #if($reg && $reg <= 0)
387 {{error}}
388 #if($reg == -2)
389 {{translation key="core.register.passwordMismatch"/}}
390 ## -3 means username taken, -8 means username is superadmin name
391 #elseif($reg == -3 || $reg == -8)
392 {{translation key="core.register.userAlreadyExists"/}}
393 #elseif($reg == -4)
394 {{translation key="core.register.invalidUsername"/}}
395 #elseif ($reg == -9)
396 {{translation key="core.register.invalidCaptcha"/}}
397 ## Note that -10 is reserved already (see api.XWiki#createUser)
398 #elseif($reg == -11)
399 {{translation key="core.register.mailSenderWronglyConfigured"/}}
400 #else
401 {{translation key="core.register.registerFailed" parameters="$reg"/}}
402 #end
403 {{/error}}
404 #elseif($reg)
405 ## Registration was successful
406 #set($registrationDone = true)
407 ##
408 ## If there is any thing to "doAfterRegistration" then do it.
409 #foreach($field in $fields)
410 #if($field.get('doAfterRegistration'))
411 #evaluate($field.get('doAfterRegistration'))
412 #end
413 #end
414 ## If there is a "global" doAfterRegistration, do that as well.
415 ## Calling toString() on a #define block will execute it and we discard the result.
416 #set($discard = $doAfterRegistration.toString())
417 ##
418 ## Define some strings which may be used by autoLogin or loginButton
419 #set($userName = $!request.get('xwikiname'))
420 #set($password = $!request.get('register_password'))
421 #set($loginURL = $xwiki.getURL($loginPage, $loginAction))
422 #if("$!request.getParameter($redirectParam)" != '')
423 #set($redirect = $request.getParameter($redirectParam))
424 #else
425 #set($redirect = $registrationConfig.defaultRedirect)
426 #end
427 ## Display a "registration successful" message
428 ## Define some strings which may be used by the welcome message
429 #set($firstName = $!request.get('register_first_name'))
430 #set($lastName = $!request.get('register_last_name'))
431 #evaluate($registrationConfig.registrationSuccessMessage)
432
433 ## Empty line prevents message from being forced into a <p> block.
434
435 ## Give the user a login button which posts their username and password to loginsubmit
436 #if($registrationConfig.loginButton)
437
438 {{html clean=false wiki=false}}
439 <form id="loginForm" action="$loginURL" method="post">
440 <div class="centered">
441 <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
442 <input id="j_username" name="j_username" type="hidden" value="$escapetool.xml($!userName)" />
443 <input id="j_password" name="j_password" type="hidden" value="$escapetool.xml($!password)" />
444 <input id="$redirectParam" name="$redirectParam" type="hidden" value="$escapetool.xml($redirect)" />
445 <span class="buttonwrapper">
446 <input type="submit" value="$services.localization.render('login')" class="button"/>
447 </span>
448 #set ($mainPage = $services.wiki.currentWikiDescriptor.mainPageReference)
449 #if ($xwiki.checkAccess($mainPage, 'view'))
450 <span class="buttonwrapper">
451 <a href="$!xwiki.getURL($mainPage)" rel="home" class="button secondary">
452 $services.localization.render('core.register.successful.backtohome')
453 </a>
454 </span>
455 #end
456 </div>
457 </form>
458 ## We don't want autoLogin if we are administrators adding users...
459 #if ($registrationConfig.autoLogin && $request.xpage != 'registerinline')
460 <script>
461 document.observe('xwiki:dom:loaded', function() {
462 document.forms['loginForm'].submit();
463 });
464 </script>
465 #end
466 {{/html}}
467
468 #end
469 #end
470 ##
471 #end## createUser Macro
472 {{/velocity}}