diff --git a/xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-ui/src/main/resources/XWiki/Registration.xml b/xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-ui/src/main/resources/XWiki/Registration.xml index 76439c3e1de7..bcab74e18203 100644 --- a/xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-ui/src/main/resources/XWiki/Registration.xml +++ b/xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-ui/src/main/resources/XWiki/Registration.xml @@ -36,7 +36,9 @@ false xwiki/2.0 true - {{velocity}} + {{template name="register_macros.vm"/}} + +{{velocity}} ## The registration is enabled: ## - on the main wiki ## - on a subwiki if there is no service "$services.wiki.user" @@ -49,7 +51,6 @@ #set($loginAction = 'loginsubmit') ## #set($documentName = 'XWiki.Registration') - #set($configurationClassName = 'XWiki.Registration') ## ## Security measure: ## If this document is changed such that it must have programming permission in order to run, change this to false. @@ -58,15 +59,6 @@ ## Load the configuration from a seperate document. #loadConfig('XWiki.RegistrationConfig') ## - ## Defines what server generated error messages should look like - ## The error message when a field is entered incorrectly - #set($failureMessageParams = { 'class' : 'LV_validation_message LV_invalid'}) - ## 'LV_validation_message LV_invalid' depends on this: - $xwiki.get('ssfx').use('uicomponents/widgets/validation/livevalidation.css', true) - ## - ## The * next to the fields to denote they are mandatory. - #set($fieldMandatoryStar = { 'class' : 'xRequired'}) - ## #* * You may include this document in other documents using {{include reference="XWiki.Registration"/}} * To specify that the user is invited and should be allowed to register even if Guest does not have permission to @@ -209,90 +201,15 @@ #set($discard = $fields.add($field)) ## Make sure the chosen user name is not already taken ## This macro is called by programmaticValidation for xwikiname (above) - #macro(nameAvailable, $name) - #if($xwiki.exists("$userSpace$name")) + #macro (nameAvailable, $name) + #if ($xwiki.exists("$userSpace$name")) failed #end #end ## ##The password field, mandatory and must be at least 6 characters long. - #set ($passwordRegexes = []) - #set ($patternLength = "/.{" + $passwordLength + ",}/") - #set ($passwordRegex = - { - 'pattern' : $patternLength, - 'failureMessage' : $services.localization.render('xe.admin.registration.passwordTooShort') - }) - #set ($discard = $passwordRegexes.add($passwordRegex)) - #if ($passwordRuleOneUpperCaseEnabled) - #set ($passwordRegex = - { - 'pattern' : '/[A-Z]+/', - 'failureMessage' : $services.localization.render('xe.admin.registration.passwordMustContainUppercase') - }) - #set ($discard = $passwordRegexes.add($passwordRegex)) - #end - #if ($passwordRuleOneLowerCaseEnabled) - #set ($passwordRegex = - { - 'pattern' : '/[a-z]+/', - 'failureMessage' : $services.localization.render('xe.admin.registration.passwordMustContainLowercase') - }) - #set ($discard = $passwordRegexes.add($passwordRegex)) - #end - #if ($passwordRuleOneNumberEnabled) - #set ($passwordRegex = - { - 'pattern' : '/[0-9]+/', - 'failureMessage' : $services.localization.render('xe.admin.registration.passwordMustContainNumber') - }) - #set ($discard = $passwordRegexes.add($passwordRegex)) - #end - #if ($passwordRuleOneSymbolEnabled) - #set ($passwordRegex = - { - 'pattern' : '/\W+/', - 'failureMessage' : $services.localization.render('xe.admin.registration.passwordMustContainSymbol') - }) - #set ($discard = $passwordRegexes.add($passwordRegex)) - #end - #set($field = - {'name' : 'register_password', - 'label' : $services.localization.render('core.register.password'), - 'params' : { - 'type' : 'password', - 'autocomplete' : 'off', - 'size' : '60' - }, - 'validate' : { - 'mandatory' : { - 'failureMessage' : $services.localization.render('core.validation.required.message') - }, - 'regexes' : $passwordRegexes - } - }) - #set($discard = $fields.add($field)) - ## ##The confirm password field, mandatory, must match password field, and must also be 6+ characters long. - #set($field = - {'name' : 'register2_password', - 'label' : $services.localization.render('core.register.passwordRepeat'), - 'params' : { - 'type' : 'password', - 'autocomplete' : 'off', - 'size' : '60' - }, - 'validate' : { - 'mandatory' : { - 'failureMessage' : $services.localization.render('core.validation.required.message') - }, - 'mustMatch' : { - 'name' : 'register_password', - 'failureMessage' : $services.localization.render('xe.admin.registration.passwordMismatch') - } - } - }) - #set($discard = $fields.add($field)) + #definePasswordFields($fields, 'register_password', 'register2_password', $passwordOptions) ## ## The email address field, regex checked with an email pattern. Mandatory if registration uses email verification #set($field = @@ -401,10 +318,6 @@ ## The Code. ####################################################################### ## - #if($useLiveValidation) - $xwiki.get('jsfx').use('uicomponents/widgets/validation/livevalidation_prototype.js') - $xwiki.get('ssfx').use('uicomponents/widgets/validation/livevalidation.css', true) - #end ## This application's HTML is dynamically generated and editing in WYSIWYG would not work #if($xcontext.getAction() == 'edit') $response.sendRedirect("$xwiki.getURL($doc.getFullName(), 'edit')?editor=wiki") @@ -438,7 +351,7 @@ ## This must not be in a #set directive as it will output messages if something goes wrong. #validateFields($fields, $request) ## If server side validation was successfull, create the user - #if(!$registrationFailed) + #if($allFieldsValid) #createUser($fields, $request, $response, $doAfterRegistration) #end #end @@ -459,15 +372,12 @@ <input type="hidden" name="parent" value="$!{services.model.serialize($userDirectoryReference, 'default')}" /> #end </div> - #generateHtml($fields, $fieldMandatoryStar, $failureMessageParams) + #generateHtml($fields, $request) <p class="buttons"> <span class="buttonwrapper"> <input type="submit" value="$services.localization.render('core.register.submit')" class="button"/> </span> </p> - #if ($useLiveValidation) - #generateJavascript($fields) - #end </form> {{/html}} @@ -485,103 +395,6 @@ #set($temp = $response.sendRedirect($xwiki.getURL($mainWikiRegisterPageReference, 'register', $request.queryString))) #end ## -#macro(validateRegex $fieldValue, $fieldName, $regex, $error) - #if($regex.get('pattern') && $regex.get('failureMessage')) - ## Make Java regexes more compatible with Perl/js style regexes by removing leading and trailing / - #if($regex.get('pattern').length() > 1) - #set($pattern = $regex.get('pattern')) - #if($pattern.lastIndexOf('/') < $pattern.length() - 1) - ERROR: In field: ${fieldName}: regex validation does not allow flags after the /, please fix [${pattern}]. - #end - #set($pattern = $pattern.substring($mathtool.add(1, $pattern.indexOf('/')), $pattern.lastIndexOf('/'))) - #else - ## I don't expect this but want to maintain compatibility. - #set($pattern = $regex.get('pattern')) - #end - #if($regextool.find($value, $pattern).isEmpty()) - #set($error = $regex.get('failureMessage')) - #end - #elseif($regex.get('pattern')) - ERROR: In field: ${fieldName}: regex validation must include failureMessage. - #end -#end - -####### The Macros (nothing below this point is run directly) ######### -#* - * Server side validation, this is necessary for security and because not everyone has Javascript - * - * @param $fields The array of fields to validate. - * @param $request An XWikiRequest object which made the register request, used to get parameters. - *### -#macro(validateFields, $fields, $request) - #foreach($field in $fields) - #if($field.get('validate') && $field.get('name')) - #set($fieldName = $field.get('name')) - #set($validate = $field.get('validate')) - #set($error = '') - #set($value = $request.get($fieldName)) - #if("$!value" != '' || $field.get('type') == 'html') - ## - ## mustMatch validation - #if($error == '' && $validate.get('mustMatch')) - #set($mustMatch = $validate.get('mustMatch')) - #if($mustMatch.get('name') && $mustMatch.get('failureMessage')) - #if($request.get($fieldName) != $request.get($mustMatch.get('name'))) - #set($error = $mustMatch.get('failureMessage')) - #end - #else - ERROR: In field: ${fieldName}: mustMatch validation required both name - (of field which this field must match) and failureMessage. - #end - #end - ## - ## Regex validation - ## We won't bother with regex validation if there is no entry, that would defeat the purpose of 'mandatory' - #if($error == '' && $validate.get('regex') && $value && $value != '') - #set($regex = $validate.get('regex')) - #validateRegex($value, $fieldName, $regex, $error) - #end - ## List of regex validation - #if($error == '' && $validate.get('regexes') && $value && $value != '') - #set($regexes = $validate.get('regexes')) - #foreach ($regex in $regexes) - #validateRegex($value, $fieldName, $regex, $error) - #end - #end - ## - ## If regex and mustMatch validation passed, try programmatic validation - #if($error == '' && $validate.get('programmaticValidation')) - #set($pv = $validate.get('programmaticValidation')) - #if($pv.get('code') && $pv.get('failureMessage')) - #set($pvReturn = "#evaluate($pv.get('code'))") - #if($pvReturn.indexOf('failed') != -1) - #set($error = $pv.get('failureMessage')) - #end - #else - ERROR: In field: ${fieldName}: programmaticValidation requires code and failureMessage - #end - #end - #else - ## - ## If no content, check if content is mandatory - #if($validate.get('mandatory')) - #set($mandatory = $validate.get('mandatory')) - #if($mandatory.get('failureMessage')) - #set($error = $mandatory.get('failureMessage')) - #else - ERROR: In field: ${fieldName}: mandatory validation requires a failureMessage - #end - #end - #end - #if($error != '') - #set($discard = $field.put('error', $error)) - #set($registrationFailed = true) - #end - #elseif(!$field.get('name')) - ERROR: Field with no name. - #end##if(validate) - #end##loop -#end##macro #* * Create the user. * Calls $xwiki.createUser to create a new user. @@ -677,230 +490,6 @@ #end ## #end## createUser Macro -#* - * Generate HTML form, this is the only place where HTML is written. - * - * @param $fields The array of fields to use for generating html code. - * @param $fieldMandatoryStar The tag parameters for a * indicating a mandatory field. - * @param $failureMessageParams The tag parameters for a failure message. - *### -#macro(generateHtml, $fields, $fieldMandatoryStar, $failureMessageParams) - ## Put the same values back into the fields. - #getParams($fields) - ## - <dl> - #foreach($field in $fields) - #if($field.get('name')) - #set($fieldName = $field.get('name')) - #if($field.get('label')) - #set($label = $field.get('label')) - <dt><label #if (!$field.get('skipLabelFor'))for="$fieldName"#end>$label - #if($field.get('validate').get('mandatory')) - <span ## - #foreach($entry in $fieldMandatoryStar.entrySet()) - $entry.getKey()="$entry.getValue()" ## - #end - >$services.localization.render('core.validation.required')</span> - #end - </label> - </dt> - #end - <dd> - #if ($field.get('type') == 'html') - $field.get('html') - #elseif ($field.get('type') == 'tag' || "$!{field.get('type')}" == '') - ## If no tag then default tag is <input> - #if($field.get('tag')) - #set($tag = $field.get('tag')) - #else - #set($tag = 'input') - #end - <$tag id="$fieldName" ## - #set($params = $field.get('params')) - ## If no name parameter is spacified, then we use the field name - #if(!$params.get('name')) - #set($discard = $params.put('name', $fieldName)) - #end - #foreach($entry in $params.entrySet()) - ## If a parameter is specified as '' then we don't include it. - #if($entry.getValue() != '') - $entry.getKey()="$escapetool.xml($entry.getValue())" ## - #end - #end - ## Close the opened tag in a way that is valid for its type (i.e. with "/>" or with "></tag>"). - #if ($tag == 'input') - #set ($voidTag = true) - #end - #if ($voidTag)/#end>#if (!$voidTag)</$tag>#end - #end - #if($field.get('error')) - <span ## - #foreach($entry in $failureMessageParams.entrySet()) - $entry.getKey()="$entry.getValue()" ## - #end - >$field.get('error')</span> - #end - </dd> - #else - ERROR: Field with no name. - #end##if fieldName exists - #end - </dl> -#end -#* - * Generate the Javascript for interacting with LiveValidation. - * - * @param $fields The array of fields which to validate. - *### -#macro(generateJavascript, $fields) - <script type='text/javascript'> - /* <![CDATA[ */ - var initRegistrationFormValidation = function() { - ## - #foreach($field in $fields) - #if($field.get('validate') && $field.get('name')) - #set($validate = $field.get('validate')) - #if(($validate.get('mandatory') && !$validate.get('mandatory').get('noscript')) - || ($validate.get('regex') && !$validate.get('regex').get('noscript')) - || ($validate.get('mustMatch') && !$validate.get('mustMatch').get('noscript'))) - #set($fieldName = $field.get('name')) - #if($validate.get('fieldOkayMessage')) - #set($okayMessage = $validate.get('fieldOkayMessage')) - #else - #set($okayMessage = $defaultFieldOkayMessage) - #end - var ${fieldName}Validator = new LiveValidation("$fieldName", { validMessage: "$okayMessage", wait: 500} ); - ## - #if($validate.get('mandatory')) - #set($mandatory = $validate.get('mandatory')) - #if($mandatory.get('failureMessage') && !$mandatory.get('noscript')) - ${fieldName}Validator.add( Validate.Presence, { failureMessage: "$!mandatory.get('failureMessage')"} ); - #end - #end - ## - #if($validate.get('mustMatch')) - #set($mustMatch = $validate.get('mustMatch')) - #if($mustMatch.get('name') && $mustMatch.get('failureMessage') && !$mustMatch.get('noscript')) - ${fieldName}Validator.add( Validate.Confirmation, { match: $$("input[name=$!mustMatch.get('name')]")[0], failureMessage: "$!mustMatch.get('failureMessage')"} ); - #end - #end - ## - #if($validate.get('regex')) - #set($regex = $validate.get('regex')) - #set($pattern = "") - #if($regex.get('jsPattern')) - #set($pattern = $regex.get('jsPattern')) - #elseif($regex.get('pattern')) - #set($pattern = $regex.get('pattern')) - #end - #set($failMessage = "") - #if($regex.get('jsFailureMessage')) - #set($failMessage = $regex.get('jsFailureMessage')) - #elseif($regex.get('failureMessage')) - #set($failMessage = $regex.get('failureMessage')) - #end - #if($pattern != '' && $failMessage != '' && !$regex.get('noscript')) - ${fieldName}Validator.add( Validate.Format, { pattern: $pattern, failureMessage: "$failMessage"} ); - #end - #end##if regex - #end##if contains js validateable fields. - #end##if validate - #end##loop - }; - document.observe('xwiki:dom:loaded', initRegistrationFormValidation); - document.observe('xwiki:dom:updated', function(event) { - var container = (event && event.memo && event.memo.elements && event.memo.elements[0]) || $('body'); - if (container.down('form#register')) { - initRegistrationFormValidation(); - } - });// ]]> - </script> -#end##macro -#* - * Get parameters from request so that values will be filled in if there is a mistake - * in one of the entries. Entries will be returned to fields[n].params.value - * Fields will not be returned if they have either noReturn or error specified. - * - * @param $fields The array of fields to get parameters for. - *### -#macro(getParams $fields) - #foreach($field in $fields) - #if($field.get('name') && $request.get($field.get('name'))) - #if(!$field.get('noReturn') && !$field.get('error')) - #if(!$field.get('params')) - #set($params = {}) - #set($discard = $field.put('params', $params)) - #else - #set($params = $field.get('params')) - #end - #set($discard = $params.put('value', $request.get($field.get('name')))) - #end - #end - #end -#end -#* - * Get the configuration from the configuration object. - * - * @param $configDocumentName The name of the document to get the configuration from. - *### -#macro(loadConfig, $configDocumentName) - #set($configDocument = $xwiki.getDocument($configDocumentName)) - #if(!$configDocument || !$configDocument.getObject($configurationClassName)) - ## No config document, load defaults. - #set($heading = "$services.localization.render('core.register.title')") - #set($welcomeMessage = "$services.localization.render('core.register.welcome')") - #set($useLiveValidation = true) - #set($defaultFieldOkayMessage = "$services.localization.render('core.validation.valid.message')") - #set($loginButton = true) - #set($defaultRedirect = "$xwiki.getURL($services.model.resolveDocument('', 'default', $doc.documentReference.extractReference('WIKI')))") - #set($userFullName = "$request.get('register_first_name') $request.get('register_last_name')") - #set($registrationSuccessMessage = '{{info}}$services.localization.render("core.register.successful", ["[[${userFullName}>>${userSpace}${userName}]]", ${userName}]){{/info}}') - #set($passwordLength = 6) - #set($passwordRuleOneLowerCaseEnabled = false) - #set($passwordRuleOneUpperCaseEnabled = false) - #set($passwordRuleOneNumberEnabled = false) - #set($passwordRuleOneSymbolEnabled = false) - #else - #set($configObject = $configDocument.getObject($configurationClassName)) - #if ($xcontext.action == 'register') - #set ($heading = "(% id='document-title'%)((( = #evaluate($configObject.getProperty('heading').getValue()) = )))(%%)") - #else - #set ($heading = "= #evaluate($configObject.getProperty('heading').getValue()) =") - #end - #set($welcomeMessage = "#evaluate($configObject.getProperty('welcomeMessage').getValue())") - #if($configObject.getProperty('liveValidation_enabled').getValue() == 1) - #set($useLiveValidation = true) - #end - #set($defaultFieldOkayMessage = "#evaluate($configObject.getProperty('liveValidation_defaultFieldOkMessage').getValue())") - #if($configObject.getProperty('loginButton_enabled').getValue() == 1) - #set($loginButton = true) - #end - #if($configObject.getProperty('loginButton_autoLogin_enabled').getValue() == 1) - #set($autoLogin = true) - #end - #set($defaultRedirect = "#evaluate($configObject.getProperty('defaultRedirect').getValue())") - #set($registrationSuccessMessage = "$configObject.getProperty('registrationSuccessMessage').getValue()") - #if($configObject.getProperty('requireCaptcha').getValue() == 1) - #set($requireCaptcha = true) - #end - #if($configObject.getProperty('passwordRuleOneLowerCaseEnabled').getValue() == 1) - #set($passwordRuleOneLowerCaseEnabled = true) - #end - #if($configObject.getProperty('passwordRuleOneUpperCaseEnabled').getValue() == 1) - #set($passwordRuleOneUpperCaseEnabled = true) - #end - #if($configObject.getProperty('passwordRuleOneNumberEnabled').getValue() == 1) - #set($passwordRuleOneNumberEnabled = true) - #end - #if($configObject.getProperty('passwordRuleOneSymbolEnabled').getValue() == 1) - #set($passwordRuleOneSymbolEnabled = true) - #end - #set($passwordLength = $configObject.getProperty('passwordLength').getValue()) - #if ("$!passwordLength" == "" || $passwordLength < 1) - #set ($passwordLength = 6) - #end - #end -#end {{/velocity}} XWiki.Registration diff --git a/xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-ui/src/main/resources/XWiki/ResetPasswordComplete.xml b/xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-ui/src/main/resources/XWiki/ResetPasswordComplete.xml index 65bfb9253801..c0b4791dc83a 100644 --- a/xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-ui/src/main/resources/XWiki/ResetPasswordComplete.xml +++ b/xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-ui/src/main/resources/XWiki/ResetPasswordComplete.xml @@ -36,7 +36,9 @@ false xwiki/2.1 true - {{velocity output="false"}} + {{template name="register_macros.vm"/}} + +{{velocity}} #** This page completes the password reset procedure. It works according to the next algorithm: 1. Verify that the correct verification URL is entered, by checking the 'u' and 'v' request parameters against the existing ResetPasswordRequest objects @@ -106,6 +108,10 @@ it is secured against unprivileged editing. #if ($message != '') {{warning}}$message{{/warning}} #end + ## Load the configuration from a seperate document. + #loadConfig('XWiki.RegistrationConfig') + #set ($passwordFields = []) + #definePasswordFields($passwordFields, 'p', 'p2', $passwordOptions) {{html}} <form action="$doc.getURL()" method="post" class="xform third" onsubmit="if($('p').value == '') {alert('$services.localization.render("xe.admin.passwordReset.step2.error.emptyPassword")'); return false;} else if($('p').value != $('p2').value) {alert('$services.localization.render("xe.admin.passwordReset.step2.error.verificationMismatch")'); return false; }"> @@ -114,12 +120,8 @@ it is secured against unprivileged editing. <input type="hidden" name="u" value="$!escapetool.xml($userName)"/> <input type="hidden" name="v" value="$!escapetool.xml($validationString)"/> </div> - <dl> - <dt><label for="p">$services.localization.render('xe.admin.passwordReset.step2.newPassword.label')</label></dt> - <dd><input id="p" type="password" name="p" value="" size="20"/></dd> - <dt><label for="p2">$services.localization.render('xe.admin.passwordReset.step2.newPasswordVerification.label')</label></dt> - <dd><input id="p2" type="password" value="" name="p2" size="20"/></dd> - </dl> + ## A null $request is passed as parameter, since we won't display inserted passwords after a request with error. + #generateHtml($passwordFields, $NULL) <div class="buttons"> <span class="buttonwrapper"><input type="submit" value="$services.localization.render('xe.admin.passwordReset.step2.submit')" class="button"/></span> </div> @@ -146,10 +148,9 @@ it is secured against unprivileged editing. #if ($isValid) #set ($vuserDoc = $xwiki.getDocumentAsAuthor($userName)) #if ($request.getParameterMap().containsKey('p'))## Second step, set the user password - #if($password == '') - #displayForm($services.localization.render('xe.admin.passwordReset.step2.error.emptyPassword') $userName $validationString) - #elseif($password != $password2) - #displayForm($services.localization.render('xe.admin.passwordReset.step2.error.verificationMismatch') $userName $validationString) + #validateFields($passwordFields, $request) + #if(!$allFieldsValid) + #displayForm($stringtool.join($allFieldsErrors, "<br/>") $userName $validationString) #else $vuserDoc.getObject('XWiki.XWikiUsers').set('password', $password) #set($discard = $vuserDoc.removeObjects($verifClass)) diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/ApplicationResources.properties b/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/ApplicationResources.properties index 02138c4f68e4..1e92b2c2faf8 100644 --- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/ApplicationResources.properties +++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/ApplicationResources.properties @@ -2512,6 +2512,7 @@ xe.admin.registration.passwordMustContainLowercase=The password must contain at xe.admin.registration.passwordMustContainUppercase=The password must contain at least one uppercase character. xe.admin.registration.passwordMustContainNumber=The password must contain at least one number. xe.admin.registration.passwordMustContainSymbol=The password must contain at least one symbol character. +xe.admin.registration.fieldWithNoName=ERROR: Field with no name. ### Attachment picker macro xe.attachmentSelector.gallery.title=Attachments diff --git a/xwiki-platform-core/xwiki-platform-web/src/main/webapp/templates/passwd.vm b/xwiki-platform-core/xwiki-platform-web/src/main/webapp/templates/passwd.vm index eeb17c794bb4..c18c69a19887 100644 --- a/xwiki-platform-core/xwiki-platform-web/src/main/webapp/templates/passwd.vm +++ b/xwiki-platform-core/xwiki-platform-web/src/main/webapp/templates/passwd.vm @@ -3,18 +3,10 @@ ### ### #template("startpage.vm") -### -### Validation script -### - +#template("register_macros.vm") +#set ($fields = []) +#loadConfig('XWiki.RegistrationConfig') +#definePasswordFields($fields, 'password', 'password2', $passwordOptions)
@@ -28,55 +20,68 @@ document.observe('xwiki:dom:loaded', function() { #set($errorMessage = "") #set($password = $request.password) #set($password2 = $request.password2) + ## Server-side checking #if($isCurrentUsersProfile || !$hasAdmin) #set($user = $xwiki.getUser()) #set($isOriginalPasswordValid = $user.checkPassword($request.originalpassword)) #if(!$isOriginalPasswordValid) - #set($errorMessage = $services.localization.render('platform.core.profile.passwd.invalidOriginalPassword')) + #set($discard = $errorMessage.add($services.localization.render('platform.core.profile.passwd.invalidOriginalPassword'))) #set($isValidPassword = false) #end #end - #if($password != $password2) - #set($errorMessage = $services.localization.render('platform.core.profile.passwd.passwordMissmatch')) - #set($isValidPassword = false) - #elseif($password == '') - #set($errorMessage = $services.localization.render('platform.core.profile.passwd.passwordCannotBeEmpty')) - #set($isValidPassword = false) - #elseif($password.length() < 6) - #set($errorMessage = $services.localization.render('platform.core.profile.passwd.passwordTooShort')) - #set($isValidPassword = false) - #end - #if($isValidPassword) + #validateFields($fields, $request) + #set ($isValidPassword = $isValidPassword && $allFieldsValid) + #if(!$isValidPassword) + #set ($discard = $errorMessage.addAll($allFieldsErrors)) + #else + #set ($isValidPassword = true) $doc.set('password', $password, $userObject) $doc.save("$services.localization.render('platform.core.profile.passwd.passwordChanged')", true) #end #end - #if($request.password) - #if($isValidPassword) - $services.localization.render('platform.core.profile.passwd.success') - $services.localization.render('platform.core.profile.passwd.return') - #else - $errorMessage - #end + #if($request.password && $isValidPassword) + $services.localization.render('platform.core.profile.passwd.success') + $services.localization.render('platform.core.profile.passwd.return') #end #if(!$isValidPassword) - #set($discard = $xwiki.jsfx.use('uicomponents/widgets/validation/livevalidation_prototype.js')) - #set($discard = $xwiki.ssfx.use('uicomponents/widgets/validation/livevalidation.css', true)) $services.localization.render('platform.core.profile.passwd.instructions') - -
+ ## We won't ask for the original password if the user is admin and not updating his own profile. + ## In all other cases, we ask for it. + #if (!$hasAdmin || $isCurrentUsersProfile) + #set ($discard = $fields.add(0, { + 'name': 'originalpassword', + 'label': $services.localization.render('platform.core.profile.passwd.originalPassword'), + 'params': { + 'type': 'password', + 'size': '60', + 'autocomplete': 'off' + }, + 'validate': { + 'mandatory': { + 'failureMessage': $services.localization.render('core.validation.required.message') + }, + 'programmaticValidation': { + 'code': '$user.checkPassword($request.originalpassword)', + 'failureMessage': $services.localization.render('platform.core.profile.passwd.invalidOriginalPassword') + }, + 'hideOkayMessage': 'true' + } + })) + #end + ## Change the label for new password fields. + ## Even if the user has admin rights or not, the fields for new password will be the last two. + #set ($index = $fields.size() - 2) + #set ($fields[$index].label = $services.localization.render('platform.core.profile.passwd.newPassword')) + #set ($index = $index + 1) + ## This key fits better. + #set ($fields[$index].label = $services.localization.render('xe.admin.passwordReset.step2.newPasswordVerification.label')) + ## CSRF prevention -
- #if($isCurrentUsersProfile || !$hasAdmin) -
-
- #end -
-
-
-
-
+ #generateHtml($fields, $NULL) + #if ($request.password && !$isValidPassword) + $stringtool.join($errorMessage, "
")
+ #end
$services.localization.render('platform.core.profile.passwd.cancel') @@ -93,6 +98,6 @@ document.observe('xwiki:dom:loaded', function() { #xwikimessageboxstart($services.localization.render('platform.core.noticeMessageType') $services.localization.render('platform.core.profile.passwd.notaUser')) #xwikimessageboxend() #end -
## mainContentArea -
## main +
+ #template("endpage.vm") diff --git a/xwiki-platform-core/xwiki-platform-web/src/main/webapp/templates/register_macros.vm b/xwiki-platform-core/xwiki-platform-web/src/main/webapp/templates/register_macros.vm new file mode 100644 index 000000000000..6396ba52bb06 --- /dev/null +++ b/xwiki-platform-core/xwiki-platform-web/src/main/webapp/templates/register_macros.vm @@ -0,0 +1,415 @@ +## Defines what server generated error messages should look like +## The error message when a field is entered incorrectly +#set ($failureMessageParams = {'class': 'LV_validation_message LV_invalid'}) +## 'LV_validation_message LV_invalid' depends on this: +$xwiki.get('ssfx').use('uicomponents/widgets/validation/livevalidation.css', true) +## +## The * next to the fields to denote they are mandatory. +#set ($fieldMandatoryStar = {'class': 'xRequired'}) +## +#macro (definePasswordFields, $fields, $passwordFieldName, $confirmPasswordFieldName, $passwordOptions) + #set ($passwordRegexes = []) + #if ("$!passwordOptions" == "") + #set ($passwordLength = 6) + #else + #set ($passwordLength = $passwordOptions.passwordLength) + #end + #set ($patternLength = "/.{" + $passwordLength + ",}/") + #set ($passwordRegex = + { + 'pattern' : $patternLength, + 'failureMessage' : $services.localization.render('xe.admin.registration.passwordTooShort') + }) + #set ($discard = $passwordRegexes.add($passwordRegex)) + #if ($passwordOptions.passwordRuleOneUpperCaseEnabled) + #set ($passwordRegex = + { + 'pattern' : '/[A-Z]+/', + 'failureMessage' : $services.localization.render('xe.admin.registration.passwordMustContainUppercase') + }) + #set ($discard = $passwordRegexes.add($passwordRegex)) + #end + #if ($passwordOptions.passwordRuleOneLowerCaseEnabled) + #set ($passwordRegex = + { + 'pattern' : '/[a-z]+/', + 'failureMessage' : $services.localization.render('xe.admin.registration.passwordMustContainLowercase') + }) + #set ($discard = $passwordRegexes.add($passwordRegex)) + #end + #if ($passwordOptions.passwordRuleOneNumberEnabled) + #set ($passwordRegex = + { + 'pattern' : '/[0-9]+/', + 'failureMessage' : $services.localization.render('xe.admin.registration.passwordMustContainNumber') + }) + #set ($discard = $passwordRegexes.add($passwordRegex)) + #end + #if ($passwordOptions.passwordRuleOneSymbolEnabled) + #set ($passwordRegex = + { + 'pattern' : '/\W+/', + 'failureMessage' : $services.localization.render('xe.admin.registration.passwordMustContainSymbol') + }) + #set ($discard = $passwordRegexes.add($passwordRegex)) + #end + #set ($discard = $fields.add({ + 'name': $passwordFieldName, + 'label': $services.localization.render('core.register.password'), + 'params': { + 'type': 'password', + 'autocomplete': 'off', + 'size': '60' + }, + 'validate': { + 'mandatory': { + 'failureMessage': $services.localization.render('core.validation.required.message') + }, + 'regexes': $passwordRegexes + } + })) +## +## The confirm password field, mandatory, must match password field, and must also be 6+ characters long. + #set ($discard = $fields.add({ + 'name': $confirmPasswordFieldName, + 'label': $services.localization.render('core.register.passwordRepeat'), + 'params': { + 'type': 'password', + 'autocomplete': 'off', + 'size': '60' + }, + 'validate': { + 'mandatory': { + 'failureMessage': $services.localization.render('core.validation.required.message') + }, + 'mustMatch': { + 'name': $passwordFieldName, + 'failureMessage': $services.localization.render('xe.admin.registration.passwordMismatch') + } + } + })) +#end +## +#* + * Generate HTML form. + * + * @param $fields The array of fields to use for generating HTML code. + * @param $request The request that is made by submitting the form. + *# +#macro (generateHtml, $fields, $request) + ## Put the same values back into the fields (if is there any problem with a field from the request that is made). + #getParams($fields, $request) +
+ #foreach ($field in $fields) + #if ($field.name) + #set ($fieldName = $field.name) + #if ($field.label) + #set ($label = $field.label) +
+
+ #end + ## If no tag then default tag is + #if ($field.tag) + #set ($tag = $field.tag) + #else + #set ($tag = 'input') + #end +
<$tag id="$fieldName" ## + #set ($params = $field.params) + ## If no name parameter is specified, then we use the field name. + #if (!$params.name) + #set ($discard = $params.put('name', $fieldName)) + #end + #foreach ($entry in $params.entrySet()) + ## If a parameter is specified as '' then we don't include it. + #if ($entry.value != '') + $entry.key="$escapetool.xml($entry.value)" ## + #end + #end + > + #if ($field.error) + $field.error + #end +
+ #else + $services.localization.render('xe.admin.registration.fieldWithNoName') + #end + #end +
+ #generateJavascript($fields) +#end +## +#* + * Generate the Javascript for interacting with LiveValidation. + * + * @param $fields The array of fields which to validate. + *### +#macro (generateJavascript, $fields) + ## Load only the JS since the CSS is loaded after the declaration of 'LV_validation_message LV_invalid'. + #set ($discard = $xwiki.jsfx.use('uicomponents/widgets/validation/livevalidation_prototype.js')) + +#end +#* + * Get parameters from request so that values will be filled in if there is a mistake + * in one of the entries. Entries will be returned to fields[n].params.value + * Fields will not be returned if they have either noReturn or error specified. + * + * @param $fields The array of fields to get parameters for. + * @param $request The request that is made, from which the params will be returned. + *### +#macro (getParams $fields, $request) + #foreach ($field in $fields) + #if ($field.name && $!request.get($field.name)) + #if (!$field.noReturn && !$field.error) + #if (!$field.params) + #set ($params = {}) + #set ($discard = $field.put('params', $params)) + #else + #set ($params = $field.params) + #end + #set ($discard = $params.put('value', $request.get($field.name))) + #end + #end + #end +#end +####### Validation macros ######### +#macro(validateRegex $fieldValue, $fieldName, $regex, $error) + #if($regex.get('pattern') && $regex.get('failureMessage')) + ## Make Java regexes more compatible with Perl/js style regexes by removing leading and trailing / + #if($regex.get('pattern').length() > 1) + #set($pattern = $regex.get('pattern')) + #if($pattern.lastIndexOf('/') < $pattern.length() - 1) + ERROR: In field: ${fieldName}: regex validation does not allow flags after the /, please fix [${pattern}]. + #end + #set($pattern = $pattern.substring($mathtool.add(1, $pattern.indexOf('/')), $pattern.lastIndexOf('/'))) + #else + ## I don't expect this but want to maintain compatibility. + #set($pattern = $regex.get('pattern')) + #end + #if($regextool.find($value, $pattern).isEmpty()) + #set($error = $regex.get('failureMessage')) + #end + #elseif($regex.get('pattern')) + ERROR: In field: ${fieldName}: regex validation must include failureMessage. + #end +#end +#* + * Server side validation, this is necessary for security and because not everyone has Javascript + * + * @param $fields The array of fields to validate. + * @param $request An XWikiRequest object which made the register request, used to get parameters. + *### +#macro(validateFields, $fields, $request) + #set ($allFieldsValid = true) + #set ($allFieldsErrors = []) + #foreach($field in $fields) + #if($field.get('validate') && $field.get('name')) + #set($fieldName = $field.get('name')) + #set($validate = $field.get('validate')) + #set($error = '') + #set($value = $request.get($fieldName)) + #if("$!value" != '' || $field.get('type') == 'html') + ## + ## mustMatch validation + #if($error == '' && $validate.get('mustMatch')) + #set($mustMatch = $validate.get('mustMatch')) + #if($mustMatch.get('name') && $mustMatch.get('failureMessage')) + #if($request.get($fieldName) != $request.get($mustMatch.get('name'))) + #set($error = $mustMatch.get('failureMessage')) + #end + #else + ERROR: In field: ${fieldName}: mustMatch validation required both name + (of field which this field must match) and failureMessage. + #end + #end + ## + ## Regex validation + ## We won't bother with regex validation if there is no entry, that would defeat the purpose of 'mandatory' + #if($error == '' && $validate.get('regex') && $value && $value != '') + #set($regex = $validate.get('regex')) + #validateRegex($value, $fieldName, $regex, $error) + #end + ## List of regex validation + #if($error == '' && $validate.get('regexes') && $value && $value != '') + #set($regexes = $validate.get('regexes')) + #foreach ($regex in $regexes) + #validateRegex($value, $fieldName, $regex, $error) + #end + #end + ## + ## If regex and mustMatch validation passed, try programmatic validation + #if($error == '' && $validate.get('programmaticValidation')) + #set($pv = $validate.get('programmaticValidation')) + #if($pv.get('code') && $pv.get('failureMessage')) + #set($pvReturn = "#evaluate($pv.get('code'))") + #if($pvReturn.indexOf('failed') != -1) + #set($error = $pv.get('failureMessage')) + #end + #else + ERROR: In field: ${fieldName}: programmaticValidation requires code and failureMessage + #end + #end + #else + ## + ## If no content, check if content is mandatory + #if($validate.get('mandatory')) + #set($mandatory = $validate.get('mandatory')) + #if($mandatory.get('failureMessage')) + #set($error = $mandatory.get('failureMessage')) + #else + ERROR: In field: ${fieldName}: mandatory validation requires a failureMessage + #end + #end + #end + #if($error != '') + #set($discard = $field.put('error', $error)) + #set ($discard = $allFieldsErrors.add($error)) + #set($allFieldsValid = false) + #end + #elseif(!$field.get('name')) + ERROR: Field with no name. + #end##if(validate) + #end##loop +#end##macro + +#* + * Get the configuration from the configuration object. + * + * @param $configDocumentName The name of the document to get the configuration from. + *### +#macro(loadConfig, $configDocumentName) + #set($configurationClassName = 'XWiki.Registration') + #set($configDocument = $xwiki.getDocument($configDocumentName)) + #set ($passwordOptions = {}) + #if(!$configDocument || !$configDocument.getObject($configurationClassName)) + ## No config document, load defaults. + #set($heading = "$services.localization.render('core.register.title')") + #set($welcomeMessage = "$services.localization.render('core.register.welcome')") + #set($useLiveValidation = true) + #set($defaultFieldOkayMessage = "$services.localization.render('core.validation.valid.message')") + #set($loginButton = true) + #set($defaultRedirect = "$xwiki.getURL($services.model.resolveDocument('', 'default', $doc.documentReference.extractReference('WIKI')))") + #set($userFullName = "$request.get('register_first_name') $request.get('register_last_name')") + #set($registrationSuccessMessage = '{{info}}$services.localization.render("core.register.successful", ["[[${userFullName}>>${userSpace}${userName}]]", ${userName}]){{/info}}') + #set($passwordOptions.passwordLength = 6) + #set($passwordOptions.passwordRuleOneLowerCaseEnabled = false) + #set($passwordOptions.passwordRuleOneUpperCaseEnabled = false) + #set($passwordOptions.passwordRuleOneNumberEnabled = false) + #set($passwordOptions.passwordRuleOneSymbolEnabled = false) + #else + #set($configObject = $configDocument.getObject($configurationClassName)) + #if ($xcontext.action == 'register') + #set ($heading = "(% id='document-title'%)((( = #evaluate($configObject.getProperty('heading').getValue()) = )))(%%)") + #else + #set ($heading = "= #evaluate($configObject.getProperty('heading').getValue()) =") + #end + #set($welcomeMessage = "#evaluate($configObject.getProperty('welcomeMessage').getValue())") + #if($configObject.getProperty('liveValidation_enabled').getValue() == 1) + #set($useLiveValidation = true) + #end + #set($defaultFieldOkayMessage = "#evaluate($configObject.getProperty('liveValidation_defaultFieldOkMessage').getValue())") + #if($configObject.getProperty('loginButton_enabled').getValue() == 1) + #set($loginButton = true) + #end + #if($configObject.getProperty('loginButton_autoLogin_enabled').getValue() == 1) + #set($autoLogin = true) + #end + #set($defaultRedirect = "#evaluate($configObject.getProperty('defaultRedirect').getValue())") + #set($registrationSuccessMessage = "$configObject.getProperty('registrationSuccessMessage').getValue()") + #if($configObject.getProperty('requireCaptcha').getValue() == 1) + #set($requireCaptcha = true) + #end + #if($configObject.getProperty('passwordRuleOneLowerCaseEnabled').getValue() == 1) + #set($passwordOptions.passwordRuleOneLowerCaseEnabled = true) + #end + #if($configObject.getProperty('passwordRuleOneUpperCaseEnabled').getValue() == 1) + #set($passwordOptions.passwordRuleOneUpperCaseEnabled = true) + #end + #if($configObject.getProperty('passwordRuleOneNumberEnabled').getValue() == 1) + #set($passwordOptions.passwordRuleOneNumberEnabled = true) + #end + #if($configObject.getProperty('passwordRuleOneSymbolEnabled').getValue() == 1) + #set($passwordOptions.passwordRuleOneSymbolEnabled = true) + #end + #set($passwordOptions.passwordLength = $configObject.getProperty('passwordLength').getValue()) + #if ("$!passwordOptions.passwordLength" == "" || $passwordOptions.passwordLength > 1) + #set ($passwordOptions.passwordLength = 6) + #end + #end +#end \ No newline at end of file