diff --git a/xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-test/xwiki-platform-administration-test-docker/src/test/it/org/xwiki/administration/test/ui/RegisterIT.java b/xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-test/xwiki-platform-administration-test-docker/src/test/it/org/xwiki/administration/test/ui/RegisterIT.java index 9ce64e18065e..db104a5c051e 100644 --- a/xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-test/xwiki-platform-administration-test-docker/src/test/it/org/xwiki/administration/test/ui/RegisterIT.java +++ b/xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-test/xwiki-platform-administration-test-docker/src/test/it/org/xwiki/administration/test/ui/RegisterIT.java @@ -243,7 +243,8 @@ void registerJohnSmith(boolean isModal, boolean closedWiki, boolean withRegistra { AbstractRegistrationPage registrationPage = setUp(testUtils, isModal, closedWiki, withRegistrationConfig); registrationPage.fillInJohnSmithValues(); - assertTrue(validateAndRegister(testUtils, isModal, registrationPage)); + assertTrue(validateAndRegister(testUtils, isModal, registrationPage), String.format("isModal: %s close " + + "wiki: %s withRegistrationConfig: %s", isModal, closedWiki, withRegistrationConfig)); tryToLoginAsJohnSmith(testUtils, AbstractRegistrationPage.JOHN_SMITH_PASSWORD, registrationPage); } @@ -345,7 +346,6 @@ void registerWikiSyntaxName(boolean isModal, boolean closedWiki, boolean withReg AbstractRegistrationPage.JOHN_SMITH_USERNAME, password, password, "wiki@example.com"); assertTrue(validateAndRegister(testUtils, isModal, registrationPage), String.format("isModal: %s close " + "wiki: %s withRegistrationConfig: %s", isModal, closedWiki, withRegistrationConfig)); - // TODO: looks like a pretty strange behavior, there might be a message box title missing somewhere String messagePrefix = closedWiki ? "" : "Information "; 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 957d4d847c11..f677d13a6dce 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 @@ -155,31 +155,7 @@ * Removing or renaming any of these fields will result in undefined behavior. * *### - #set($fields = []) - ## - ## The first name field, no checking. - #set($field = - {'name' : 'register_first_name', - 'label' : $services.localization.render('core.register.firstName'), - 'params' : { - 'type' : 'text', - 'size' : '60', - 'autocomplete' : 'given-name' - } - }) - #set($discard = $fields.add($field)) - ## - ## The last name field, no checking. - #set($field = - {'name' : 'register_last_name', - 'label' : $services.localization.render('core.register.lastName'), - 'params' : { - 'type' : 'text', - 'size' : '60', - 'autocomplete' : 'family-name' - } - }) - #set($discard = $fields.add($field)) + #set($mainFields = []) ## ## The user name field, mandatory and programmatically checked to make sure the username doesn't exist. #set($field = @@ -201,7 +177,7 @@ } } }) - #set($discard = $fields.add($field)) + #set($discard = $mainFields.add($field)) ## Make sure the chosen user name is not already taken ## This macro is called by programmaticValidation for xwikiname (above) #macro (nameAvailable, $name) @@ -212,7 +188,7 @@ ## ##The password field, mandatory and must be at least 6 characters long. ##The confirm password field, mandatory, must match password field, and must also be 6+ characters long. - #definePasswordFields($fields, 'register_password', 'register2_password', $registrationConfig.passwordOptions) + #definePasswordFields($mainFields, 'register_password', 'register2_password', $registrationConfig.passwordOptions) ## ## The email address field, regex checked with an email pattern. Mandatory if registration uses email verification #set($field = @@ -233,7 +209,7 @@ #if($registrationConfig.useEmailVerification) #set($field.validate.mandatory = {'failureMessage' : $services.localization.render('core.validation.required.message')}) #end - #set($discard = $fields.add($field)) + #set($discard = $mainFields.add($field)) ## #********* ## Uncomment this code to see an example of how you can easily add a field to the registration page @@ -259,7 +235,7 @@ }, 'doAfterRegistration' : '#saveFavoriteColor()' }) - #set($discard = $fields.add($field)) + #set($discard = $mainFields.add($field)) ## Save the user's favorite color on their user page. #macro(saveFavoriteColor) #set($xwikiname = $request.get('xwikiname')) @@ -270,6 +246,31 @@ $userDoc.saveWithProgrammingRights("Saved favorite color from registration form.") #end *********### + #set($aboutYouFields = []) + ## + ## The first name field, no checking. + #set($field = + {'name' : 'register_first_name', + 'label' : $services.localization.render('core.register.firstName'), + 'params' : { + 'type' : 'text', + 'size' : '60', + 'autocomplete' : 'given-name' + } + }) + #set($discard = $aboutYouFields.add($field)) + ## + ## The last name field, no checking. + #set($field = + {'name' : 'register_last_name', + 'label' : $services.localization.render('core.register.lastName'), + 'params' : { + 'type' : 'text', + 'size' : '60', + 'autocomplete' : 'family-name' + } + }) + #set($discard = $aboutYouFields.add($field)) ## ## To disable the CAPTCHA on this page, comment out the next entry. ## The CAPTCHA, not really an input field but still defined the same way. @@ -283,10 +284,11 @@ ## Also, not filled back in if there is an error ('noReturn'). #set($field = {'name' : 'captcha_placeholder', - 'label' : $services.localization.render('core.captcha.instruction'), + 'label' : $services.localization.render('core.captcha.label') 'skipLabelFor' : true, 'type' : 'html', - 'html' : "$!{services.captcha.default.display()}", + 'html' : "<span class='xHint'>$escapetool.xml($services.localization.render('core.captcha.instruction')) + </span> $!{services.captcha.default.display()}", 'validate' : { 'programmaticValidation' : { 'code' : '#if (!$services.captcha.default.isValid())failed#end', @@ -295,7 +297,7 @@ }, 'noReturn' : true }) - #set($discard = $fields.add($field)) + #set($discard = $aboutYouFields.add($field)) #end ## Pass the redirect parameter on so that the login page may redirect to the right place. ## Not necessary in Firefox 3.0.10 or Opera 9.64, I don't know about IE or Safari. @@ -305,7 +307,10 @@ 'type' : 'hidden' } }) - #set($discard = $fields.add($field)) + #set($discard = $aboutYouFields.add($field)) + #set($fields = []) + #set($discard = $fields.addAll($mainFields)) + #set($discard = $fields.addAll($aboutYouFields)) ## ####################################################################### ## The Code. @@ -365,7 +370,11 @@ #end </div> ## Note that the macro inject the form_token field. - #generateHtml($fields, $request) + #generateHtml($mainFields, $request, 'false') + <h2>$services.localization.render('core.register.aboutYou')</h2> + #generateHtml($aboutYouFields, $request, 'false') + <input type="hidden" name="form_token" value="$services.csrf.getToken()" /> + #generateJavascript($fields) <p class="buttons"> <span class="buttonwrapper"> <input type="submit" value="$services.localization.render('core.register.submit')" class="button"/> @@ -452,7 +461,9 @@ #set($redirect = $registrationConfig.defaultRedirect) #end ## Display a "registration successful" message - + ## Define some strings which may be used by the welcome message + #set($firstName = $escapetool.xml($!request.get('register_first_name'))) + #set($lastName = $escapetool.xml($!request.get('register_last_name'))) #evaluate($registrationConfig.registrationSuccessMessage) ## Empty line prevents message from being forced into a <p> block. @@ -470,6 +481,14 @@ <span class="buttonwrapper"> <input type="submit" value="$services.localization.render('login')" class="button"/> </span> + #set ($mainPage = $services.wiki.currentWikiDescriptor.mainPageReference) + #if ($xwiki.checkAccess($mainPage, 'view')) + <span class="buttonwrapper"> + <a href="$!xwiki.getURL($mainPage)" rel="home" class="button secondary"> + $services.localization.render('core.register.successful.backtohome') + </a> + </span> + #end </div> </form> ## We don't want autoLogin if we are administrators adding users... @@ -487,6 +506,16 @@ ## #end## createUser Macro {{/velocity}} + + registration_success_hero.svg + image/svg+xml + UTF-8 + xwiki:XWiki.Admin + 1.2 + + PHN2ZyBkYXRhLW5hbWU9IkxheWVyIDEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjY4MC44Mzg1OCIgaGVpZ2h0PSI1ODQuMjMyMDciIHZpZXdCb3g9IjAgMCA2ODAuODM4NTggNTg0LjIzMjA3IiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PHBhdGggaWQ9ImI5Y2NhZTVhLWZmZGQtNGY1Yy05YzFlLTA1YWY5ZjBmMzM3Mi0xNzciIGRhdGEtbmFtZT0iUGF0aCA0MzgiIGQ9Ik0zMTAuNzA1NjksNjk0LjAyODE4YTI0LjIxNDU5LDI0LjIxNDU5LDAsMCwwLDIzLjM4MjY5LTQuMTE4NzdjOC4xODk3Ny02Ljg3NDQxLDEwLjc1OC0xOC4xOTYsMTIuODQ2Ny0yOC42ODE5MWw2LjE3OTczLTMxLjAxNjU3LTEyLjkzNzcsOC45MDgzN2MtOS4zMDQ2NSw2LjQwNjQxLTE4LjgxODI2LDEzLjAxODY2LTI1LjI2MDExLDIyLjI5Nzg1cy05LjI1MjIzLDIxLjk0NzA3LTQuMDc3OTIsMzEuOTg4IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjU5LjU4MDcxIC0xNTcuODgzOTYpIiBmaWxsPSIjZTZlNmU2Ii8+PHBhdGggaWQ9ImY0YWQxZDA2LWJkMDMtNGNlZC1hNWM0LWMxOWE2NWFiNGVlNS0xNzgiIGRhdGEtbmFtZT0iUGF0aCA0MzkiIGQ9Ik0zMTIuNzAzNCw3MzMuNzM4NzRjLTEuNjI4MzktMTEuODYzNjgtMy4zMDM4Mi0yMy44ODA3OC0yLjE1ODg0LTM1Ljg3MTY3LDEuMDE0NjctMTAuNjQ5MzIsNC4yNjM3My0yMS4wNDg4MSwxMC44NzgzMS0yOS41NzkzOGE0OS4yMDU5Miw0OS4yMDU5MiwwLDAsMSwxMi42MjQ2Ni0xMS40NDAzOWMxLjI2MjE1LS43OTY0OCwyLjQyNDA5LDEuMjAzNTQsMS4xNjczMywxLjk5N2E0Ni43Nzk0OSw0Ni43Nzk0OSwwLDAsMC0xOC41MDQ0NiwyMi4zMjU2MmMtNC4wMjg1NywxMC4yNDYwNy00LjY3NTQ1LDIxLjQxNTgyLTMuOTgxNTQsMzIuMzAwMy40MTk0NCw2LjU4MjE4LDEuMzEwNzQsMTMuMTIxMiwyLjIwNTg4LDE5LjY1MjUxYTEuMTk4MTcsMS4xOTgxNywwLDAsMS0uODA4LDEuNDIyNTEsMS4xNjM0OCwxLjE2MzQ4LDAsMCwxLTEuNDIyNTMtLjgwOFoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yNTkuNTgwNzEgLTE1Ny44ODM5NikiIGZpbGw9IiNmMmYyZjIiLz48cGF0aCBpZD0iYmFmNzg1ZjgtYjRjNi00MmNmLTg1YmQtOGExNjAzNzg0NWY3LTE3OSIgZGF0YS1uYW1lPSJQYXRoIDQ0MiIgZD0iTTMyNC40MjQ0Myw3MTQuNzAyMjlhMTcuODI1MTMsMTcuODI1MTMsMCwwLDAsMTUuNTMxNDEsOC4wMTg2MmM3Ljg2NDQtLjM3MzE4LDE0LjQxODA2LTUuODU5NzMsMjAuMzE3MTMtMTEuMDcwMjdsMTcuNDUyLTE1LjQwODgtMTEuNTQ5ODctLjU1MjgxYy04LjMwNjE5LS4zOTc4NC0xNi44MjY3Mi0uNzcxLTI0LjczODEzLDEuNzkzMzhzLTE1LjIwNzU4LDguNzI2MzktMTYuNjU0LDE2LjkxNTQxIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjU5LjU4MDcxIC0xNTcuODgzOTYpIiBmaWxsPSIjZTZlNmU2Ii8+PHBhdGggaWQ9ImExNGU0MzMwLTcxMjUtNGUwMy1hODU2LWQ2NDUzYzM0ZjZjYy0xODAiIGRhdGEtbmFtZT0iUGF0aCA0NDMiIGQ9Ik0zMDguMTAwNDIsNzQwLjU1ODQzYzcuODM5NzItMTMuODcxNDIsMTYuOTMyMzQtMjkuMjg3OTQsMzMuMTgwOC0zNC4yMTU1MmEzNy4wMjYwOSwzNy4wMjYwOSwwLDAsMSwxMy45NTU0NS0xLjQ0MWMxLjQ4MTg5LjEyOCwxLjExMTc5LDIuNDExNzQtLjM2NywyLjI4NDU0YTM0LjM5ODMzLDM0LjM5ODMzLDAsMCwwLTIyLjI3MTY0LDUuODkyMTVjLTYuMjc5OTQsNC4yNzQ1My0xMS4xNjk3NSwxMC4yMTc1NS0xNS4zMDc4MSwxNi41MTkwNy0yLjUzNTExLDMuODYwNTEtNC44MDU3Niw3Ljg4NDQ1LTcuMDc2NDIsMTEuOTAzQzMwOS40ODgyNCw3NDIuNzg1MTMsMzA3LjM2NjQxLDc0MS44NTc1OSwzMDguMTAwNDIsNzQwLjU1ODQzWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI1OS41ODA3MSAtMTU3Ljg4Mzk2KSIgZmlsbD0iI2YyZjJmMiIvPjxwYXRoIGlkPSJhYzIwYTEwNi03ZWI4LTRhNDUtODgzNS02NzRlZjNiZjMyMjItMTgxIiBkYXRhLW5hbWU9IlBhdGggMTQxIiBkPSJNOTM1LjM5NTcsNTY5LjMxNjU0SDUwMy4xODA5MmE1LjAzMDE0LDUuMDMwMTQsMCwwLDEtNS4wMjM1OS01LjAyMzU5VjE2Mi45MDc1NGE1LjAzMDE3LDUuMDMwMTcsMCwwLDEsNS4wMjM1OS01LjAyMzU4SDkzNS4zOTU3YTUuMDMwMTcsNS4wMzAxNywwLDAsMSw1LjAyMzU5LDUuMDIzNThWNTY0LjI5MmE1LjAyOTIyLDUuMDI5MjIsMCwwLDEtNS4wMjM1OSw1LjAyMzU5WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI1OS41ODA3MSAtMTU3Ljg4Mzk2KSIgZmlsbD0iI2ZmZiIvPjxwYXRoIGlkPSJhODg3ODA3OS1jN2NkLTQwNmYtYTQzNC04YjE1YjkxNGI5YjQtMTgyIiBkYXRhLW5hbWU9IlBhdGggMTQxIiBkPSJNOTM1LjM5NTcsNTY5LjMxNjU0SDUwMy4xODA5MmE1LjAzMDE0LDUuMDMwMTQsMCwwLDEtNS4wMjM1OS01LjAyMzU5VjE2Mi45MDc1NGE1LjAzMDE3LDUuMDMwMTcsMCwwLDEsNS4wMjM1OS01LjAyMzU4SDkzNS4zOTU3YTUuMDMwMTcsNS4wMzAxNywwLDAsMSw1LjAyMzU5LDUuMDIzNThWNTY0LjI5MmE1LjAyOTIyLDUuMDI5MjIsMCwwLDEtNS4wMjM1OSw1LjAyMzU5Wk01MDMuMTgwOTIsMTU5Ljg4OTQ0YTMuMDE4MDgsMy4wMTgwOCwwLDAsMC0zLjAxMTUyLDMuMDExNTFWNTY0LjI5MmEzLjAxODA4LDMuMDE4MDgsMCwwLDAsMy4wMTE1MiwzLjAxMTUySDkzNS4zOTU3YTMuMDE3MTcsMy4wMTcxNywwLDAsMCwzLjAxMTUzLTMuMDExNTJWMTYyLjkwNzU0YTMuMDE4MDksMy4wMTgwOSwwLDAsMC0zLjAxMTUzLTMuMDExNTFaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjU5LjU4MDcxIC0xNTcuODgzOTYpIiBmaWxsPSIjY2FjYWNhIi8+PHBhdGggaWQ9ImFmNjRmOTYxLWU5YTItNGM1My1hMzMzLTUwNjBjN2Y4NTBkMi0xODMiIGRhdGEtbmFtZT0iUGF0aCAxNDIiIGQ9Ik03MDcuNDEwMjMsMjYyLjE4NTI4YTMuNDEwNTMsMy40MTA1MywwLDAsMCwwLDYuODIxMDVIODk0LjU1MzA1YTMuNDEwNTMsMy40MTA1MywwLDAsMCwwLTYuODIxMDVaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjU5LjU4MDcxIC0xNTcuODgzOTYpIiBmaWxsPSIjZTRlNGU0Ii8+PHBhdGggaWQ9ImJhYWQ0Y2ZiLTE1OGQtNDQzOS05Y2MzLTIyNDc1YmY0N2IyMi0xODQiIGRhdGEtbmFtZT0iUGF0aCAxNDMiIGQ9Ik03MDcuNDEwMjMsMjgyLjY1MDM3YTMuNDEwNTQsMy40MTA1NCwwLDAsMCwwLDYuODIxMDZoOTUuNTQwMTlhMy40MTA1NCwzLjQxMDU0LDAsMCwwLDAtNi44MjEwNloiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yNTkuNTgwNzEgLTE1Ny44ODM5NikiIGZpbGw9IiNlNGU0ZTQiLz48cGF0aCBpZD0iZjM0NTYyNzktOTFlNS00OWFkLWFhNDMtOTgzOGIyNmZiNmNhLTE4NSIgZGF0YS1uYW1lPSJQYXRoIDE0MiIgZD0iTTU0My44NDE0NiwzOTIuNzA0NmEzLjQxMDU0LDMuNDEwNTQsMCwwLDAsMCw2LjgyMTA2aDM1MC44OTM3YTMuNDEwNTQsMy40MTA1NCwwLDAsMCwwLTYuODIxMDZaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjU5LjU4MDcxIC0xNTcuODgzOTYpIiBmaWxsPSIjZTRlNGU0Ii8+PHBhdGggaWQ9ImEzMjg4YWRmLTQ5ZjgtNDg1Zi04YWU5LTFlNGYxYTEzZDg0OS0xODYiIGRhdGEtbmFtZT0iUGF0aCAxNDMiIGQ9Ik01NDMuODQxNDYsNDEzLjE2OTdhMy40MTA1NCwzLjQxMDU0LDAsMCwwLDAsNi44MjEwNkg4MDMuMTMyNTRhMy40MTA1NCwzLjQxMDU0LDAsMCwwLDAtNi44MjEwNloiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yNTkuNTgwNzEgLTE1Ny44ODM5NikiIGZpbGw9IiNlNGU0ZTQiLz48cGF0aCBpZD0iZTYzYTViNDgtNWE3ZC00MGEyLWI5YjAtNmFkZWMzMjYzNDhhLTE4NyIgZGF0YS1uYW1lPSJQYXRoIDE0MiIgZD0iTTU0My44NDE0Niw0MzMuMTcxNzdhMy40MTA1NCwzLjQxMDU0LDAsMCwwLDAsNi44MjEwNmgzNTAuODkzN2EzLjQxMDU0LDMuNDEwNTQsMCwwLDAsMC02LjgyMTA2WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI1OS41ODA3MSAtMTU3Ljg4Mzk2KSIgZmlsbD0iI2U0ZTRlNCIvPjxwYXRoIGlkPSJhMWM2NjliNC1kZmMzLTRjZmEtYTdiZS02NmI3MTM5OTg0NGQtMTg4IiBkYXRhLW5hbWU9IlBhdGggMTQzIiBkPSJNNTQzLjg0MTQ2LDQ1My42MzY4N2EzLjQxMDU0LDMuNDEwNTQsMCwwLDAsMCw2LjgyMTA2SDgwMy4xMzI1NGEzLjQxMDU0LDMuNDEwNTQsMCwwLDAsMC02LjgyMTA2WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI1OS41ODA3MSAtMTU3Ljg4Mzk2KSIgZmlsbD0iI2U0ZTRlNCIvPjxwYXRoIGlkPSJiZmVjNTBkMS1mZmIxLTRkZTYtYTllZi1hMTA4NWU0MGUwMTYtMTg5IiBkYXRhLW5hbWU9IlBhdGggMTQyIiBkPSJNNTQzLjg0MTQ2LDQ3NC4xNzE3N2EzLjQxMDU0LDMuNDEwNTQsMCwwLDAsMCw2LjgyMTA2aDM1MC44OTM3YTMuNDEwNTQsMy40MTA1NCwwLDAsMCwwLTYuODIxMDZaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjU5LjU4MDcxIC0xNTcuODgzOTYpIiBmaWxsPSIjZTRlNGU0Ii8+PHBhdGggaWQ9ImJjOTY5NmVjLWVjOTktNDFkNS05MTE2LTNhZDk3MzdhMzhhYy0xOTAiIGRhdGEtbmFtZT0iUGF0aCAxNDMiIGQ9Ik01NDMuODQxNDYsNDk0LjYzNjg3YTMuNDEwNTQsMy40MTA1NCwwLDAsMCwwLDYuODIxMDZIODAzLjEzMjU0YTMuNDEwNTQsMy40MTA1NCwwLDAsMCwwLTYuODIxMDZaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjU5LjU4MDcxIC0xNTcuODgzOTYpIiBmaWxsPSIjZTRlNGU0Ii8+PHBhdGggZD0iTTU5OS40MTk0MywzMjQuODI4MTJhNDksNDksMCwxLDEsNDguOTk5NTItNDlBNDkuMDU1NjcsNDkuMDU1NjcsMCwwLDEsNTk5LjQxOTQzLDMyNC44MjgxMloiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yNTkuNTgwNzEgLTE1Ny44ODM5NikiIGZpbGw9IiMzZTc5YmMiLz48cGF0aCBkPSJNNDUwLjY3ODMzLDUxMC4xMDA0MWExMi4yNDc1NCwxMi4yNDc1NCwwLDAsMC0xNC45NTMtMTEuMzYyMzFsLTE2LjE5NjQxLTIyLjgyNTIxLTE2LjI3MTM4LDYuNDU5NDUsMjMuMzI1MTksMzEuOTEyMzdhMTIuMzEzOTIsMTIuMzEzOTIsMCwwLDAsMjQuMDk1NTktNC4xODQzWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI1OS41ODA3MSAtMTU3Ljg4Mzk2KSIgZmlsbD0iI2EwNjE2YSIvPjxwYXRoIGQ9Ik00MTkuMTEyMTEsNTA4LjQwODg4bC00OS4wMDc3NC02My41Nzc3N0wzODguNDY3MTQsMzg3LjEyYzEuMzQ1NjMtMTQuNTA5MzYsMTAuNDI1LTE4LjU2MDg5LDEwLjgxMTM1LTE4LjcyNjQ1bC41ODkzLS4yNTI4MSwxNS45NzksNDIuNjExOS0xMS43MzIzNSwzMS4yODYyNSwyOC43OTY3MSw0OC40MzE5WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI1OS41ODA3MSAtMTU3Ljg4Mzk2KSIgZmlsbD0iIzNmM2Q1NiIvPjxwYXRoIGQ9Ik01ODkuMzA3OTQsMzEyLjQxOTkzYTEyLjI0NzU4LDEyLjI0NzU4LDAsMCwwLTEwLjE3MjE5LDE1Ljc4NjcybC0yMS41MDQ2MywxNy45MTI2OSw3LjY5ODE2LDE1LjcyMzI2LDMwLjAxMzQzLTI1LjcyMjcyYTEyLjMxMzkyLDEyLjMxMzkyLDAsMCwwLTYuMDM0NzctMjMuNjk5OTVaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjU5LjU4MDcxIC0xNTcuODgzOTYpIiBmaWxsPSIjYTA2MTZhIi8+PHBhdGggZD0iTTU5MC4wNjIwNiwzNDQuMDIyNDRsLTU5LjU5ODM1LDUzLjc3NjY1LTU4Ljk1ODE1LTEzLjg0NTc4Yy0xNC41Ny0uMjE5NzktMTkuMzExMzYtOC45NTg3LTE5LjUwNjI5LTkuMzMxMTNsLS4yOTc2MS0uNTY4LDQxLjI0ODktMTkuMjI1NzgsMzIuMDk5Nyw5LjI3ODI4LDQ2LjA2MDQ2LTMyLjQ1NTA5WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI1OS41ODA3MSAtMTU3Ljg4Mzk2KSIgZmlsbD0iIzNmM2Q1NiIvPjxwb2x5Z29uIHBvaW50cz0iMjI3LjI0OCA1NjguNDM3IDI0My4yNjEgNTY4LjQzNiAyNTAuODc4IDUwNi42NzIgMjI3LjI0NSA1MDYuNjczIDIyNy4yNDggNTY4LjQzNyIgZmlsbD0iI2EwNjE2YSIvPjxwYXRoIGQ9Ik00ODMuMzk3MzMsNzIxLjc0NDc2aDUwLjMyNjE0YTAsMCwwLDAsMSwwLDBWNzQxLjE4OWEwLDAsMCwwLDEsMCwwaC0zNi4yMDdhMTQuMTE5MTQsMTQuMTE5MTQsMCwwLDEtMTQuMTE5MTQtMTQuMTE5MTR2LTUuMzI1MDVBMCwwLDAsMCwxLDQ4My4zOTczMyw3MjEuNzQ0NzZaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg3NTcuNTczNDggMTMwNS4wMjY1NCkgcm90YXRlKDE3OS45OTczOCkiIGZpbGw9IiMyZjJlNDEiLz48cG9seWdvbiBwb2ludHM9IjE2My4yNDcgNTY4LjQzNyAxNzkuMjYgNTY4LjQzNiAxODYuODc4IDUwNi42NzIgMTYzLjI0NSA1MDYuNjczIDE2My4yNDcgNTY4LjQzNyIgZmlsbD0iI2EwNjE2YSIvPjxwYXRoIGQ9Ik00MTkuMzk3LDcyMS43NDQ3Nkg0NjkuNzIzMWEwLDAsMCwwLDEsMCwwVjc0MS4xODlhMCwwLDAsMCwxLDAsMGgtMzYuMjA3QTE0LjExOTE0LDE0LjExOTE0LDAsMCwxLDQxOS4zOTcsNzI3LjA2OTgxdi01LjMyNTA1YTAsMCwwLDAsMSwwLDBaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2MjkuNTcyNzMgMTMwNS4wMjk0Nikgcm90YXRlKDE3OS45OTczOCkiIGZpbGw9IiMyZjJlNDEiLz48cG9seWdvbiBwb2ludHM9IjE1Ny41NTIgMzQyLjk5MSAxNTguODU4IDQzNC40MiAxNjAuMTY1IDU1NC41ODQgMTg4Ljg5OSA1NTEuOTcyIDIwMy4yNjcgMzg2LjA5NCAyMjEuNTUzIDU1MS45NzIgMjUxLjIxOCA1NTEuOTcyIDI1NC4yMDYgMzg0Ljc4OCAyNDMuNzU3IDM0OC4yMTYgMTU3LjU1MiAzNDIuOTkxIiBmaWxsPSIjMmYyZTQxIi8+PHBhdGggZD0iTTQ3My4zNzQxNyw1MTMuMTUzMWMtMzEuMjY1MzMuMDAyMzktNjAuMDQ0NzEtMTQuMTQ4MzktNjAuNDMzMTktMTQuMzQyNjNsLS4zMjI3My0uMTYxMzYtMi42MjM3My02Mi45NjYzN2MtLjc2MDgyLTIuMjI1MDktMTUuNzQyNjMtNDYuMTMwOTEtMTguMjgtNjAuMDg2MjUtMi41NzA4My0xNC4xMzg4MiwzNC42ODg0Mi0yNi41NDc0MiwzOS4yMTMtMjcuOTk4NTNsMS4wMjY3OC0xMS4zNzQwNSw0MS43NTM2Ni00LjQ5OTE4LDUuMjkyLDE0LjU1MzYsMTQuOTc5NDIsNS42MTY4YTcuNDA5MjQsNy40MDkyNCwwLDAsMSw0LjU5MjEyLDguNzA0M2wtOC4zMjUzOSwzMy44NTYxOSwyMC4zMzMyNSwxMTIuMDEyNjYtNC4zNzc1NS4xODk0NkM0OTUuNzA5LDUxMS4zOTY1OCw0ODQuMzg0MjUsNTEzLjE1MjUsNDczLjM3NDE3LDUxMy4xNTMxWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI1OS41ODA3MSAtMTU3Ljg4Mzk2KSIgZmlsbD0iIzNmM2Q1NiIvPjxjaXJjbGUgY3g9IjQ1NC40NjczOCIgY3k9IjI5NC40NTk2NSIgcj0iMzAuMDYyODQiIHRyYW5zZm9ybT0ibWF0cml4KDAuODc3NDUsIC0wLjQ3OTY2LCAwLjQ3OTY2LCAwLjg3NzQ1LCAtMzQ1LjEyODI0LCA5Ni4xOTAzNykiIGZpbGw9IiNhMDYxNmEiLz48cGF0aCBkPSJNNDMwLjExNjYsMzIzLjU2MTMyYzUuNzI5MjYsNi4xMDI4OSwxNi4zNjkyNywyLjgyNjcyLDE3LjExNTgtNS41MTA2OWExMC4wNzE1MywxMC4wNzE1MywwLDAsMC0uMDEyNjgtMS45NDUyM2MtLjM4NTQ0LTMuNjkzMTEtMi41MTktNy4wNDYtMi4wMDgtMTAuOTQ1NDJhNS43Mzk3NCw1LjczOTc0LDAsMCwxLDEuMDUwNDYtMi42ODdjNC41NjU0OC02LjExMzU5LDE1LjI4MjYzLDIuNzM0NDQsMTkuNTkxMzgtMi44LDIuNjQyLTMuMzkzNTktLjQ2MzY0LTguNzM2NjQsMS41NjM4MS0xMi41Mjk1NiwyLjY3NTkxLTUuMDA2LDEwLjYwMTgzLTIuNTM2NTQsMTUuNTcyMjItNS4yNzgwOSw1LjUzMDE3LTMuMDUwMzIsNS4xOTk0LTExLjUzNTE3LDEuNTU5MDctMTYuNjk2MS00LjQzOTU1LTYuMjk0LTEyLjIyMzQ4LTkuNjUyNDEtMTkuOTEwNDQtMTAuMTM2NDNzLTE1LjMyMDk0LDEuNTkzOTQtMjIuNDk3NCw0LjM5MDY5Yy04LjE1MzkyLDMuMTc3NjctMTYuMjM5NjksNy41NjkyNS0yMS4yNTc0OSwxNC43MzktNi4xMDIxOCw4LjcxOTE5LTYuNjg5NDIsMjAuNDQxMzItMy42Mzc2LDMwLjYzNjc3QzQxOS4xMDIyMiwzMTEuMDAxMyw0MjUuNDM4MDUsMzE4LjU3NzY2LDQzMC4xMTY2LDMyMy41NjEzMloiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yNTkuNTgwNzEgLTE1Ny44ODM5NikiIGZpbGw9IiMyZjJlNDEiLz48cGF0aCBkPSJNNjQxLjU4MDcxLDc0MS45NjI2aC0zODFhMSwxLDAsMCwxLDAtMmgzODFhMSwxLDAsMCwxLDAsMloiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yNTkuNTgwNzEgLTE1Ny44ODM5NikiIGZpbGw9IiNjYWNhY2EiLz48cGF0aCBkPSJNNTk2LjU4OTg0LDI5NC4zMzU0NWEzLjQ4OCwzLjQ4OCwwLDAsMS0yLjM4MTM0LS45MzU1NWwtMTYuMTU3MjMtMTUuMDA3MzJhMy40OTk5NCwzLjQ5OTk0LDAsMCwxLDQuNzYzNjctNS4xMjg5MWwxMy42ODU1NSwxMi43MTE5MiwyNy4wNzY2Ni0yNy4wNzYxOGEzLjUsMy41LDAsMSwxLDQuOTQ5MjIsNC45NTAybC0yOS40NjA5NCwyOS40NjA5NEEzLjQ5Mjc1LDMuNDkyNzUsMCwwLDEsNTk2LjU4OTg0LDI5NC4zMzU0NVoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yNTkuNTgwNzEgLTE1Ny44ODM5NikiIGZpbGw9IiNmZmYiLz48L3N2Zz4= + 8462 + XWiki.Registration @@ -763,4 +792,109 @@ XWiki.XWikiGuest + + XWiki.Registration + 0 + XWiki.UIExtensionClass + 5f751597-be0d-4010-ad85-57f4b6c4bd18 + + XWiki.UIExtensionClass + + + + + + + + + 0 + content + 3 + Extension Content + 10 + 40 + 0 + com.xpn.xwiki.objects.classes.TextAreaClass + + + 0 + extensionPointId + 1 + Extension Point ID + 30 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + 0 + name + 2 + Extension ID + 30 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + 0 + parameters + 4 + Extension Parameters + 10 + 40 + 0 + com.xpn.xwiki.objects.classes.TextAreaClass + + + 0 + 0 + select + 0 + scope + 5 + Extension Scope + 0 + + |, + 1 + 0 + wiki=Current Wiki|user=Current User|global=Global + com.xpn.xwiki.objects.classes.StaticListClass + + + + {{velocity}}{{html clean="false"}} +## If xredirect is not set +#if (!$request.xredirect) + ## Define redirect URL value using current document relative request URL + #set($redirectURL = $escapetool.url($xwiki.relativeRequestURL)) +## If xredirect is already set +#else + ## Reuse the current value in the login link + #set($redirectURL = $escapetool.url($request.xredirect)) +#end +#if ($xcontext.user == 'XWiki.XWikiGuest' && !$xcontext.inactiveUserReference) +<li> + <a href="$xwiki.getURL('XWiki.XWikiLogin', 'login', "xredirect=$redirectURL&loginLink=1")" id="tmLogin" rel="nofollow">$escapetool.xml($services.localization.render('login'))</a> +</li> +#end +#if ($xcontext.user == 'XWiki.XWikiGuest' && !$xcontext.inactiveUserReference && $xwiki.hasAccessLevel('register', 'XWiki.XWikiPreferences')) +<li> +<a href="$xwiki.getURL('XWiki.XWikiRegister', 'register', "xredirect=$redirectURL")" id="tmRegister" rel="nofollow">$escapetool.xml($services.localization.render('register'))</a> +</li> +#end +{{/html}}{{/velocity}} + + + org.xwiki.platform.topmenu.right + + + org.xwiki.plaform.registration + + + order=40000 + + + wiki + + diff --git a/xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-ui/src/main/resources/XWiki/RegistrationConfig.xml b/xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-ui/src/main/resources/XWiki/RegistrationConfig.xml index 6d9b5914c331..57dc2dd59b5b 100644 --- a/xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-ui/src/main/resources/XWiki/RegistrationConfig.xml +++ b/xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-ui/src/main/resources/XWiki/RegistrationConfig.xml @@ -522,9 +522,30 @@ 0 - #set($message = $services.localization.render('core.register.successful', 'xwiki/2.1', ['USERLINK', $userName])) + #set($discard = $xwiki.ssx.use("XWiki.RegistrationConfig")) +#set($displayName = "$!firstName $!lastName") +#set($noReadableName = ($!firstName == "") || ($!lastName == "")) +#if($noReadableName) + #set($displayName = $userName) +#end +#set($headline = $services.localization.render('core.register.successful.welcome', [$displayName])) #set($userLink = $xwiki.getUserName("$userSpace$userName")) -{{info}}$message.replace('USERLINK', "{{html clean=false}}$userLink{{/html}}"){{/info}} +#set($successAndLogin = $services.localization.render('core.register.successful.successandlogin')) +[[image:registration_success_hero.svg||data-xwiki-image-style-alignment="center" height="50vh"]] + +{{html}} +<div class="registration-success-headline"> + <h2>$escapetool.xml($headline) </h2> + #if(!$noReadableName) + <p class="registration-success-subtitle"> + ($escapetool.xml($userName)) + </p> + #end +</div> +<p class="registration-success-hint"> + $escapetool.xml($successAndLogin) +</p> +{{/html}} 0 @@ -533,4 +554,138 @@ {{translation key="core.register.welcome"/}} + + XWiki.RegistrationConfig + 0 + XWiki.StyleSheetExtension + 6e9139ba-c4ac-4243-9ca7-9e935b0cc730 + + XWiki.StyleSheetExtension + + + + + + + + + 0 + long + 0 + select + forbidden + 0 + 0 + cache + 5 + Caching policy + 0 + + |, + 1 + 0 + long|short|default|forbid + com.xpn.xwiki.objects.classes.StaticListClass + + + PureText + 0 + PureText + code + 2 + Code + 0 + 20 + 50 + 0 + com.xpn.xwiki.objects.classes.TextAreaClass + + + 0 + 0 + select + forbidden + 0 + 0 + contentType + 6 + Content Type + 0 + + |, + 1 + 0 + CSS|LESS + com.xpn.xwiki.objects.classes.StaticListClass + + + 0 + name + 1 + Name + 30 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + 0 + select + yesno + parse + 4 + Parse content + 0 + com.xpn.xwiki.objects.classes.BooleanClass + + + 0 + 0 + select + forbidden + 0 + 0 + use + 3 + Use this extension + 0 + + |, + 1 + 0 + currentPage|onDemand|always + com.xpn.xwiki.objects.classes.StaticListClass + + + + long + + + .registration-success-headline { + display: flex; + align-items: baseline; + justify-content: center; +} + +.registration-success-headline > .registration-success-subtitle { + display: block; +} + +.registration-success-hint { + display: block; + text-align: center; +} + +/* Resize the hero image displayed when the registration is successful so that the buttons are always on screen without + needing to scroll. */ +img.wikigeneratedid { + height: 50vh; +} + + + + + + onDemand + + diff --git a/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/drawer.vm b/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/drawer.vm index be12627a0a0a..e63018881ee3 100644 --- a/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/drawer.vm +++ b/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/drawer.vm @@ -41,21 +41,6 @@ aria-label="$escapetool.xml($services.localization.render('core.menu.drawer.labe #elseif($xcontext.user == 'XWiki.XWikiGuest' && $xcontext.inactiveUserReference) $!xwiki.getUserName($xcontext.inactiveUserReference, false) $logoutLink - #else - ## If xredirect is not set - #if (!$request.xredirect) - ## Define redirect URL value using current document relative request URL - #set($redirectURL = $escapetool.url($xwiki.relativeRequestURL)) - ## If xredirect is already set - #else - ## Reuse the current value in the login link - #set($redirectURL = $escapetool.url($request.xredirect)) - #end - - $services.icon.renderHTML('log-in') $escapetool.xml($services.localization.render('login')) - #if ($xwiki.hasAccessLevel('register', 'XWiki.XWikiPreferences')) - $services.icon.renderHTML('log-in') $escapetool.xml($services.localization.render('register')) - #end #end ## ## UIX diff --git a/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/less/action-menus.less b/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/less/action-menus.less index e0e7b00dcd88..180f97d2cbf8 100644 --- a/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/less/action-menus.less +++ b/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/less/action-menus.less @@ -79,6 +79,16 @@ } } } +// Authentication buttons ======================================================== +// Reduce the spacing between both items if there's both +li:has(#tmLogin) + li #tmRegister { + padding-right: 8px; +} +li:has(+ li #tmRegister) #tmLogin { + padding-left: 8px; +} + +// Quick search ======================================================== #globalsearch { display: flex; diff --git a/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/less/drawer.less b/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/less/drawer.less index a2799f7a504f..9f39be8b4cdb 100644 --- a/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/less/drawer.less +++ b/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/less/drawer.less @@ -191,7 +191,7 @@ margin: 12px 0; } -// Special styling for login/logout/register ============================= -#tmLogin, #tmLogout, #tmRegister { +// Special styling for logout ============================= +#tmLogout { font-style: italic; } 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 5e39c4ab3554..7398bda6b982 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 @@ -1681,7 +1681,10 @@ core.register.invalidUsername=Invalid username provided. Please use only letters core.register.mailSenderWronglyConfigured=The user has been created but the validation email has not been sent. Please check the Mail Sending Configuration and consider recreating the user. core.register.invalidCaptcha=Incorrect CAPTCHA answer. core.register.registerFailed=Registration has failed due to unknown reasons. (Error code: {0}) -core.register.successful={0} ({1}): Registration successful. +core.register.successful.welcome=Welcome {0} +core.register.successful.successandlogin=Registration successful. You can now log into your account. +core.register.successful.backtohome=Back to Home +core.register.aboutYou = About you core.register.firstName=First Name core.register.lastName=Last Name core.register.username=Username @@ -1707,6 +1710,7 @@ core.validation.valid.message=Ok. # Captcha core.captcha.captchaAnswerIsWrong=Incorrect answer, please try again. +core.captcha.label=CAPTCHA core.captcha.instruction=Please validate the CAPTCHA to prove you are not a robot # History @@ -5655,6 +5659,11 @@ platform.index.spaceIndex=Space Index platform.index.spaceIndexDescription=Pages in the {0} space: platform.index.spaceIndexDocumentListCreate=Create a new page +####################################### +## until 16.10.0RC1 +####################################### +core.register.successful={0} ({1}): Registration successful. + ## Used to indicate where deprecated keys end #@deprecatedend diff --git a/xwiki-platform-core/xwiki-platform-test/xwiki-platform-test-ui/src/main/java/org/xwiki/test/ui/po/BasePage.java b/xwiki-platform-core/xwiki-platform-test/xwiki-platform-test-ui/src/main/java/org/xwiki/test/ui/po/BasePage.java index db0ef0374ada..0ba6fd8a2443 100644 --- a/xwiki-platform-core/xwiki-platform-test/xwiki-platform-test-ui/src/main/java/org/xwiki/test/ui/po/BasePage.java +++ b/xwiki-platform-core/xwiki-platform-test/xwiki-platform-test-ui/src/main/java/org/xwiki/test/ui/po/BasePage.java @@ -426,7 +426,6 @@ public boolean hasLoginLink() */ public LoginPage login() { - getDrawerMenu().toggle(); this.loginLink.click(); return new LoginPage(); } @@ -511,7 +510,6 @@ public void logout() */ public RegistrationPage register() { - getDrawerMenu().toggle(); this.registerLink.click(); return new RegistrationPage(); } diff --git a/xwiki-platform-core/xwiki-platform-test/xwiki-platform-test-ui/src/main/java/org/xwiki/test/ui/po/RegistrationPage.java b/xwiki-platform-core/xwiki-platform-test/xwiki-platform-test-ui/src/main/java/org/xwiki/test/ui/po/RegistrationPage.java index 3eb0ef141f2e..780e9c665dfe 100644 --- a/xwiki-platform-core/xwiki-platform-test/xwiki-platform-test-ui/src/main/java/org/xwiki/test/ui/po/RegistrationPage.java +++ b/xwiki-platform-core/xwiki-platform-test/xwiki-platform-test-ui/src/main/java/org/xwiki/test/ui/po/RegistrationPage.java @@ -61,9 +61,12 @@ public void clickRegister() */ public Optional getRegistrationSuccessMessage() { - List infos = getDriver().findElements(By.className("infomessage")); + List infos = getDriver().findElements( + By.xpath("//*[contains(@class, 'infomessage') or" + + " contains(@class, 'registration-success-headline')]")); for (WebElement info : infos) { - if (info.getText().contains("Registration successful.")) { + if (info.getText().contains("Registration successful.") || + info.getText().contains("Welcome ")) { return Optional.of(info.getText().replaceAll("\n", " ")); } } diff --git a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/register_macros.vm b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/register_macros.vm index 4aacec73774f..16e6d170fc3a 100644 --- a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/register_macros.vm +++ b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/register_macros.vm @@ -19,7 +19,7 @@ ## --------------------------------------------------------------------------- ## 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'}) +#set ($failureMessageParams = {'class': 'LV_invalid'}) ## 'LV_validation_message LV_invalid' depends on this: $xwiki.get('ssfx').use('uicomponents/widgets/validation/livevalidation.css', true) ## @@ -114,8 +114,12 @@ $xwiki.get('ssfx').use('uicomponents/widgets/validation/livevalidation.css', tru * * @param $fields The array of fields to use for generating HTML code. * @param $request The request that is made by submitting the form. + * @param $isOnlyBlock Whether the fields are the only block of the form *# -#macro (generateHtml, $fields, $request) +#macro (generateHtml, $fields, $request, $isOnlyBlock) + #if(!$isOnlyBlock) + #set($isOnlyBlock = 'true') + #end ## 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)
@@ -158,12 +162,67 @@ $xwiki.get('ssfx').use('uicomponents/widgets/validation/livevalidation.css', tru #end #end /> - #if ($field.error) - $field.error + + $services.icon.renderHTML('accept') ## + $services.icon.renderHTML('cross') ## + ## Add the proper content to the validation string if it's a failure on initialisation + ## If it's a regex validation, we also want to make sure to keep it shown at all times, we still put the + ## message. Note that the class displaying the state of this validation will change though. + #if($field.error && $field.error == $!validation.failureMessage || + $!extraValidationClass.toString().contains('regex')) + $!validation.failureMessage + #end + + #end + #set($extraValidationClass = '') + #if ($field.validate.mandatory) + #set($extraValidationClass = 'mandatory') + #set($validation = $field.validate.mandatory) + $validationContainer + #end + #if ($field.validate.mustMatch) + #set($extraValidationClass = 'must-match') + #set($validation = $field.validate.mustMatch) + $validationContainer + #end + #if ($field.validate.programmaticValidation) + #set($extraValidationClass = 'programmatic-validation') + #set($validation = $field.validate.programmaticValidation) + $validationContainer + #end + #if ($field.validate.regex) + #set($extraValidationClass = 'regex') + #set($validation = $field.validate.regex) + $validationContainer + #end + #foreach ($regex in $field.validate.regexes) + #set($extraValidationClass = 'regex-'+ $foreach.count) + #set($validation = $regex) + $validationContainer #end #end @@ -172,8 +231,10 @@ $xwiki.get('ssfx').use('uicomponents/widgets/validation/livevalidation.css', tru #end #end
- - #generateJavascript($fields) + #if ($isOnlyBlock == 'true') + + #generateJavascript($fields) + #end #end ## #macro (validateRegexJS $regex $fieldName) @@ -190,7 +251,12 @@ $xwiki.get('ssfx').use('uicomponents/widgets/validation/livevalidation.css', tru #set ($failMessage = $regex.failureMessage) #end #if ($pattern != '' && $failMessage != '' && !$regex.noscript) - ${fieldName}Validator.add(Validate.Format, {pattern: $pattern, failureMessage: "$failMessage"}); + ## We assume here that the field uses either `regex` or `regexes`, not both at the same time. + #if(!$validate.regex) + ${fieldName}Validator.add(Validate.Format, {pattern: $pattern, failureMessage: "$failMessage", identifier: 'regex-'+ $foreach.count}); + #else + ${fieldName}Validator.add(Validate.Format, {pattern: $pattern, failureMessage: "$failMessage", identifier: 'regex'}); + #end #end #end #* @@ -225,15 +291,15 @@ $xwiki.get('ssfx').use('uicomponents/widgets/validation/livevalidation.css', tru #if ($validate.mandatory) #set ($mandatory = $validate.mandatory) #if ($mandatory.failureMessage && !$mandatory.noscript) - ${fieldName}Validator.add(Validate.Presence, {failureMessage: "$!mandatory.failureMessage"}); + ${fieldName}Validator.add(Validate.Presence, {failureMessage: "$!mandatory.failureMessage", identifier: 'mandatory'}); #end #end ## #if ($validate.mustMatch) #set ($mustMatch = $validate.mustMatch) #if ($mustMatch.name && $mustMatch.failureMessage && !$mustMatch.noscript) - ${fieldName}Validator.add(Validate.Confirmation, {match: $$("input[name=$!mustMatch.name]")[0], - failureMessage: "$!mustMatch.failureMessage"}); + ${fieldName}Validator.add(Validate.Confirmation, {match: $$("input[name=$!mustMatch.name]")[0], + failureMessage: "$!mustMatch.failureMessage", identifier: 'must-match'}); #end #end ## diff --git a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-war/src/main/webapp/resources/uicomponents/widgets/validation/livevalidation.css b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-war/src/main/webapp/resources/uicomponents/widgets/validation/livevalidation.css index 3cbffc34b1fe..649e91d3ab99 100644 --- a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-war/src/main/webapp/resources/uicomponents/widgets/validation/livevalidation.css +++ b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-war/src/main/webapp/resources/uicomponents/widgets/validation/livevalidation.css @@ -3,7 +3,6 @@ font-size: 0.8em; line-height: 1.8em; margin: 0 0 0 0.5em; - position: absolute; } .LV_valid { @@ -14,3 +13,19 @@ color: $theme.notificationErrorColor; } +.LV_validation_message_valid_icon, +.LV_validation_message_invalid_icon { + display: none; + margin-right: .5rem; +} + +.LV_valid .LV_validation_message_valid_icon, +.LV_invalid .LV_validation_message_invalid_icon { + display: unset; +} + +/* We don't want to display anything when some kind of validations are successful.*/ +.LV_valid.mandatory, +.LV_valid.must-match { + display: none; +} \ No newline at end of file diff --git a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-war/src/main/webapp/resources/uicomponents/widgets/validation/livevalidation_prototype.js b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-war/src/main/webapp/resources/uicomponents/widgets/validation/livevalidation_prototype.js index 962cc6ad75e0..2739d54c2d5a 100644 --- a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-war/src/main/webapp/resources/uicomponents/widgets/validation/livevalidation_prototype.js +++ b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-war/src/main/webapp/resources/uicomponents/widgets/validation/livevalidation_prototype.js @@ -100,17 +100,15 @@ LiveValidation.prototype = { // hooks beforeValidation: function(){}, beforeValid: function(){}, - onValid: function(){ this.insertMessage(this.createMessageSpan()); this.addFieldClass(); }, + onValid: function(){this.addFieldClass(); }, afterValid: function(){}, beforeInvalid: function(){}, - onInvalid: function(){ this.insertMessage(this.createMessageSpan()); this.addFieldClass(); }, + onInvalid: function(){this.addFieldClass(); }, afterInvalid: function(){}, afterValidation: function(){}, }, optionsObj || {}); var node = this.options.insertAfterWhatNode || this.element; this.options.insertAfterWhatNode = $(node); - this.messageHolder = this.createMessageSpan(); - this.options.insertAfterWhatNode.up().appendChild(this.messageHolder); Object.extend(this, this.options); // copy the options to the actual object // add to form if it has been provided if(this.form){ @@ -171,18 +169,36 @@ LiveValidation.prototype = { } } this.validations = []; - this.removeMessageAndFieldClass(); + this.removeFieldClass(); }, /** - * adds a validation to perform to a LiveValidation object + * adds a validation to perform on a LiveValidation object * * @param validationFunction {Function} - validation function to be used (ie Validate.Presence ) * @param validationParamsObj {Object} - parameters for doing the validation, if wanted or necessary * @return {Object} - the LiveValidation object itself so that calls can be chained */ add: function(validationFunction, validationParamsObj){ - this.validations.push( { type: validationFunction, params: validationParamsObj || {} } ); + let validationMessageHolder; + /* The identifier helps us give specific behaviour to different kinds of validations. The user expectations change + * depending on the validation type, and this identifier makes sure we can retrieve the right message from the set + * of messages in the template. */ + if (validationParamsObj.identifier) { + validationMessageHolder = this.options.insertAfterWhatNode.up().querySelector("." + validationParamsObj.identifier); + /* We use the failure message as a fallback for the success message for regexes. This ensures that the regex stays + * shown to the user at all times. */ + if (validationParamsObj.identifier.includes("regex") && validationParamsObj.validMessage == null) { + validationParamsObj.validMessage = validationParamsObj.failureMessage; + } + } else { + /* If we don't have an identifier for the validation, this is a legacy use of the livevalidation. + We create a message holder from scratch to make it compatible with the way the new implementation works. */ + validationMessageHolder = this.createMessageSpan(); + this.options.insertAfterWhatNode.up().appendChild(validationMessageHolder); + } + /* We add one validation object to the list of validations for the current field. */ + this.validations.push( { type: validationFunction, params: validationParamsObj || {}, messageHolder: validationMessageHolder } ); return this; }, @@ -204,7 +220,6 @@ LiveValidation.prototype = { * makes the validation wait the alotted time from the last keystroke */ deferValidation: function(e){ - if(this.wait >= 300) this.removeMessageAndFieldClass(); if(this.timeout) clearTimeout(this.timeout); this.timeout = setTimeout(this.validate.bind(this), this.wait); }, @@ -222,7 +237,7 @@ LiveValidation.prototype = { */ doOnFocus: function(){ this.focused = true; - this.removeMessageAndFieldClass(); + this.removeFieldClass(); }, /** @@ -264,11 +279,11 @@ LiveValidation.prototype = { doValidations: function(){ this.validationFailed = false; for(var i = 0, len = this.validations.length; i < len; ++i){ - this.validationFailed = !this.validateElement(this.validations[i].type, this.validations[i].params); - if(this.validationFailed) return false; + let isValid = this.validateElement(this.validations[i].type, this.validations[i].params); + this.insertMessage(this.validations[i], isValid); + this.validationFailed = this.validationFailed || !isValid; } - this.message = this.validMessage; - return true; + return !this.validationFailed; }, /** @@ -364,50 +379,64 @@ LiveValidation.prototype = { /** Message insertion methods **************************** * - * These are only used in the onValid and onInvalid callback functions and so if you overide the default callbacks, - * you must either impliment your own functions to do whatever you want, or call some of these from them if you + * These are only used in the onValid and onInvalid callback functions and so if you override the default callbacks, + * you must either implement your own functions to do whatever you want, or call some of these from them if you * want to keep some of the functionality */ /** - * makes a span containg the passed or failed message + * makes a span to contain the passed or failed message * - * @return {HTMLSpanObject} - a span element with the message in it + * @return {HTMLSpanObject} - an empty span element to contain the message. */ createMessageSpan: function(){ var span = document.createElement('span'); + span.classList.add(this.messageClass); span.setAttribute('aria-live', 'polite'); return span; }, - + /** - * inserts the element to contain the message in place of the element that already exists (if it does) - * - * @param elementToInsert {HTMLElementObject} - an element node to insert - */ - insertMessageHolder: function(elementToInsert){ - var className = this.validationFailed ? this.invalidClass : this.validClass; - $(elementToInsert).addClassName(this.messageClass + ' ' + className); - var parent = this.options.insertAfterWhatNode.up(); - var nxtSibling = this.options.insertAfterWhatNode.next(); - if (nxtSibling) { - parent.insertBefore(elementToInsert, nxtSibling); - }else{ - parent.appendChild(elementToInsert); + * inserts the message in its container. + */ + insertMessage: function(validation, isValid) { + let messageHolder = validation.messageHolder; + if(validation.params.validMessage=="" && !validation.params.validMessage) return; // dont insert anything if validMessage is an empty string + if( (this.displayMessageWhenEmpty && (this.elementType == LiveValidation.CHECKBOX || this.element.value == '')) || + this.element.value != '' || + validation.params.identifier.includes("regex")){ + this.removeMessageClass(messageHolder); + /* If the isValid state is null, we assume that the validation did not occur yet. We do not add any class that + * would give a false hint towards a result. */ + if(isValid != null) { + this.addMessageClass(messageHolder, isValid); + } + // We change just the lastChild textContent. This last child is a text node. This allows to not remove the icons. + let validMessage = validation.params.validMessage ? validation.params.validMessage : this.options.validMessage; + let newMessageContent = isValid ? validMessage : validation.params.failureMessage; + if (messageHolder.lastChild != null) { + messageHolder.lastChild.textContent = newMessageContent; + } else { + messageHolder.textContent = newMessageContent; + } + } }, /** - * inserts the message in its container of the message that already exists (if it does) + * Add a class for the holder of the validation message. */ - insertMessage: function() { - this.removeMessage(); - if(!this.validationFailed && !this.validMessage) return; // dont insert anything if validMesssage has been set to false or empty string - if( (this.displayMessageWhenEmpty && (this.elementType == LiveValidation.CHECKBOX || this.element.value == '')) || this.element.value != '' ){ - var className = this.validationFailed ? this.invalidClass : this.validClass; - this.messageHolder.addClassName(this.messageClass + ' ' + className); - this.messageHolder.textContent = this.message; - } + removeMessageClass: function(messageHolder) { + messageHolder.removeClassName(this.invalidClass); + messageHolder.removeClassName(this.validClass); + }, + + /** + * Add a class for the holder of the validation message. + */ + addMessageClass: function(messageHolder, isValid) { + let className = isValid ? this.validClass : this.invalidClass; + messageHolder.addClassName(className); }, /** @@ -425,15 +454,18 @@ LiveValidation.prototype = { }, /** - * removes the message element if it exists + * Empties out the message elements if they exist */ removeMessage: function(){ - this.messageHolder.className = 'message-holder'; - this.messageHolder.textContent = ''; + for (let i = 0; i < this.validations.length; i++) { + let messageHolder = this.validations[i].messageHolder; + this.removeMessageClass(messageHolder); + messageHolder.lastChild.textContent = ''; + } }, /** - * removes the class that has been applied to the field to indicte if valid or not + * removes the class that has been applied to the field to indicate if valid or not */ removeFieldClass: function(){ this.element.removeClassName(this.invalidFieldClass);