You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by lo...@apache.org on 2021/07/08 13:41:18 UTC
[syncope] branch master updated: [SYNCOPE-1435] New wicket enduser
(#275)
This is an automated email from the ASF dual-hosted git repository.
loredicola pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git
The following commit(s) were added to refs/heads/master by this push:
new a2ac11d [SYNCOPE-1435] New wicket enduser (#275)
a2ac11d is described below
commit a2ac11d426a504581c7c714f4016dcede910f8ac
Author: Lorenzo <lo...@tirasa.net>
AuthorDate: Thu Jul 8 15:41:10 2021 +0200
[SYNCOPE-1435] New wicket enduser (#275)
---
.../syncope/client/ui/commons/BaseLogin.java | 10 +-
.../syncope/client/ui/commons/Constants.java | 2 +
.../commons/markup/html/form/AjaxPalettePanel.java | 30 +
.../markup/html/form/BinaryFieldDownload.java | 2 +-
.../client/ui/commons/panels/CardPanel.java | 66 +
.../client/ui/commons}/themes/AdminLTE.java | 2 +-
.../themes/AdminLTECssResourceReference.java | 2 +-
.../ui/commons/wizards/any/PasswordPanel.java | 20 +-
.../client/ui/commons/panels/CardPanel.html} | 12 +-
.../client/ui/commons}/themes/css/AdminLTE.css | 0
.../ui/commons}/themes/js/AdminLTE-app.min.js | 0
client/idrepo/console/pom.xml | 1 -
.../client/console/SyncopeWebApplication.java | 10 +-
.../syncope/client/console/pages/BasePage.java | 18 +-
.../apache/syncope/client/console/pages/Login.java | 6 -
.../console/panels/SchemaTypeWizardBuilder.java | 3 +-
.../client/console/status/ChangePasswordModal.java | 2 +-
.../markup/html/bootstrap/dialog/BaseModal.java | 2 -
.../wicket/markup/html/form/BinaryFieldPanel.java | 2 +
.../wizards/any/AbstractAttrsWizardStep.java | 3 +-
.../client/console/wizards/any/UserDetails.java | 2 +-
client/idrepo/enduser/pom.xml | 13 +-
...tants.java => BookmarkablePageLinkBuilder.java} | 24 +-
.../syncope/client/enduser/PreferenceManager.java | 172 +++
.../SyncopeEnduserRequestCycleListener.java | 104 ++
.../client/enduser/SyncopeEnduserSession.java | 264 ++--
.../client/enduser/SyncopeWebApplication.java | 188 +--
.../client/enduser/assets/SyncopeEnduserCss.java | 49 -
.../EnduserConstants.java} | 21 +-
.../init/ClassPathScanImplementationLookup.java | 27 +-
.../client/enduser/layout/AnyLayoutUtils.java | 116 --
.../client/enduser/layout/CustomizationOption.java | 14 -
.../client/enduser/layout/UserFormLayoutInfo.java | 16 +-
.../enduser/markup/html/form/AjaxDownload.java | 88 --
.../enduser/markup/html/form/BinaryFieldPanel.java | 29 +-
.../syncope/client/enduser/navigation/Navbar.java | 153 ---
.../enduser/pages/AbstractChangePassword.java | 89 ++
.../syncope/client/enduser/pages/BasePage.java | 161 +++
.../{BaseEnduserWebPage.java => Dashboard.java} | 31 +-
.../client/enduser/pages/EditChangePassword.java | 80 ++
.../client/enduser/pages/EditSecurityQuestion.java | 210 +++
.../syncope/client/enduser/pages/EditUser.java | 62 +
.../apache/syncope/client/enduser/pages/Login.java | 54 +-
.../client/enduser/pages/MustChangePassword.java | 35 +-
.../apache/syncope/client/enduser/pages/Self.java | 137 --
.../enduser/pages/SelfConfirmPasswordReset.java | 137 +-
.../client/enduser/pages/SelfPasswordReset.java | 202 ++-
.../client/enduser/pages/SelfRegistration.java | 86 ++
.../syncope/client/enduser/pages/SelfResult.java | 67 +
.../enduser/panels/AbstractAnyFormPanel.java | 95 ++
.../client/enduser/panels/AbstractFormPanel.java | 63 +
.../client/enduser/panels/AnyFormPanel.java | 184 +++
.../client/enduser/panels/ChangePasswordPanel.java | 199 +++
.../client/enduser/panels/SelfPwdResetPanel.java | 217 ---
.../syncope/client/enduser/panels/Sidebar.java | 200 +++
.../client/enduser/panels/UserFormPanel.java | 161 +++
.../client/enduser/panels/UserSelfFormPanel.java | 135 ++
.../{wizards => panels}/any/AbstractAttrs.java | 84 +-
.../enduser/{wizards => panels}/any/DerAttrs.java | 32 +-
.../any/Resources.java => panels/any/Details.java} | 31 +-
.../enduser/{wizards => panels}/any/Groups.java | 91 +-
.../{wizards => panels}/any/PlainAttrs.java | 197 ++-
.../client/enduser/panels/any/Resources.java | 87 ++
.../client/enduser/panels/any/SelfUserDetails.java | 46 +
.../client/enduser/panels/any/UserDetails.java | 113 ++
.../enduser/{wizards => panels}/any/VirAttrs.java | 63 +-
.../any => panels/captcha}/CaptchaPanel.java | 23 +-
.../client/enduser/resources/CaptchaResource.java | 56 -
.../client/enduser/rest/AnyTypeRestClient.java | 3 +-
.../client/enduser/rest/BaseRestClient.java | 27 +-
.../client/enduser/rest/GroupRestClient.java | 19 +-
.../client/enduser/rest/RoleRestClient.java | 80 --
.../client/enduser/rest/SchemaRestClient.java | 3 +-
.../client/enduser/rest/SyncopeRestClient.java | 8 +-
.../client/enduser/rest/UserSelfRestClient.java | 42 +-
.../markup/head/MetaHeaderItem.java} | 36 +-
.../SelfUpdate.java => widgets/BaseWidget.java} | 12 +-
.../client/enduser/widgets/UserProfileWidget.java | 65 +
.../enduser/wizards/any/AnyWizardBuilder.java | 293 -----
.../enduser/wizards/any/EnduserAuxClasses.java | 45 -
.../client/enduser/wizards/any/UserDetails.java | 235 ----
.../enduser/wizards/any/UserWizardBuilder.java | 133 --
.../AdminLTE_plugins/dataTables.bootstrap4.min.css | 1 +
.../META-INF/resources/css/accessibility.css} | 30 +-
.../css/accessibility/accessibilityFont.css | 101 ++
.../css/accessibility/accessibilityHC.css | 296 +++++
.../resources/META-INF/resources/css/fonts.css | 89 ++
...-latin-ext_cyrillic_latin_cyrillic-ext-300.woff | Bin 0 -> 113012 bytes
...latin-ext_cyrillic_latin_cyrillic-ext-300.woff2 | Bin 0 -> 86284 bytes
...-ext_cyrillic_latin_cyrillic-ext-300italic.woff | Bin 0 -> 46356 bytes
...ext_cyrillic_latin_cyrillic-ext-300italic.woff2 | Bin 0 -> 35780 bytes
...-latin-ext_cyrillic_latin_cyrillic-ext-600.woff | Bin 0 -> 113196 bytes
...latin-ext_cyrillic_latin_cyrillic-ext-600.woff2 | Bin 0 -> 86120 bytes
...-ext_cyrillic_latin_cyrillic-ext-600italic.woff | Bin 0 -> 46280 bytes
...ext_cyrillic_latin_cyrillic-ext-600italic.woff2 | Bin 0 -> 35928 bytes
...-latin-ext_cyrillic_latin_cyrillic-ext-700.woff | Bin 0 -> 112628 bytes
...latin-ext_cyrillic_latin_cyrillic-ext-700.woff2 | Bin 0 -> 85436 bytes
...tin-ext_cyrillic_latin_cyrillic-ext-italic.woff | Bin 0 -> 46364 bytes
...in-ext_cyrillic_latin_cyrillic-ext-italic.woff2 | Bin 0 -> 35924 bytes
...in-ext_cyrillic_latin_cyrillic-ext-regular.woff | Bin 0 -> 113924 bytes
...n-ext_cyrillic_latin_cyrillic-ext-regular.woff2 | Bin 0 -> 86732 bytes
.../resources/META-INF/resources/css/login.css | 141 ++
.../META-INF/resources/css/search.css} | 63 +-
.../META-INF/resources/css/syncopeEnduser.css | 1377 ++++++++++++++++++++
.../META-INF/resources/js/accessibility.js | 228 ++++
.../META-INF/resources/js/copyToClipboard.js | 79 ++
.../META-INF/resources/js/syncopeEnduser.js} | 21 +-
.../src/main/resources/customFormAttributes.json | 1 -
.../src/main/resources/customFormLayout.json | 14 +
.../enduser/src/main/resources/enduser.properties | 9 +
...s_ru.properties => oidcclient-agent.properties} | 11 +-
...erties => SyncopeEnduserApplication.properties} | 25 +-
.../SyncopeEnduserApplication_fr_CA.properties | 105 ++
...ies => SyncopeEnduserApplication_it.properties} | 28 +-
...ies => SyncopeEnduserApplication_ja.properties} | 28 +-
... => SyncopeEnduserApplication_pt_BR.properties} | 28 +-
...ies => SyncopeEnduserApplication_ru.properties} | 28 +-
.../enduser/markup/html/form/BinaryFieldPanel.html | 5 +-
.../enduser/markup/html/form/MultiFieldPanel.html | 1 +
.../markup/html/form/MultiFieldPanel_ru.properties | 29 -
.../syncope/client/enduser/navigation/Navbar.html | 2 +-
.../client/enduser/pages/BaseEnduserWebPage.html | 57 -
.../syncope/client/enduser/pages/BasePage.html | 90 ++
.../BasePage.properties} | 11 +-
..._pt_BR.properties => BasePage_fr_CA.properties} | 10 +-
.../BasePage_it.properties} | 11 +-
.../BasePage_ja.properties} | 11 +-
.../BasePage_pt_BR.properties} | 11 +-
.../client/enduser/pages/BasePage_ru.properties} | 26 +-
.../syncope/client/enduser/pages/Dashboard.html | 5 +-
...Reset_pt_BR.properties => Dashboard.properties} | 4 +-
...pt_BR.properties => Dashboard_fr_CA.properties} | 4 +-
...et_pt_BR.properties => Dashboard_it.properties} | 4 +-
...et_pt_BR.properties => Dashboard_ja.properties} | 4 +-
...pt_BR.properties => Dashboard_pt_BR.properties} | 4 +-
...rd_pt_BR.properties => Dashboard_ru.properties} | 12 +-
.../client/enduser/pages/EditChangePassword.html | 7 +-
...rd.properties => EditChangePassword.properties} | 6 +-
.../pages/EditChangePassword_fr_CA.properties | 24 +
...properties => EditChangePassword_it.properties} | 7 +-
...properties => EditChangePassword_ja.properties} | 7 +-
...perties => EditChangePassword_pt_BR.properties} | 8 +-
...properties => EditChangePassword_ru.properties} | 10 +-
.../client/enduser/pages/EditSecurityQuestion.html | 64 +
....properties => EditSecurityQuestion.properties} | 8 +-
...operties => EditSecurityQuestion_it.properties} | 9 +-
...operties => EditSecurityQuestion_ja.properties} | 9 +-
...rties => EditSecurityQuestion_pt_BR.properties} | 9 +-
.../pages/EditSecurityQuestion_ru.properties | 24 +
.../syncope/client/enduser/pages/EditUser.html | 19 +-
...wordReset_ja.properties => EditUser.properties} | 8 +-
..._pt_BR.properties => EditUser_fr_CA.properties} | 4 +-
...set_pt_BR.properties => EditUser_it.properties} | 4 +-
...set_pt_BR.properties => EditUser_ja.properties} | 4 +-
..._pt_BR.properties => EditUser_pt_BR.properties} | 4 +-
...ord_pt_BR.properties => EditUser_ru.properties} | 12 +-
.../apache/syncope/client/enduser/pages/Login.html | 13 +-
.../client/enduser/pages/Login_ru.properties | 1 -
.../client/enduser/pages/MustChangePassword.html | 93 +-
.../enduser/pages/MustChangePassword.properties | 3 +
.../enduser/pages/MustChangePassword_it.properties | 3 +
.../enduser/pages/MustChangePassword_ja.properties | 3 +
.../pages/MustChangePassword_pt_BR.properties | 2 +
.../enduser/pages/MustChangePassword_ru.properties | 5 +-
.../apache/syncope/client/enduser/pages/Self.html | 1 +
.../enduser/pages/SelfConfirmPasswordReset.html | 48 +-
.../pages/SelfConfirmPasswordReset.properties | 6 +-
.../pages/SelfConfirmPasswordReset_it.properties | 6 +-
.../pages/SelfConfirmPasswordReset_ja.properties | 3 +
.../SelfConfirmPasswordReset_pt_BR.properties | 3 +
.../pages/SelfConfirmPasswordReset_ru.properties | 6 +-
.../SelfPasswordReset$SelfPwdResetPanel.html} | 23 +-
...SelfPasswordReset$SelfPwdResetPanel.properties} | 2 -
...sswordReset$SelfPwdResetPanel_fr_CA.properties} | 12 +-
...fPasswordReset$SelfPwdResetPanel_it.properties} | 2 -
...fPasswordReset$SelfPwdResetPanel_ja.properties} | 2 -
...sswordReset$SelfPwdResetPanel_pt_BR.properties} | 2 -
...fPasswordReset$SelfPwdResetPanel_ru.properties} | 2 -
.../client/enduser/pages/SelfPasswordReset.html | 33 +-
.../enduser/pages/SelfPasswordReset.properties | 4 +
...operties => SelfPasswordReset_fr_CA.properties} | 4 +
.../enduser/pages/SelfPasswordReset_it.properties | 4 +
.../enduser/pages/SelfPasswordReset_ja.properties | 4 +
.../pages/SelfPasswordReset_pt_BR.properties | 4 +
.../enduser/pages/SelfPasswordReset_ru.properties | 4 +
...elfPasswordReset.html => SelfRegistration.html} | 36 +-
.../syncope/client/enduser/pages/SelfResult.html | 74 ++
...swordReset.properties => SelfResult.properties} | 2 +-
...eset.properties => SelfResult_fr_CA.properties} | 2 +-
...rdReset.properties => SelfResult_it.properties} | 2 +-
...rdReset.properties => SelfResult_ja.properties} | 2 +-
...eset.properties => SelfResult_pt_BR.properties} | 2 +-
...rdReset.properties => SelfResult_ru.properties} | 2 +-
.../syncope/client/enduser/pages/SelfUpdate.html | 1 +
.../client/enduser/pages/WizardMgtPanel.html | 60 -
.../client/enduser/panels/ChangePasswordPanel.html | 61 +
.../ChangePasswordPanel.properties} | 1 +
.../ChangePasswordPanel_it.properties} | 0
.../ChangePasswordPanel_ja.properties} | 0
.../ChangePasswordPanel_pt_BR.properties} | 0
.../ChangePasswordPanel_ru.properties} | 2 -
.../syncope/client/enduser/panels/Sidebar.html | 58 +
.../client/enduser/panels/UserFormPanel.html | 45 +
.../UserFormPanel.properties} | 6 +-
.../UserFormPanel_it.properties} | 12 +-
.../UserFormPanel_ja.properties} | 12 +-
.../UserFormPanel_pt_BR.properties} | 12 +-
.../enduser/panels/UserFormPanel_ru.properties} | 21 +-
.../any/AbstractAttrs$Schemas.html | 1 +
.../{wizards => panels}/any/AbstractAttrs.html | 1 +
.../any/AbstractAttrs.properties | 0
.../any/AbstractAttrs_it.properties | 0
.../any/AbstractAttrs_ja.properties | 0
.../any/AbstractAttrs_pt_BR.properties | 0
.../any/AbstractAttrs_ru.properties | 1 -
.../enduser/{wizards => panels}/any/DerAttrs.html | 1 +
.../{wizards => panels}/any/DerAttrs.properties | 0
.../{wizards => panels}/any/DerAttrs_it.properties | 0
.../{wizards => panels}/any/DerAttrs_ja.properties | 0
.../any/DerAttrs_pt_BR.properties | 0
.../{wizards => panels}/any/DerAttrs_ru.properties | 1 -
.../enduser/{wizards => panels}/any/Groups.html | 6 +-
.../{wizards => panels}/any/Groups.properties | 0
.../{wizards => panels}/any/Groups_it.properties | 0
.../{wizards => panels}/any/Groups_ja.properties | 0
.../any/Groups_pt_BR.properties | 0
.../{wizards => panels}/any/Groups_ru.properties | 1 -
.../{wizards => panels}/any/PlainAttrs.html | 3 +-
.../{wizards => panels}/any/PlainAttrs.properties | 0
.../any/PlainAttrs_it.properties | 0
.../any/PlainAttrs_ja.properties | 0
.../any/PlainAttrs_pt_BR.properties | 0
.../any/PlainAttrs_ru.properties | 1 -
.../enduser/{wizards => panels}/any/Resources.html | 6 +-
.../{wizards => panels}/any/Resources.properties | 0
.../any/Resources_it.properties | 0
.../any/Resources_ja.properties | 0
.../any/Resources_pt_BR.properties | 0
.../any/Resources_ru.properties | 2 -
.../client/enduser/panels/any/SelfUserDetails.html | 3 +-
.../any/UserDetails$EditUserPasswordPanel.html | 3 +-
.../{wizards => panels}/any/UserDetails.html | 9 +-
.../{wizards => panels}/any/UserDetails.properties | 0
.../any/UserDetails_it.properties | 0
.../any/UserDetails_ja.properties | 0
.../any/UserDetails_pt_BR.properties | 0
.../any/UserDetails_ru.properties | 0
.../enduser/{wizards => panels}/any/VirAttrs.html | 1 +
.../{wizards => panels}/any/VirAttrs.properties | 0
.../{wizards => panels}/any/VirAttrs_it.properties | 0
.../{wizards => panels}/any/VirAttrs_ja.properties | 0
.../any/VirAttrs_pt_BR.properties | 0
.../{wizards => panels}/any/VirAttrs_ru.properties | 1 -
.../any => panels/captcha}/CaptchaPanel.html | 8 +-
.../any => panels/captcha}/CaptchaPanel.properties | 0
.../captcha}/CaptchaPanel_it.properties | 0
.../captcha}/CaptchaPanel_ja.properties | 0
.../client/enduser/widgets/UserProfileWidget.html | 50 +
.../UserProfileWidget.properties} | 5 +-
.../UserProfileWidget_fr_CA.properties} | 5 +-
.../UserProfileWidget_it.properties} | 6 +-
.../UserProfileWidget_ja.properties} | 6 +-
.../UserProfileWidget_pt_BR.properties} | 6 +-
.../UserProfileWidget_ru.properties} | 6 +-
.../wizards/any/CaptchaPanel_pt_BR.properties | 17 -
.../enduser/wizards/any/CaptchaPanel_ru.properties | 17 -
.../enduser/wizards/any/EnduserAuxClasses.html | 35 -
.../wizards/any/EnduserAuxClasses_it.properties | 17 -
.../wizards/any/EnduserAuxClasses_ja.properties | 17 -
.../wizards/any/EnduserAuxClasses_pt_BR.properties | 17 -
.../wizards/any/EnduserAuxClasses_ru.properties | 18 -
.../client/ui/commons/wizards/AjaxWizard.html | 46 -
.../ui/commons/wizards/AjaxWizardMgtButtonBar.html | 38 -
.../ui/commons/wizards/AjaxWizard_it.properties | 23 -
.../ui/commons/wizards/AjaxWizard_ja.properties | 23 -
.../ui/commons/wizards/AjaxWizard_pt_BR.properties | 23 -
.../ui/commons/wizards/AjaxWizard_ru.properties | 30 -
.../syncope/client/enduser/AbstractTest.java | 229 ++++
.../enduser/SyncopeEnduserApplicationTest.java | 138 ++
.../idrepo/enduser/src/test/resources/log4j2.xml | 54 +
.../syncope/client/enduser/pages/Flowable.java | 131 +-
.../client/enduser/panels/UserRequestDetails.java | 126 ++
.../client/enduser/rest/BaseRestClient.java | 2 +-
.../client/enduser/rest/UserRequestRestClient.java | 16 +-
.../common/ui/panels/UserRequestFormPanel.java | 220 ++++
.../enduser/pages/Flowable$UserRequestDetails.html | 36 -
.../syncope/client/enduser/pages/Flowable.html | 85 +-
.../client/enduser/pages/Flowable.properties | 1 +
.../client/enduser/pages/Flowable_it.properties | 1 +
.../client/enduser/pages/Flowable_ja.properties | 1 +
.../client/enduser/pages/Flowable_pt_BR.properties | 1 +
.../client/enduser/pages/Flowable_ru.properties | 1 +
.../client/enduser/pages/UserRequestForms.html | 1 +
.../client/enduser/panels/UserRequestDetails.html | 29 +-
.../common/ui/panels/UserRequestFormPanel.html | 13 +-
.../ui/panels/UserRequestFormPanel.properties | 3 +-
.../ui/panels/UserRequestFormPanel_it.properties | 3 +-
.../ui/panels/UserRequestFormPanel_ja.properties | 3 +-
.../panels/UserRequestFormPanel_pt_BR.properties | 3 +-
.../ui/panels/UserRequestFormPanel_ru.properties | 10 +-
.../enduser/panels/OIDCSSOLoginFormPanel.java | 4 +-
.../resources/EnduserCodeConsumerResource.java | 6 +-
.../enduser/panels/OIDCSSOLoginFormPanel.html | 9 +-
.../enduser/panels/SAMLSSOLoginFormPanel.java | 4 +-
.../EnduserAssertionConsumerResource.java | 6 +-
.../enduser/panels/SAMLSSOLoginFormPanel.html | 9 +-
.../syncope/fit/{ui => }/AbstractUITCase.java | 2 +-
.../syncope/fit/console/AbstractConsoleITCase.java | 2 +-
.../syncope/fit/console/AbstractTypesITCase.java | 30 +-
.../syncope/fit/console/AjaxBrowseITCase.java | 16 +-
.../syncope/fit/console/AnyObjectsITCase.java | 8 +-
.../syncope/fit/console/AnyTypeClassesITCase.java | 4 +-
.../apache/syncope/fit/console/AnyTypesITCase.java | 2 +-
.../apache/syncope/fit/console/BatchesITCase.java | 14 +-
.../fit/console/DisplayAttributesITCase.java | 2 +-
.../apache/syncope/fit/console/GroupsITCase.java | 8 +-
.../syncope/fit/console/LinkedAccountsITCase.java | 10 +-
.../org/apache/syncope/fit/console/LogsITCase.java | 2 +-
.../syncope/fit/console/NotificationsITCase.java | 8 +-
.../syncope/fit/console/ParametersITCase.java | 2 +-
.../apache/syncope/fit/console/PoliciesITCase.java | 6 +-
.../apache/syncope/fit/console/RealmsITCase.java | 4 +-
.../apache/syncope/fit/console/ReportsITCase.java | 6 +-
.../apache/syncope/fit/console/RolesITCase.java | 4 +-
.../fit/console/SecurityQuestionsITCase.java | 4 +-
.../apache/syncope/fit/console/TopologyITCase.java | 8 +-
.../apache/syncope/fit/console/UsersITCase.java | 22 +-
.../apache/syncope/fit/core/PlainSchemaITCase.java | 95 +-
.../syncope/fit/core/PropagationTaskITCase.java | 9 +-
.../syncope/fit/enduser/AbstractEnduserITCase.java | 48 +-
.../syncope/fit/enduser/AnonymousITCase.java | 154 +++
.../syncope/fit/enduser/AuthenticatedITCase.java | 122 ++
.../apache/syncope/fit/enduser/EnduserITCase.java | 305 -----
.../src/test/resources/enduser.properties | 9 +
.../src/main/resources/enduser.properties | 9 +
335 files changed, 8449 insertions(+), 3929 deletions(-)
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/BaseLogin.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/BaseLogin.java
index f03f20a..44c4e23 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/BaseLogin.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/BaseLogin.java
@@ -63,6 +63,10 @@ public abstract class BaseLogin extends WebPage {
protected static final Logger LOG = LoggerFactory.getLogger(BaseLogin.class);
+ public static final List<Locale> SUPPORTED_LOCALES = List.of(
+ Locale.ENGLISH, Locale.CANADA_FRENCH, Locale.ITALIAN, Locale.JAPANESE, new Locale("pt", "BR"),
+ new Locale("ru"));
+
@SpringBean
private DomainOps domainOps;
@@ -219,8 +223,6 @@ public abstract class BaseLogin extends WebPage {
protected abstract String getAnonymousUser();
- protected abstract List<Locale> getSupportedLocales();
-
protected abstract void authenticate(
String username,
String password,
@@ -245,7 +247,7 @@ public abstract class BaseLogin extends WebPage {
}
LocaleDropDown(final String id) {
- super(id, getSupportedLocales());
+ super(id, SUPPORTED_LOCALES);
setChoiceRenderer(new LocaleRenderer());
setModel(new IModel<Locale>() {
@@ -275,7 +277,7 @@ public abstract class BaseLogin extends WebPage {
getHeader(HttpHeaders.ACCEPT_LANGUAGE);
if (StringUtils.isNotBlank(acceptLanguage)) {
try {
- filtered = Locale.filter(Locale.LanguageRange.parse(acceptLanguage), getSupportedLocales());
+ filtered = Locale.filter(Locale.LanguageRange.parse(acceptLanguage), SUPPORTED_LOCALES);
} catch (Exception e) {
LOG.debug("Could not parse {} HTTP header value '{}'",
HttpHeaders.ACCEPT_LANGUAGE, acceptLanguage, e);
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/Constants.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/Constants.java
index ee8976a..2efdb0b 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/Constants.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/Constants.java
@@ -113,6 +113,8 @@ public final class Constants {
public static final int MAX_ROLE_LIST_SIZE = 30;
+ public static final String NOTIFICATION_TITLE_PARAM = "notificationTitle";
+
public static final String NOTIFICATION_MSG_PARAM = "notificationMessage";
public static final String NOTIFICATION_LEVEL_PARAM = "notificationLevel";
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AjaxPalettePanel.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AjaxPalettePanel.java
index 339bea7..c6c2250 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AjaxPalettePanel.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AjaxPalettePanel.java
@@ -24,6 +24,7 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -33,6 +34,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.client.ui.commons.Constants;
import org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
+import org.apache.syncope.common.lib.to.UserTO;
import org.apache.wicket.Component;
import org.apache.wicket.Session;
import org.apache.wicket.ajax.AjaxRequestTarget;
@@ -193,6 +195,7 @@ public class AjaxPalettePanel<T extends Serializable> extends AbstractFieldPanel
@Override
protected void onUpdate(final AjaxRequestTarget target) {
processInput();
+ Optional.ofNullable(builder.event).ifPresent(e -> e.apply(target));
}
});
@@ -267,6 +270,8 @@ public class AjaxPalettePanel<T extends Serializable> extends AbstractFieldPanel
protected Function<String, Stream<String>> idExtractor =
(Function<String, Stream<String>> & Serializable) input -> Stream.of(Strings.split(input, ','));
+ protected Function<AjaxRequestTarget, Boolean> event;
+
protected Function<Object, Map<String, String>> additionalAttributes;
public Builder<T> setName(final String name) {
@@ -320,6 +325,11 @@ public class AjaxPalettePanel<T extends Serializable> extends AbstractFieldPanel
return this;
}
+ public Builder<T> event(final Function<AjaxRequestTarget, Boolean> event) {
+ this.event = event;
+ return this;
+ }
+
public Builder<T> additionalAttributes(final Function<Object, Map<String, String>> additionalAttributes) {
this.additionalAttributes = additionalAttributes;
return this;
@@ -404,4 +414,24 @@ public class AjaxPalettePanel<T extends Serializable> extends AbstractFieldPanel
return filtered;
}
}
+
+ public static class UpdateActionEvent {
+
+ private final UserTO item;
+
+ private final AjaxRequestTarget target;
+
+ public UpdateActionEvent(final UserTO item, final AjaxRequestTarget target) {
+ this.item = item;
+ this.target = target;
+ }
+
+ public UserTO getItem() {
+ return item;
+ }
+
+ public AjaxRequestTarget getTarget() {
+ return target;
+ }
+ }
}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldDownload.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/BinaryFieldDownload.java
similarity index 98%
rename from client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldDownload.java
rename to client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/BinaryFieldDownload.java
index 65b0c03..d64f997 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldDownload.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/BinaryFieldDownload.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.client.console.wicket.markup.html.form;
+package org.apache.syncope.client.ui.commons.markup.html.form;
import java.time.Duration;
import org.apache.commons.lang3.StringUtils;
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/CardPanel.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/CardPanel.java
new file mode 100644
index 0000000..face722
--- /dev/null
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/CardPanel.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.ui.commons.panels;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.ResourceModel;
+
+public final class CardPanel<T extends Component> extends Panel {
+
+ private static final long serialVersionUID = -7906010415162945394L;
+
+ private CardPanel(final String id, final Builder<T> builder) {
+ super(id);
+ this.setOutputMarkupId(true);
+ this.setVisible(builder.visible);
+
+ this.add(new Label("cardLabel", new ResourceModel(builder.name, builder.name)).setOutputMarkupId(true));
+ this.add(builder.component);
+ }
+
+ public static class Builder<T extends Component> {
+
+ protected String name;
+
+ protected T component;
+
+ protected boolean visible;
+
+ public Builder<T> setName(final String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Builder<T> setComponent(final T component) {
+ this.component = component;
+ return this;
+ }
+
+ public Builder<T> isVisible(final boolean visible) {
+ this.visible = visible;
+ return this;
+ }
+
+ public CardPanel<T> build(final String id) {
+ return new CardPanel<>(id, this);
+ }
+ }
+}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/themes/AdminLTE.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/themes/AdminLTE.java
similarity index 96%
rename from client/idrepo/console/src/main/java/org/apache/syncope/client/console/themes/AdminLTE.java
rename to client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/themes/AdminLTE.java
index d270149..1dccc49 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/themes/AdminLTE.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/themes/AdminLTE.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.client.console.themes;
+package org.apache.syncope.client.ui.commons.themes;
import de.agilecoders.wicket.core.settings.Theme;
import java.util.ArrayList;
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/themes/AdminLTECssResourceReference.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/themes/AdminLTECssResourceReference.java
similarity index 97%
rename from client/idrepo/console/src/main/java/org/apache/syncope/client/console/themes/AdminLTECssResourceReference.java
rename to client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/themes/AdminLTECssResourceReference.java
index 397ef5e..f9e3617 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/themes/AdminLTECssResourceReference.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/themes/AdminLTECssResourceReference.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.client.console.themes;
+package org.apache.syncope.client.ui.commons.themes;
import de.agilecoders.wicket.core.Bootstrap;
import java.util.ArrayList;
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel.java
index 6cf3087..d34ef77 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel.java
@@ -38,14 +38,16 @@ public class PasswordPanel extends Panel {
public PasswordPanel(
final String id,
final UserWrapper wrapper,
+ final Boolean storePasswordInSyncope,
final boolean templateMode) {
- this(id, wrapper, templateMode, null);
+ this(id, wrapper, templateMode, storePasswordInSyncope, null);
}
public PasswordPanel(
final String id,
final UserWrapper wrapper,
final boolean templateMode,
+ final Boolean storePasswordInSyncope,
final PasswordStrengthBehavior passwordStrengthBehavior) {
super(id);
@@ -88,16 +90,16 @@ public class PasswordPanel extends Panel {
form.add(new EqualPasswordInputValidator(passwordField.getField(), confirmPasswordField.getField()));
}
- AjaxCheckBoxPanel storePasswordInSyncope = new AjaxCheckBoxPanel("storePasswordInSyncope",
+ AjaxCheckBoxPanel storePasswordInSyncopePanel = new AjaxCheckBoxPanel("storePasswordInSyncope",
"storePasswordInSyncope", new PropertyModel<>(wrapper, "storePasswordInSyncope"));
- storePasswordInSyncope.getField().setLabel(new ResourceModel("storePasswordInSyncope"));
- storePasswordInSyncope.setOutputMarkupId(true);
- storePasswordInSyncope.setOutputMarkupPlaceholderTag(true);
- if (wrapper.getInnerObject().getKey() == null) {
- storePasswordInSyncope.getField().setDefaultModelObject(Boolean.TRUE);
+ storePasswordInSyncopePanel.getField().setLabel(new ResourceModel("storePasswordInSyncope"));
+ storePasswordInSyncopePanel.setOutputMarkupId(true);
+ storePasswordInSyncopePanel.setOutputMarkupPlaceholderTag(true);
+ if (storePasswordInSyncope) {
+ storePasswordInSyncopePanel.getField().setDefaultModelObject(Boolean.TRUE);
} else {
- storePasswordInSyncope.setVisible(false);
+ storePasswordInSyncopePanel.setVisible(false);
}
- form.add(storePasswordInSyncope);
+ form.add(storePasswordInSyncopePanel);
}
}
diff --git a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/wizards/any/UserDetails$EditUserPasswordPanel.html b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/panels/CardPanel.html
similarity index 73%
copy from client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/wizards/any/UserDetails$EditUserPasswordPanel.html
copy to client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/panels/CardPanel.html
index e77ed5d..1ef8d64 100644
--- a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/wizards/any/UserDetails$EditUserPasswordPanel.html
+++ b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/panels/CardPanel.html
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" ?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -18,12 +19,13 @@ under the License.
-->
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
<wicket:panel>
- <div id="editUserChangePassword">
- <div class="alert alert-warning">
- <i class="fas fa-exclamation-triangle"></i> <label wicket:id="warning">[warning]</label>
+ <div class="box-header formcard">
+ <header class="card-container card-red">
+ <label class="card-header-style" wicket:id="cardLabel">[CARD LABEL]</label>
+ </header>
+ <div class="card-container-body">
+ <span wicket:id="contentPanel">[CONTENT PANEL]</span>
</div>
-
- <div wicket:id="passwordPanel">[change password panel]</div>
</div>
</wicket:panel>
</html>
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/themes/css/AdminLTE.css b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/themes/css/AdminLTE.css
similarity index 100%
rename from client/idrepo/console/src/main/resources/org/apache/syncope/client/console/themes/css/AdminLTE.css
rename to client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/themes/css/AdminLTE.css
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/themes/js/AdminLTE-app.min.js b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/themes/js/AdminLTE-app.min.js
similarity index 100%
rename from client/idrepo/console/src/main/resources/org/apache/syncope/client/console/themes/js/AdminLTE-app.min.js
rename to client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/themes/js/AdminLTE-app.min.js
diff --git a/client/idrepo/console/pom.xml b/client/idrepo/console/pom.xml
index db78158..dc4ca1a 100644
--- a/client/idrepo/console/pom.xml
+++ b/client/idrepo/console/pom.xml
@@ -123,7 +123,6 @@ under the License.
<artifactId>disruptor</artifactId>
</dependency>
- <!-- required by wicket tester -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeWebApplication.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeWebApplication.java
index 76ff69c..a9e48da 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeWebApplication.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeWebApplication.java
@@ -28,8 +28,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.lang3.BooleanUtils;
@@ -43,7 +41,6 @@ import org.apache.syncope.client.console.init.ClassPathScanImplementationLookup;
import org.apache.syncope.client.console.pages.BasePage;
import org.apache.syncope.client.console.pages.Dashboard;
import org.apache.syncope.client.console.pages.Login;
-import org.apache.syncope.client.console.themes.AdminLTE;
import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
import org.apache.syncope.common.lib.PropertyUtils;
import org.apache.wicket.Page;
@@ -69,6 +66,7 @@ import org.apache.syncope.client.console.commons.StatusProvider;
import org.apache.syncope.client.console.commons.VirSchemaDetailsPanelProvider;
import org.apache.syncope.client.console.pages.MustChangePassword;
import org.apache.syncope.client.console.panels.AnyPanel;
+import org.apache.syncope.client.ui.commons.themes.AdminLTE;
import org.apache.syncope.client.ui.commons.SyncopeUIRequestCycleListener;
import org.apache.syncope.client.ui.commons.Constants;
import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
@@ -88,10 +86,6 @@ public class SyncopeWebApplication extends WicketBootSecuredWebApplication {
private static final String CONSOLE_PROPERTIES = "console.properties";
- public static final List<Locale> SUPPORTED_LOCALES = List.of(
- Locale.ENGLISH, Locale.CANADA_FRENCH, Locale.ITALIAN, Locale.JAPANESE, new Locale("pt", "BR"),
- new Locale("ru"));
-
public static SyncopeWebApplication get() {
return (SyncopeWebApplication) WebApplication.get();
}
@@ -169,7 +163,7 @@ public class SyncopeWebApplication extends WicketBootSecuredWebApplication {
}
}
- protected void setSecurityHeaders(final Properties props, final WebResponse response) {
+ protected static void setSecurityHeaders(final Properties props, final WebResponse response) {
@SuppressWarnings("unchecked")
Enumeration<String> propNames = (Enumeration<String>) props.propertyNames();
while (propNames.hasMoreElements()) {
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java
index 11a1cad..a6ff75f 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java
@@ -22,6 +22,7 @@ import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.time.Duration;
import java.util.List;
+import java.util.stream.StreamSupport;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
import org.apache.syncope.client.console.SyncopeConsoleSession;
@@ -394,19 +395,16 @@ public class BasePage extends BaseWebPage {
}
// 4. when found, set CSS coordinates for menu
if (containingLI != null) {
- for (Component child : containingLI) {
- if (child instanceof Link) {
- child.add(new Behavior() {
+ StreamSupport.stream(containingLI.spliterator(), false).filter(Link.class::isInstance).
+ forEach(child -> child.add(new Behavior() {
- private static final long serialVersionUID = -5775607340182293596L;
+ private static final long serialVersionUID = -5775607340182293596L;
- @Override
- public void onComponentTag(final Component component, final ComponentTag tag) {
- tag.append("class", "active", " ");
- }
- });
+ @Override
+ public void onComponentTag(final Component component, final ComponentTag tag) {
+ tag.append("class", "active", " ");
}
- }
+ }));
if (keymasterULContainer.getId().equals(containingLI.getParent().getId())) {
keymasterULContainer.add(new Behavior() {
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Login.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Login.java
index 37944e8..a1244fe 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Login.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Login.java
@@ -21,7 +21,6 @@ package org.apache.syncope.client.console.pages;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.List;
-import java.util.Locale;
import org.apache.syncope.client.console.SyncopeWebApplication;
import org.apache.syncope.client.console.SyncopeConsoleSession;
import org.apache.syncope.client.ui.commons.BaseLogin;
@@ -71,11 +70,6 @@ public class Login extends BaseLogin {
}
@Override
- protected List<Locale> getSupportedLocales() {
- return SyncopeWebApplication.SUPPORTED_LOCALES;
- }
-
- @Override
protected void authenticate(final String username, final String password, final AjaxRequestTarget target)
throws AccessControlException {
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/SchemaTypeWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/SchemaTypeWizardBuilder.java
index b646d0a..77366e6 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/SchemaTypeWizardBuilder.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/SchemaTypeWizardBuilder.java
@@ -32,6 +32,7 @@ import org.apache.syncope.client.console.rest.SchemaRestClient;
import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
import org.apache.syncope.client.console.wizards.BaseAjaxWizardBuilder;
+import org.apache.syncope.client.ui.commons.BaseLogin;
import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
import org.apache.syncope.common.lib.to.DerSchemaTO;
@@ -163,7 +164,7 @@ public class SchemaTypeWizardBuilder extends BaseAjaxWizardBuilder<SchemaTO> {
}
});
locale.setRequired(true).hideLabel();
- locale.setChoices(SyncopeWebApplication.SUPPORTED_LOCALES.stream().
+ locale.setChoices(BaseLogin.SUPPORTED_LOCALES.stream().
map(Objects::toString).collect(Collectors.toList()));
locale.addValidator(validatable -> {
try {
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/status/ChangePasswordModal.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/status/ChangePasswordModal.java
index 5d56dba..0e865ec 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/status/ChangePasswordModal.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/status/ChangePasswordModal.java
@@ -58,7 +58,7 @@ public class ChangePasswordModal extends AbstractModalPanel<AnyWrapper<UserTO>>
this.wrapper = wrapper;
- final PasswordPanel passwordPanel = new PasswordPanel("passwordPanel", wrapper, false);
+ final PasswordPanel passwordPanel = new PasswordPanel("passwordPanel", wrapper, false, false);
passwordPanel.setOutputMarkupId(true);
add(passwordPanel);
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/bootstrap/dialog/BaseModal.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/bootstrap/dialog/BaseModal.java
index 861394e..2bdd5b4 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/bootstrap/dialog/BaseModal.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/bootstrap/dialog/BaseModal.java
@@ -102,8 +102,6 @@ public class BaseModal<T extends Serializable> extends Modal<T> {
this.windowClosedCallback = null;
components = new ArrayList<>();
- // Note: not adding this would imply adding WebjarsJavaScriptResourceReference about JQuery resizable and mouse
- // add(new Resizable().withChildSelector(".modal-content"));
// Note: not adding this would imply adding of WebjarsJavaScriptResourceReference about JQuery draggable
add(new Draggable(new DraggableConfig().withHandle(".modal-header").withCursor("move")));
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java
index bc48ae0..bc46754 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java
@@ -18,6 +18,8 @@
*/
package org.apache.syncope.client.console.wicket.markup.html.form;
+import org.apache.syncope.client.ui.commons.markup.html.form.BinaryFieldDownload;
+
import static de.agilecoders.wicket.jquery.JQuery.$;
import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.fileinput.BootstrapFileInputField;
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrsWizardStep.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrsWizardStep.java
index f4ae2c3..600fc1d 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrsWizardStep.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrsWizardStep.java
@@ -285,7 +285,7 @@ public abstract class AbstractAttrsWizardStep<S extends SchemaTO> extends Wizard
break;
case Binary:
- final PageReference pageRef = getPageReference();
+ PageReference pageRef = getPageReference();
panel = new BinaryFieldPanel(
"panel",
plainSchema.getLabel(SyncopeConsoleSession.get().getLocale()),
@@ -299,7 +299,6 @@ public abstract class AbstractAttrsWizardStep<S extends SchemaTO> extends Wizard
protected PageReference getPageReference() {
return pageRef;
}
-
};
if (required) {
panel.addRequiredLabel();
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserDetails.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserDetails.java
index d8c49e3..e067ef1 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserDetails.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserDetails.java
@@ -137,7 +137,7 @@ public class UserDetails extends Details<UserTO> {
super(id);
setOutputMarkupId(true);
add(new Label("warning", new ResourceModel("password.change.warning")));
- add(new PasswordPanel("passwordPanel", wrapper, templateMode));
+ add(new PasswordPanel("passwordPanel", wrapper, false, templateMode));
}
}
}
diff --git a/client/idrepo/enduser/pom.xml b/client/idrepo/enduser/pom.xml
index f7ed9fa..9bf1786 100644
--- a/client/idrepo/enduser/pom.xml
+++ b/client/idrepo/enduser/pom.xml
@@ -110,14 +110,16 @@ under the License.
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
</dependency>
+
<dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <scope>provided</scope>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
</dependency>
<dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>jcl-over-slf4j</artifactId>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter</artifactId>
+ <scope>test</scope>
</dependency>
</dependencies>
@@ -240,6 +242,7 @@ under the License.
<configuration>
<jvmArguments>
-Dwicket.core.settings.general.configuration-type=development
+ -XX:HotswapAgent=fatjar
-Xdebug -Xrunjdwp:transport=dt_socket,address=8004,server=y,suspend=n
</jvmArguments>
<profiles>
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserConstants.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/BookmarkablePageLinkBuilder.java
similarity index 50%
copy from client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserConstants.java
copy to client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/BookmarkablePageLinkBuilder.java
index 0a683a9..7f5c806 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserConstants.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/BookmarkablePageLinkBuilder.java
@@ -18,18 +18,28 @@
*/
package org.apache.syncope.client.enduser;
-public final class SyncopeEnduserConstants {
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
- public static final String CAPTCHA_SESSION_KEY = "captcha";
+public final class BookmarkablePageLinkBuilder {
- public static final String XSRF_COOKIE = "XSRF-TOKEN";
+ public static <T extends WebPage> BookmarkablePageLink<T> build(
+ final String key, final Class<T> defaultPageClass) {
- public static final String XSRF_HEADER_NAME = "X-XSRF-TOKEN";
+ return build(key, key, defaultPageClass);
+ }
- public static final String MEMBERSHIP_ATTR_SEPARATOR = "#";
+ public static <T extends WebPage> BookmarkablePageLink<T> build(
+ final String key, final String id, final Class<T> defaultPageClass) {
- private SyncopeEnduserConstants() {
- // private constructor for utility class
+ @SuppressWarnings("unchecked")
+ Class<T> pageClass = (Class<T>) SyncopeWebApplication.get().getPageClass(key);
+ return new BookmarkablePageLink<>(
+ id,
+ pageClass == null ? defaultPageClass : pageClass);
}
+ private BookmarkablePageLinkBuilder() {
+ // private constructor for static utility class
+ }
}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/PreferenceManager.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/PreferenceManager.java
new file mode 100644
index 0000000..845e7a6
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/PreferenceManager.java
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.core.type.TypeReference;
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.math.NumberUtils;
+import org.apache.wicket.util.cookies.CookieDefaults;
+import org.apache.wicket.util.cookies.CookieUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PreferenceManager implements Serializable {
+
+ private static final long serialVersionUID = 3581434664555284193L;
+
+ private static final Logger LOG = LoggerFactory.getLogger(PreferenceManager.class);
+
+ private static final String COOKIE_NAME = "syncope2EnduserPrefs";
+
+ private static final int ONE_YEAR_TIME = 60 * 60 * 24 * 365;
+
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ private static final TypeReference<Map<String, String>> MAP_TYPE_REF = new TypeReference<Map<String, String>>() {
+ };
+
+ private static final List<Integer> PAGINATOR_CHOICES = Arrays.asList(new Integer[] { 10, 25, 50 });
+
+ private static final CookieUtils COOKIE_UTILS;
+
+ static {
+ CookieDefaults cookieDefaults = new CookieDefaults();
+ cookieDefaults.setMaxAge(ONE_YEAR_TIME);
+ COOKIE_UTILS = new CookieUtils(cookieDefaults);
+ }
+
+ public List<Integer> getPaginatorChoices() {
+ return PAGINATOR_CHOICES;
+ }
+
+ private Map<String, String> getPrefs(final String value) {
+ Map<String, String> prefs;
+ try {
+ if (StringUtils.isNotBlank(value)) {
+ prefs = MAPPER.readValue(value, MAP_TYPE_REF);
+ } else {
+ throw new Exception("Invalid cookie value '" + value + "'");
+ }
+ } catch (Exception e) {
+ LOG.debug("No preferences found", e);
+ prefs = new HashMap<>();
+ }
+
+ return prefs;
+ }
+
+ private String setPrefs(final Map<String, String> prefs) throws IOException {
+ StringWriter writer = new StringWriter();
+ MAPPER.writeValue(writer, prefs);
+
+ return writer.toString();
+ }
+
+ public String get(final String key) {
+ String result = null;
+
+ String prefString = COOKIE_UTILS.load(COOKIE_NAME);
+ if (prefString != null) {
+ Map<String, String> prefs = getPrefs(new String(Base64.getDecoder().decode(prefString.getBytes())));
+ result = prefs.get(key);
+ }
+
+ return result;
+ }
+
+ public Integer getPaginatorRows(final String key) {
+ Integer result = getPaginatorChoices().get(0);
+
+ String value = get(key);
+ if (value != null) {
+ result = NumberUtils.toInt(value, 10);
+ }
+
+ return result;
+ }
+
+ public List<String> getList(final String key) {
+ List<String> result = new ArrayList<>();
+
+ String compound = get(key);
+
+ if (StringUtils.isNotBlank(compound)) {
+ String[] items = compound.split(";");
+ result.addAll(Arrays.asList(items));
+ }
+
+ return result;
+ }
+
+ public void set(final Map<String, List<String>> prefs) {
+ Map<String, String> current = new HashMap<>();
+
+ String prefString = COOKIE_UTILS.load(COOKIE_NAME);
+ if (prefString != null) {
+ current.putAll(getPrefs(new String(Base64.getDecoder().decode(prefString.getBytes()))));
+ }
+
+ // after retrieved previous setting in order to overwrite the key ...
+ prefs.forEach((key, values) -> {
+ current.put(key, StringUtils.join(values, ";"));
+ });
+
+ try {
+ COOKIE_UTILS.save(COOKIE_NAME, Base64.getEncoder().encodeToString(setPrefs(current).getBytes()));
+ } catch (IOException e) {
+ LOG.error("Could not save {} info: {}", getClass().getSimpleName(), current, e);
+ }
+ }
+
+ public void set(final String key, final String value) {
+ String prefString = COOKIE_UTILS.load(COOKIE_NAME);
+
+ final Map<String, String> current = new HashMap<>();
+ if (prefString != null) {
+ current.putAll(getPrefs(new String(Base64.getDecoder().decode(prefString.getBytes()))));
+ }
+
+ // after retrieved previous setting in order to overwrite the key ...
+ current.put(key, value);
+
+ try {
+ COOKIE_UTILS.save(COOKIE_NAME, Base64.getEncoder().encodeToString(setPrefs(current).getBytes()));
+ } catch (IOException e) {
+ LOG.error("Could not save {} info: {}", getClass().getSimpleName(), current, e);
+ }
+ }
+
+ public void setList(final String key, final List<String> values) {
+ set(key, StringUtils.join(values, ";"));
+ }
+
+ public void setList(final Map<String, List<String>> prefs) {
+ set(prefs);
+ }
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserRequestCycleListener.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserRequestCycleListener.java
new file mode 100644
index 0000000..a0e2192
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserRequestCycleListener.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser;
+
+import java.security.AccessControlException;
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.ForbiddenException;
+import javax.xml.ws.WebServiceException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.enduser.pages.Login;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.wicket.authorization.UnauthorizedInstantiationException;
+import org.apache.wicket.core.request.handler.PageProvider;
+import org.apache.wicket.core.request.handler.RenderPageRequestHandler;
+import org.apache.wicket.markup.html.pages.ExceptionErrorPage;
+import org.apache.wicket.protocol.http.PageExpiredException;
+import org.apache.wicket.request.IRequestHandler;
+import org.apache.wicket.request.component.IRequestablePage;
+import org.apache.wicket.request.cycle.IRequestCycleListener;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SyncopeEnduserRequestCycleListener implements IRequestCycleListener {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SyncopeEnduserRequestCycleListener.class);
+
+ private Throwable instanceOf(final Exception e, final Class<? extends Exception> clazz) {
+ return clazz.isAssignableFrom(e.getClass())
+ ? e
+ : e.getCause() != null && clazz.isAssignableFrom(e.getCause().getClass())
+ ? e.getCause()
+ : e.getCause() != null && e.getCause().getCause() != null
+ && clazz.isAssignableFrom(e.getCause().getCause().getClass())
+ ? e.getCause().getCause()
+ : null;
+ }
+
+ @Override
+ public IRequestHandler onException(final RequestCycle cycle, final Exception e) {
+ LOG.error("Exception found", e);
+
+ PageParameters errorParameters = new PageParameters();
+
+ IRequestablePage errorPage;
+ if (instanceOf(e, UnauthorizedInstantiationException.class) != null) {
+ errorParameters.add("errorMessage", SyncopeEnduserSession.Error.AUTHORIZATION.fallback());
+ errorPage = new Login(errorParameters);
+ } else if (instanceOf(e, AccessControlException.class) != null) {
+ if (StringUtils.containsIgnoreCase(instanceOf(e, AccessControlException.class).getMessage(), "expired")) {
+ errorParameters.add("errorMessage", SyncopeEnduserSession.Error.SESSION_EXPIRED.fallback());
+ } else {
+ errorParameters.add("errorMessage", SyncopeEnduserSession.Error.AUTHORIZATION.fallback());
+ }
+ errorPage = new Login(errorParameters);
+ } else if (instanceOf(e, PageExpiredException.class) != null || !SyncopeEnduserSession.get().isSignedIn()) {
+ errorParameters.add("errorMessage", SyncopeEnduserSession.Error.SESSION_EXPIRED.fallback());
+ errorPage = new Login(errorParameters);
+ } else if (instanceOf(e, BadRequestException.class) != null
+ || instanceOf(e, WebServiceException.class) != null
+ || instanceOf(e, SyncopeClientException.class) != null) {
+
+ errorParameters.add("errorMessage", SyncopeEnduserSession.Error.REST.fallback());
+ errorPage = new Login(errorParameters);
+ } else {
+ Throwable cause = instanceOf(e, ForbiddenException.class);
+ if (cause == null) {
+ // redirect to default Wicket error page
+ errorPage = new ExceptionErrorPage(e, null);
+ } else {
+ errorParameters.add("errorMessage", cause.getMessage());
+ errorPage = new Login(errorParameters);
+ }
+ }
+
+ if (errorPage instanceof Login) {
+ try {
+ SyncopeEnduserSession.get().invalidate();
+ } catch (Throwable t) {
+ // ignore
+ LOG.debug("Unexpected error while forcing logout after error", t);
+ }
+ }
+
+ return new RenderPageRequestHandler(new PageProvider(errorPage));
+ }
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserSession.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserSession.java
index 7ea4470..81f8a25 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserSession.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserSession.java
@@ -18,21 +18,6 @@
*/
package org.apache.syncope.client.enduser;
-import java.security.AccessControlException;
-import java.text.DateFormat;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Future;
-import java.util.stream.Collectors;
-import javax.ws.rs.BadRequestException;
-import javax.ws.rs.ForbiddenException;
-import javax.ws.rs.core.EntityTag;
-import javax.ws.rs.core.MediaType;
-import javax.xml.ws.WebServiceException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.time.FastDateFormat;
@@ -43,24 +28,63 @@ import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
import org.apache.syncope.client.ui.commons.BaseSession;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.info.PlatformInfo;
+import org.apache.syncope.common.lib.info.SystemInfo;
import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.common.lib.types.IdRepoEntitlement;
import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.SyncopeService;
import org.apache.wicket.Session;
-import org.apache.wicket.protocol.http.WebSession;
+import org.apache.wicket.authroles.authentication.AuthenticatedWebSession;
+import org.apache.wicket.authroles.authorization.strategies.role.Roles;
import org.apache.wicket.request.Request;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.task.TaskRejectedException;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.ForbiddenException;
+import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.MediaType;
+import javax.xml.ws.WebServiceException;
+import java.security.AccessControlException;
+import java.text.DateFormat;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+import java.util.stream.Collectors;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
-/**
- * Custom Syncope Enduser Session class.
- */
-public class SyncopeEnduserSession extends WebSession implements BaseSession {
+public class SyncopeEnduserSession extends AuthenticatedWebSession implements BaseSession {
+
+ private static final long serialVersionUID = 747562246415852166L;
+
+ public enum Error {
+ INVALID_SECURITY_ANSWER("invalid.security.answer", "Invalid Security Answer"),
+ SESSION_EXPIRED("error.session.expired", "Session expired: please login again"),
+ AUTHORIZATION("error.authorization", "Insufficient access rights when performing the requested operation"),
+ REST("error.rest", "There was an error while contacting the Core server");
+
+ private final String key;
- private static final long serialVersionUID = 1284946129513378647L;
+ private final String fallback;
+
+ Error(final String key, final String fallback) {
+ this.key = key;
+ this.fallback = fallback;
+ }
+
+ public String key() {
+ return key;
+ }
+
+ public String fallback() {
+ return fallback;
+ }
+ }
private static final Logger LOG = LoggerFactory.getLogger(SyncopeEnduserSession.class);
@@ -68,9 +92,9 @@ public class SyncopeEnduserSession extends WebSession implements BaseSession {
private final SyncopeClient anonymousClient;
- private SyncopeClient client;
+ private final PlatformInfo platformInfo;
- private UserTO selfTO;
+ private final SystemInfo systemInfo;
private final Map<Class<?>, Object> services = Collections.synchronizedMap(new HashMap<>());
@@ -78,6 +102,10 @@ public class SyncopeEnduserSession extends WebSession implements BaseSession {
private String domain;
+ private SyncopeClient client;
+
+ private UserTO selfTO;
+
public static SyncopeEnduserSession get() {
return (SyncopeEnduserSession) Session.get();
}
@@ -86,22 +114,39 @@ public class SyncopeEnduserSession extends WebSession implements BaseSession {
super(request);
clientFactory = SyncopeWebApplication.get().newClientFactory();
- anonymousClient = clientFactory.create(new AnonymousAuthenticationHandler(
- SyncopeWebApplication.get().getAnonymousUser(),
- SyncopeWebApplication.get().getAnonymousKey()));
+ anonymousClient = clientFactory.
+ create(new AnonymousAuthenticationHandler(
+ SyncopeWebApplication.get().getAnonymousUser(),
+ SyncopeWebApplication.get().getAnonymousKey()));
+
+ platformInfo = getAnonymousService(SyncopeService.class).platform();
+ systemInfo = getAnonymousService(SyncopeService.class).system();
executor = new ThreadPoolTaskExecutor();
executor.setWaitForTasksToCompleteOnShutdown(false);
- executor.setCorePoolSize(SyncopeWebApplication.get().getCorePoolSize());
- executor.setMaxPoolSize(SyncopeWebApplication.get().getMaxPoolSize());
- executor.setQueueCapacity(SyncopeWebApplication.get().getQueueCapacity());
executor.initialize();
}
protected String message(final SyncopeClientException sce) {
- return sce.getType().name() + ": " + sce.getElements().stream().collect(Collectors.joining(", "));
+ Error error = null;
+ if (sce.getType() == ClientExceptionType.InvalidSecurityAnswer) {
+ error = Error.INVALID_SECURITY_ANSWER;
+ }
+ if (error == null) {
+ return sce.getType().name() + ": " + sce.getElements().stream().collect(Collectors.joining(", "));
+ }
+ return getApplication().getResourceSettings().getLocalizer().
+ getString(error.key(), null, null, null, null, error.fallback());
}
+ /**
+ * Extract and localize (if translation available) the actual message from the given exception; then, report it
+ * via {@link Session#error(java.io.Serializable)}.
+ *
+ * @see org.apache.syncope.client.lib.RestClientExceptionMapper
+ *
+ * @param e raised exception
+ */
@Override
public void onException(final Exception e) {
Throwable root = ExceptionUtils.getRootCause(e);
@@ -111,8 +156,6 @@ public class SyncopeEnduserSession extends WebSession implements BaseSession {
SyncopeClientException sce = (SyncopeClientException) root;
message = sce.isComposite()
? sce.asComposite().getExceptions().stream().map(this::message).collect(Collectors.joining("; "))
- : sce.getType() == ClientExceptionType.InvalidSecurityAnswer
- ? getApplication().getResourceSettings().getLocalizer().getString("invalid.security.answer", null)
: message(sce);
} else if (root instanceof AccessControlException || root instanceof ForbiddenException) {
Error error = StringUtils.containsIgnoreCase(message, "expired")
@@ -130,15 +173,31 @@ public class SyncopeEnduserSession extends WebSession implements BaseSession {
error(message);
}
- public void cleanup() {
- client = null;
- selfTO = null;
- services.clear();
+ public MediaType getMediaType() {
+ return clientFactory.getContentType().getMediaType();
+ }
+
+ public SyncopeClient getAnonymousClient() {
+ return anonymousClient;
+ }
+
+ public void execute(final Runnable command) {
+ try {
+ executor.execute(command);
+ } catch (TaskRejectedException e) {
+ LOG.error("Could not execute {}", command, e);
+ }
}
@Override
- public String getJWT() {
- return Optional.ofNullable(client).map(SyncopeClient::getJWT).orElse(null);
+ public <T> Future<T> execute(final Callable<T> command) {
+ try {
+ return executor.submit(command);
+ } catch (TaskRejectedException e) {
+ LOG.error("Could not execute {}", command, e);
+
+ return new CompletableFuture<>();
+ }
}
@Override
@@ -151,24 +210,30 @@ public class SyncopeEnduserSession extends WebSession implements BaseSession {
return StringUtils.isBlank(domain) ? SyncopeConstants.MASTER_DOMAIN : domain;
}
- private void afterAuthentication(final String username) {
- try {
- selfTO = client.self().getRight();
- } catch (ForbiddenException e) {
- LOG.warn("Could not read self(), probably in a {} scenario", IdRepoEntitlement.MUST_CHANGE_PASSWORD, e);
+ @Override
+ public String getJWT() {
+ return client == null ? null : client.getJWT();
+ }
- selfTO = new UserTO();
- selfTO.setUsername(username);
- selfTO.setMustChangePassword(true);
- }
+ @Override
+ public Roles getRoles() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
- // bind explicitly this session to have a stateful behavior during http requests, unless session will
- // expire at each request
- this.bind();
+ public PlatformInfo getPlatformInfo() {
+ return platformInfo;
}
+ public SystemInfo getSystemInfo() {
+ return systemInfo;
+ }
+
+ @Override
public boolean authenticate(final String username, final String password) {
boolean authenticated = false;
+ if (SyncopeWebApplication.get().getAdminUser().equalsIgnoreCase(username)) {
+ return authenticated;
+ }
try {
client = clientFactory.setDomain(getDomain()).create(username, password);
@@ -199,6 +264,36 @@ public class SyncopeEnduserSession extends WebSession implements BaseSession {
return authenticated;
}
+ private void afterAuthentication(final String username) {
+ try {
+ selfTO = client.self().getRight();
+ } catch (ForbiddenException e) {
+ LOG.warn("Could not read self(), probably in a {} scenario", IdRepoEntitlement.MUST_CHANGE_PASSWORD, e);
+
+ selfTO = new UserTO();
+ selfTO.setUsername(username);
+ selfTO.setMustChangePassword(true);
+ }
+
+ // bind explicitly this session to have a stateful behavior during http requests, unless session will
+ // expire at each request
+ this.bind();
+ }
+
+ protected boolean isAuthenticated() {
+ return client != null && client.getJWT() != null;
+ }
+
+ protected boolean isMustChangePassword() {
+ return selfTO != null && selfTO.isMustChangePassword();
+ }
+
+ public void cleanup() {
+ client = null;
+ selfTO = null;
+ services.clear();
+ }
+
@Override
public void invalidate() {
if (isAuthenticated()) {
@@ -214,49 +309,15 @@ public class SyncopeEnduserSession extends WebSession implements BaseSession {
super.invalidate();
}
- @Override
- public <T> T getAnonymousService(final Class<T> serviceClass) {
- return getService(serviceClass);
- }
-
- @Override
- public <T> T getService(final Class<T> serviceClass) {
- T service = (client == null || !isAuthenticated())
- ? anonymousClient.getService(serviceClass)
- : client.getService(serviceClass);
- WebClient.client(service).header(RESTHeaders.DOMAIN, getDomain());
- return service;
- }
-
- @Override
- public <T> T getService(final String etag, final Class<T> serviceClass) {
- T serviceInstance = getService(serviceClass);
- WebClient.client(serviceInstance).match(new EntityTag(etag), false).
- type(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON);
-
- return serviceInstance;
- }
-
public UserTO getSelfTO() {
- if (selfTO == null) {
- throw new IllegalArgumentException("User not authenticated");
- }
- return selfTO;
+ return getSelfTO(false);
}
- @Override
- public <T> Future<T> execute(final Callable<T> command) {
- try {
- return executor.submit(command);
- } catch (TaskRejectedException e) {
- LOG.error("Could not execute {}", command, e);
-
- return new CompletableFuture<>();
+ public UserTO getSelfTO(final boolean reload) {
+ if (reload) {
+ afterAuthentication(selfTO.getUsername());
}
- }
-
- public boolean isAuthenticated() {
- return client != null && client.getJWT() != null;
+ return selfTO;
}
@SuppressWarnings("unchecked")
@@ -275,6 +336,24 @@ public class SyncopeEnduserSession extends WebSession implements BaseSession {
}
@Override
+ public <T> T getService(final Class<T> serviceClass) {
+ T service = (client == null || !isAuthenticated())
+ ? anonymousClient.getService(serviceClass)
+ : client.getService(serviceClass);
+ WebClient.client(service).header(RESTHeaders.DOMAIN, getDomain());
+ return service;
+ }
+
+ @Override
+ public <T> T getService(final String etag, final Class<T> serviceClass) {
+ T serviceInstance = getService(serviceClass);
+ WebClient.client(serviceInstance).match(new EntityTag(etag), false).
+ type(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON);
+
+ return serviceInstance;
+ }
+
+ @Override
public <T> void resetClient(final Class<T> service) {
T serviceInstance = getCachedService(service);
WebClient.client(serviceInstance).reset();
@@ -284,4 +363,9 @@ public class SyncopeEnduserSession extends WebSession implements BaseSession {
public FastDateFormat getDateFormat() {
return FastDateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, getLocale());
}
+
+ @Override
+ public <T> T getAnonymousService(final Class<T> serviceClass) {
+ return getAnonymousClient().getService(serviceClass);
+ }
}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeWebApplication.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeWebApplication.java
index af4fa3a..37ca700 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeWebApplication.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeWebApplication.java
@@ -25,9 +25,11 @@ import com.google.common.net.HttpHeaders;
import de.agilecoders.wicket.core.Bootstrap;
import de.agilecoders.wicket.core.settings.BootstrapSettings;
import de.agilecoders.wicket.core.settings.IBootstrapSettings;
+import de.agilecoders.wicket.core.settings.SingleThemeProvider;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
@@ -40,26 +42,26 @@ import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.client.enduser.init.ClassPathScanImplementationLookup;
-import org.apache.syncope.client.enduser.assets.SyncopeEnduserCss;
-import org.apache.syncope.client.enduser.model.CustomAttributesInfo;
+import org.apache.syncope.client.enduser.layout.UserFormLayoutInfo;
+import org.apache.syncope.client.enduser.pages.BasePage;
+import org.apache.syncope.client.enduser.pages.Dashboard;
import org.apache.syncope.client.enduser.pages.Login;
import org.apache.syncope.client.enduser.pages.MustChangePassword;
-import org.apache.syncope.client.enduser.pages.Self;
import org.apache.syncope.client.enduser.pages.SelfConfirmPasswordReset;
+import org.apache.syncope.client.enduser.panels.Sidebar;
import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
import org.apache.syncope.client.ui.commons.SyncopeUIRequestCycleListener;
import org.apache.syncope.client.ui.commons.annotations.Resource;
+import org.apache.syncope.client.ui.commons.themes.AdminLTE;
import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
import org.apache.syncope.common.keymaster.client.api.ServiceOps;
import org.apache.syncope.common.lib.PropertyUtils;
import org.apache.wicket.Page;
import org.apache.wicket.Session;
import org.apache.wicket.WicketRuntimeException;
-import org.apache.wicket.markup.head.CssHeaderItem;
-import org.apache.wicket.markup.head.IHeaderResponse;
-import org.apache.wicket.markup.html.IHeaderContributor;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.protocol.http.ResourceIsolationRequestCycleListener;
import org.apache.wicket.protocol.http.WebApplication;
@@ -74,7 +76,6 @@ import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.request.resource.AbstractResource;
import org.apache.wicket.request.resource.IResource;
import org.apache.wicket.request.resource.ResourceReference;
-import org.apache.wicket.resource.JQueryResourceReference;
import org.apache.wicket.util.lang.Args;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -88,7 +89,7 @@ public class SyncopeWebApplication extends WicketBootStandardWebApplication {
private static final String ENDUSER_PROPERTIES = "enduser.properties";
- private static final String CUSTOM_FORM_ATTRIBUTES_FILE = "customFormAttributes.json";
+ private static final String CUSTOM_FORM_LAYOUT_FILE = "customFormLayout.json";
public static final List<Locale> SUPPORTED_LOCALES = List.of(
Locale.ENGLISH, Locale.ITALIAN, new Locale("pt", "BR"), new Locale("ru"), Locale.JAPANESE);
@@ -125,11 +126,36 @@ public class SyncopeWebApplication extends WicketBootStandardWebApplication {
private Integer maxUploadFileSizeMB;
- private FileAlterationMonitor customFormAttributesMonitor;
+ private FileAlterationMonitor customFormLayoutMonitor;
- private Map<String, CustomAttributesInfo> customFormAttributes;
+ private Map<String, Class<? extends BasePage>> pageClasses;
- protected void setSecurityHeaders(final Properties props, final WebResponse response) {
+ private Class<? extends Sidebar> sidebar;
+
+ private UserFormLayoutInfo customFormLayout;
+
+ @SuppressWarnings("unchecked")
+ protected void populatePageClasses(final Properties props) {
+ Enumeration<String> propNames = (Enumeration<String>) props.propertyNames();
+ while (propNames.hasMoreElements()) {
+ String className = propNames.nextElement();
+ if (className.startsWith("page.")) {
+ try {
+ Class<?> clazz = ClassUtils.getClass(props.getProperty(className));
+ if (BasePage.class.isAssignableFrom(clazz)) {
+ pageClasses.put(
+ StringUtils.substringAfter(className, "page."), (Class<? extends BasePage>) clazz);
+ } else {
+ LOG.warn("{} does not extend {}, ignoring...", clazz.getName(), BasePage.class.getName());
+ }
+ } catch (ClassNotFoundException e) {
+ LOG.error("While looking for class identified by property '{}'", className, e);
+ }
+ }
+ }
+ }
+
+ protected static void setSecurityHeaders(final Properties props, final WebResponse response) {
@SuppressWarnings("unchecked")
Enumeration<String> propNames = (Enumeration<String>) props.propertyNames();
while (propNames.hasMoreElements()) {
@@ -199,33 +225,31 @@ public class SyncopeWebApplication extends WicketBootStandardWebApplication {
maxPoolSize = 50;
}
- // read customFormAttributes.json
- File enduserDir;
- try (InputStream is = SyncopeWebApplication.class.getResourceAsStream('/' + CUSTOM_FORM_ATTRIBUTES_FILE)) {
- customFormAttributes = MAPPER.readValue(is,
- new TypeReference<HashMap<String, CustomAttributesInfo>>() {
+ // read customFormLayout.json
+ try (InputStream is = SyncopeWebApplication.class.getResourceAsStream('/' + CUSTOM_FORM_LAYOUT_FILE)) {
+ customFormLayout = MAPPER.readValue(is, new TypeReference<UserFormLayoutInfo>() {
});
- enduserDir = new File(props.getProperty("enduser.directory"));
+ File enduserDir = new File(props.getProperty("enduser.directory"));
boolean existsEnduserDir = enduserDir.exists() && enduserDir.canRead() && enduserDir.isDirectory();
if (existsEnduserDir) {
- File customFormAttributesFile = FileUtils.getFile(enduserDir, CUSTOM_FORM_ATTRIBUTES_FILE);
- if (customFormAttributesFile.exists()
- && customFormAttributesFile.canRead()
- && customFormAttributesFile.isFile()) {
- customFormAttributes = MAPPER.readValue(FileUtils.openInputStream(customFormAttributesFile),
- new TypeReference<HashMap<String, CustomAttributesInfo>>() {
+ File customFormLayoutFile = FileUtils.getFile(enduserDir, CUSTOM_FORM_LAYOUT_FILE);
+ if (customFormLayoutFile.exists()
+ && customFormLayoutFile.canRead()
+ && customFormLayoutFile.isFile()) {
+ customFormLayout = MAPPER.readValue(FileUtils.openInputStream(customFormLayoutFile),
+ new TypeReference<UserFormLayoutInfo>() {
});
}
}
FileAlterationObserver observer = existsEnduserDir
? new FileAlterationObserver(
enduserDir,
- pathname -> StringUtils.contains(pathname.getPath(), CUSTOM_FORM_ATTRIBUTES_FILE))
+ pathname -> StringUtils.contains(pathname.getPath(), CUSTOM_FORM_LAYOUT_FILE))
: new FileAlterationObserver(
- SyncopeWebApplication.class.getResource('/' + CUSTOM_FORM_ATTRIBUTES_FILE).getFile(),
- pathname -> StringUtils.contains(pathname.getPath(), CUSTOM_FORM_ATTRIBUTES_FILE));
+ SyncopeWebApplication.class.getResource('/' + CUSTOM_FORM_LAYOUT_FILE).getFile(),
+ pathname -> StringUtils.contains(pathname.getPath(), CUSTOM_FORM_LAYOUT_FILE));
- customFormAttributesMonitor = new FileAlterationMonitor(5000);
+ customFormLayoutMonitor = new FileAlterationMonitor(5000);
FileAlterationListener listener = new FileAlterationListenerAdaptor() {
@@ -233,13 +257,13 @@ public class SyncopeWebApplication extends WicketBootStandardWebApplication {
public void onFileChange(final File file) {
try {
LOG.trace("{} has changed. Reloading form attributes customization configuration.",
- CUSTOM_FORM_ATTRIBUTES_FILE);
- customFormAttributes = MAPPER.readValue(FileUtils.openInputStream(file),
- new TypeReference<HashMap<String, CustomAttributesInfo>>() {
+ CUSTOM_FORM_LAYOUT_FILE);
+ customFormLayout = MAPPER.readValue(FileUtils.openInputStream(file),
+ new TypeReference<UserFormLayoutInfo>() {
});
} catch (IOException e) {
LOG.error("{} While reading app customization configuration.",
- CUSTOM_FORM_ATTRIBUTES_FILE, e);
+ CUSTOM_FORM_LAYOUT_FILE, e);
}
}
@@ -247,34 +271,44 @@ public class SyncopeWebApplication extends WicketBootStandardWebApplication {
public void onFileCreate(final File file) {
try {
LOG.trace("{} has been created. Loading form attributes customization configuration.",
- CUSTOM_FORM_ATTRIBUTES_FILE);
- customFormAttributes = MAPPER.readValue(FileUtils.openInputStream(file),
- new TypeReference<HashMap<String, CustomAttributesInfo>>() {
+ CUSTOM_FORM_LAYOUT_FILE);
+ customFormLayout = MAPPER.readValue(FileUtils.openInputStream(file),
+ new TypeReference<UserFormLayoutInfo>() {
});
} catch (IOException e) {
LOG.error("{} While reading app customization configuration.",
- CUSTOM_FORM_ATTRIBUTES_FILE, e);
+ CUSTOM_FORM_LAYOUT_FILE, e);
}
}
@Override
public void onFileDelete(final File file) {
LOG.trace("{} has been deleted. Resetting form attributes customization configuration.",
- CUSTOM_FORM_ATTRIBUTES_FILE);
- customFormAttributes = null;
+ CUSTOM_FORM_LAYOUT_FILE);
+ customFormLayout = null;
}
};
observer.addListener(listener);
- customFormAttributesMonitor.addObserver(observer);
- customFormAttributesMonitor.start();
+ customFormLayoutMonitor.addObserver(observer);
+ customFormLayoutMonitor.start();
} catch (Exception e) {
- throw new WicketRuntimeException("Could not read " + CUSTOM_FORM_ATTRIBUTES_FILE, e);
+ throw new WicketRuntimeException("Could not read " + CUSTOM_FORM_LAYOUT_FILE, e);
}
+ // process page properties
+ pageClasses = new HashMap<>();
+ populatePageClasses(props);
+ pageClasses = Collections.unmodifiableMap(pageClasses);
+
+ buildSidebarClass(props);
+
// Application settings
IBootstrapSettings settings = new BootstrapSettings();
+ // set theme provider
+ settings.setThemeProvider(new SingleThemeProvider(new AdminLTE()));
+
// install application settings
Bootstrap.install(this, settings);
@@ -282,25 +316,8 @@ public class SyncopeWebApplication extends WicketBootStandardWebApplication {
getResourceSettings().setUseDefaultOnMissingResource(true);
getResourceSettings().setThrowExceptionOnMissingResource(false);
- getJavaScriptLibrarySettings().setJQueryReference(JQueryResourceReference.getV2());
-
- getResourceSettings().setThrowExceptionOnMissingResource(true);
-
getMarkupSettings().setStripWicketTags(true);
getMarkupSettings().setCompressWhitespace(true);
- getMarkupSettings().setStripComments(true);
-
- // add some css assets as Java Wicket resource in order to set Bootstrap css as a dependency of those
- // and stop it to override the custom css rules
- getHeaderContributorListeners().add(new IHeaderContributor() {
-
- private static final long serialVersionUID = -8955205747168484695L;
-
- @Override
- public void renderHead(final IHeaderResponse response) {
- response.render(CssHeaderItem.forReference(SyncopeEnduserCss.INSTANCE));
- }
- });
getRequestCycleListeners().add(new SyncopeUIRequestCycleListener() {
@@ -318,7 +335,6 @@ public class SyncopeWebApplication extends WicketBootStandardWebApplication {
protected IRequestablePage getErrorPage(final PageParameters errorParameters) {
return new Login(errorParameters);
}
-
});
if (BooleanUtils.toBoolean(props.getProperty("x-forward"))) {
@@ -388,11 +404,11 @@ public class SyncopeWebApplication extends WicketBootStandardWebApplication {
@Override
protected void onDestroy() {
- if (customFormAttributesMonitor != null) {
+ if (customFormLayoutMonitor != null) {
try {
- customFormAttributesMonitor.stop(0);
+ customFormLayoutMonitor.stop(0);
} catch (Exception e) {
- LOG.error("{} While stopping file monitor", CUSTOM_FORM_ATTRIBUTES_FILE, e);
+ LOG.error("{} While stopping file monitor", CUSTOM_FORM_LAYOUT_FILE, e);
}
}
}
@@ -400,11 +416,37 @@ public class SyncopeWebApplication extends WicketBootStandardWebApplication {
@Override
public Class<? extends Page> getHomePage() {
return SyncopeEnduserSession.get().isAuthenticated()
- && SyncopeEnduserSession.get().getSelfTO().isMustChangePassword()
+ && SyncopeEnduserSession.get().isMustChangePassword()
? MustChangePassword.class
: SyncopeEnduserSession.get().isAuthenticated()
- ? Self.class
- : Login.class;
+ ? getPageClass("profile", Dashboard.class)
+ : getSignInPageClass();
+ }
+
+ public ClassPathScanImplementationLookup getLookup() {
+ return lookup;
+ }
+
+ @SuppressWarnings("unchecked")
+ private void buildSidebarClass(final Properties props) {
+ try {
+ Class<?> clazz = ClassUtils.getClass(props.getProperty("sidebar", Sidebar.class.getCanonicalName()));
+ if (Sidebar.class.isAssignableFrom(clazz)) {
+ sidebar = (Class<? extends Sidebar>) clazz;
+ } else {
+ LOG.warn("{} does not extend {}, ignoring...", clazz.getName(), Sidebar.class.getName());
+ }
+ } catch (ClassNotFoundException e) {
+ LOG.error("While looking for class identified by property 'sidebar'", e);
+ }
+ }
+
+ public UserFormLayoutInfo getCustomFormLayout() {
+ return customFormLayout;
+ }
+
+ public Class<? extends Sidebar> getSidebar() {
+ return sidebar;
}
@Override
@@ -418,7 +460,15 @@ public class SyncopeWebApplication extends WicketBootStandardWebApplication {
setUseCompression(useGZIPCompression);
}
- protected static Class<? extends WebPage> getSignInPageClass() {
+ public Class<? extends BasePage> getPageClass(final String key) {
+ return pageClasses.get(key);
+ }
+
+ public Class<? extends BasePage> getPageClass(final String key, final Class<? extends BasePage> defaultValue) {
+ return pageClasses.getOrDefault(key, defaultValue);
+ }
+
+ protected Class<? extends WebPage> getSignInPageClass() {
return Login.class;
}
@@ -458,12 +508,4 @@ public class SyncopeWebApplication extends WicketBootStandardWebApplication {
return maxWaitTime;
}
- public Map<String, CustomAttributesInfo> getCustomFormAttributes() {
- return customFormAttributes;
- }
-
- public void setCustomFormAttributes(final Map<String, CustomAttributesInfo> customFormAttributes) {
- this.customFormAttributes.clear();
- this.customFormAttributes.putAll(customFormAttributes);
- }
}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/assets/SyncopeEnduserCss.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/assets/SyncopeEnduserCss.java
deleted file mode 100644
index bc25789..0000000
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/assets/SyncopeEnduserCss.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.enduser.assets;
-
-import de.agilecoders.wicket.core.Bootstrap;
-import java.util.ArrayList;
-import java.util.List;
-import org.apache.wicket.markup.head.CssHeaderItem;
-import org.apache.wicket.markup.head.HeaderItem;
-import org.apache.wicket.request.resource.CssResourceReference;
-
-public class SyncopeEnduserCss extends CssResourceReference {
-
- private static final long serialVersionUID = 7244898174745686253L;
-
- /**
- * Singleton instance of this reference.
- */
- public static final SyncopeEnduserCss INSTANCE = new SyncopeEnduserCss();
-
- public SyncopeEnduserCss() {
- super(SyncopeEnduserCss.class, "css/syncopeEnduser.css");
- }
-
- @Override
- public List<HeaderItem> getDependencies() {
- final List<HeaderItem> dependencies = new ArrayList<>();
- dependencies.add(CssHeaderItem.forReference(Bootstrap.getSettings().getCssResourceReference()));
- dependencies.addAll(super.getDependencies());
- return dependencies;
- }
-
-}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserConstants.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/commons/EnduserConstants.java
similarity index 60%
copy from client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserConstants.java
copy to client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/commons/EnduserConstants.java
index 0a683a9..abdf652 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserConstants.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/commons/EnduserConstants.java
@@ -16,20 +16,23 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.client.enduser;
+package org.apache.syncope.client.enduser.commons;
-public final class SyncopeEnduserConstants {
+public final class EnduserConstants {
- public static final String CAPTCHA_SESSION_KEY = "captcha";
+ public static final String STATUS = "status";
- public static final String XSRF_COOKIE = "XSRF-TOKEN";
+ public static final String SUCCESS = "success";
- public static final String XSRF_HEADER_NAME = "X-XSRF-TOKEN";
+ public static final String LANDING_PAGE = "landingPage";
- public static final String MEMBERSHIP_ATTR_SEPARATOR = "#";
+ public static final String CONTENT_PANEL = "contentPanel";
- private SyncopeEnduserConstants() {
- // private constructor for utility class
- }
+ public static final String SELF_ALLOWED = "selfRegistration.allowed";
+
+ public static final String PAGE_TITLE = "pageTitle";
+ private EnduserConstants() {
+ // private constructor for static utility class
+ }
}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/init/ClassPathScanImplementationLookup.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/init/ClassPathScanImplementationLookup.java
index c89afff..6f1e334 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/init/ClassPathScanImplementationLookup.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/init/ClassPathScanImplementationLookup.java
@@ -24,8 +24,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.apache.commons.lang3.ArrayUtils;
-import org.apache.syncope.client.enduser.pages.BaseEnduserWebPage;
-import org.apache.syncope.client.enduser.pages.BaseExtPage;
+import org.apache.syncope.client.enduser.pages.BasePage;
import org.apache.syncope.client.ui.commons.annotations.BinaryPreview;
import org.apache.syncope.client.ui.commons.annotations.ExtPage;
import org.apache.syncope.client.ui.commons.annotations.Resource;
@@ -50,9 +49,7 @@ public class ClassPathScanImplementationLookup {
private List<Class<? extends BinaryPreviewer>> previewers;
- private List<Class<? extends BaseExtPage>> extPages;
-
- private List<Class<? extends BaseEnduserWebPage>> pages;
+ private List<Class<? extends BasePage>> extPages;
/**
* This method can be overridden by subclasses to customize classpath scan.
@@ -63,9 +60,8 @@ public class ClassPathScanImplementationLookup {
return DEFAULT_BASE_PACKAGE;
}
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({ "unchecked", "unchecked" })
public void load() {
- pages = new ArrayList<>();
previewers = new ArrayList<>();
extPages = new ArrayList<>();
ssoLoginFormPanels = new ArrayList<>();
@@ -73,10 +69,9 @@ public class ClassPathScanImplementationLookup {
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AssignableTypeFilter(AbstractResource.class));
- scanner.addIncludeFilter(new AssignableTypeFilter(BaseExtPage.class));
+ scanner.addIncludeFilter(new AssignableTypeFilter(BasePage.class));
scanner.addIncludeFilter(new AssignableTypeFilter(BaseSSOLoginFormPanel.class));
scanner.addIncludeFilter(new AssignableTypeFilter(BinaryPreviewer.class));
- scanner.addIncludeFilter(new AssignableTypeFilter(BaseEnduserWebPage.class));
scanner.findCandidateComponents(getBasePackage()).forEach(bd -> {
try {
@@ -84,9 +79,9 @@ public class ClassPathScanImplementationLookup {
ClassUtils.getDefaultClassLoader());
boolean isAbstractClazz = Modifier.isAbstract(clazz.getModifiers());
if (!isAbstractClazz) {
- if (BaseExtPage.class.isAssignableFrom(clazz)) {
+ if (BasePage.class.isAssignableFrom(clazz)) {
if (clazz.isAnnotationPresent(ExtPage.class)) {
- extPages.add((Class<? extends BaseExtPage>) clazz);
+ extPages.add((Class<? extends BasePage>) clazz);
} else {
LOG.error("Could not find annotation {} in {}, ignoring",
ExtPage.class.getName(), clazz.getName());
@@ -102,8 +97,6 @@ public class ClassPathScanImplementationLookup {
previewers.add((Class<? extends BinaryPreviewer>) clazz);
} else if (BaseSSOLoginFormPanel.class.isAssignableFrom(clazz)) {
ssoLoginFormPanels.add((Class<? extends BaseSSOLoginFormPanel>) clazz);
- } else if (BaseEnduserWebPage.class.isAssignableFrom(clazz)) {
- pages.add((Class<? extends BaseEnduserWebPage>) clazz);
}
}
} catch (Throwable t) {
@@ -114,8 +107,6 @@ public class ClassPathScanImplementationLookup {
ssoLoginFormPanels = Collections.unmodifiableList(ssoLoginFormPanels);
- pages = Collections.unmodifiableList(pages);
-
LOG.debug("Binary previewers found: {}", previewers);
LOG.debug("Extension pages found: {}", extPages);
LOG.debug("SSO Login pages found: {}", ssoLoginFormPanels);
@@ -144,11 +135,7 @@ public class ClassPathScanImplementationLookup {
return this.ssoLoginFormPanels;
}
- public List<Class<? extends BaseExtPage>> getExtPageClasses() {
+ public List<Class<? extends BasePage>> getExtPageClasses() {
return extPages;
}
-
- public List<Class<? extends BaseEnduserWebPage>> getPageClasses() {
- return pages;
- }
}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/AnyLayoutUtils.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/AnyLayoutUtils.java
deleted file mode 100644
index 5739f02..0000000
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/AnyLayoutUtils.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.enduser.layout;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.util.List;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.client.enduser.SyncopeEnduserSession;
-import org.apache.syncope.client.ui.commons.wizards.ModalPanelBuilder;
-import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
-import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.wicket.PageReference;
-
-/**
- * Utility methods for dealing with form layout information.
- */
-public final class AnyLayoutUtils {
-
- private static final ObjectMapper MAPPER;
-
- private static final String DEFAULT_USER_FORM_LAYOUT_INFO;
-
- static {
- MAPPER = new ObjectMapper();
- try {
- DEFAULT_USER_FORM_LAYOUT_INFO = MAPPER.writeValueAsString(new UserFormLayoutInfo());
- } catch (IOException e) {
- throw new IllegalArgumentException("While generating default enduser layout info for "
- + SyncopeEnduserSession.get().getSelfTO().getUsername(), e);
- }
- }
-
- public static String getDefaultValue() {
- return DEFAULT_USER_FORM_LAYOUT_INFO;
- }
-
- public static UserFormLayoutInfo fromJsonString(final String content) {
- try {
- return MAPPER.readValue(content, UserFormLayoutInfo.class);
- } catch (IOException e) {
- throw new IllegalArgumentException("While parsing console layout info for "
- + SyncopeEnduserSession.get().getSelfTO().getUsername(), e);
- }
- }
-
- public static String defaultConsoleLayoutInfoIfEmpty(final String content) {
- String result;
-
- if (StringUtils.isBlank(content)) {
- try {
- ObjectNode tree = MAPPER.createObjectNode();
-
- tree.set(AnyTypeKind.USER.name(), MAPPER.valueToTree(new UserFormLayoutInfo()));
-
- result = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(tree);
- } catch (IOException e) {
- throw new IllegalArgumentException("While generating default console layout info for "
- + SyncopeEnduserSession.get().getSelfTO().getUsername(), e);
- }
- } else {
- try {
- result = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(MAPPER.readTree(content));
- } catch (IOException e) {
- result = content;
- }
- }
-
- return result;
- }
-
- public static ModalPanelBuilder<AnyWrapper<UserTO>> newUserWizardBuilder(
- final UserTO userTO,
- final List<String> anyTypeClasses,
- final UserFormLayoutInfo userFormLayoutInfo,
- final PageReference pageRef) {
-
- try {
- return userFormLayoutInfo.getFormClass().getConstructor(
- userTO.getClass(), // previous
- userTO.getClass(), // actual
- List.class,
- userFormLayoutInfo.getClass(),
- pageRef.getClass()).
- newInstance(null, userTO, anyTypeClasses, userFormLayoutInfo, pageRef);
-
- } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException
- | IllegalArgumentException | InvocationTargetException e) {
- throw new IllegalArgumentException(
- "Could not instantiate " + userFormLayoutInfo.getFormClass().getName(), e);
- }
- }
-
- private AnyLayoutUtils() {
- // private constructor for static utility class
- }
-}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/CustomizationOption.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/CustomizationOption.java
index c716f07..0fe471f 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/CustomizationOption.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/CustomizationOption.java
@@ -30,9 +30,6 @@ public class CustomizationOption implements Serializable {
private List<String> defaultValues = new ArrayList<>();
- public CustomizationOption() {
- }
-
public boolean isReadonly() {
return readonly;
}
@@ -48,15 +45,4 @@ public class CustomizationOption implements Serializable {
public void setDefaultValues(final List<String> defaultValues) {
this.defaultValues = defaultValues;
}
-
- public CustomizationOption readonly(final Boolean value) {
- this.readonly = value;
- return this;
- }
-
- public CustomizationOption defaultValues(final List<String> value) {
- this.defaultValues = value;
- return this;
- }
-
}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/UserFormLayoutInfo.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/UserFormLayoutInfo.java
index 477c1d1..0c81adc 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/UserFormLayoutInfo.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/UserFormLayoutInfo.java
@@ -20,10 +20,10 @@ package org.apache.syncope.client.enduser.layout;
import java.util.HashMap;
import java.util.Map;
-import org.apache.syncope.client.enduser.wizards.any.UserWizardBuilder;
+import org.apache.syncope.client.enduser.panels.UserFormPanel;
import org.apache.syncope.client.ui.commons.layout.AbstractAnyFormBaseLayout;
-import org.apache.syncope.client.ui.commons.layout.UserForm;
import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.client.ui.commons.layout.UserForm;
public class UserFormLayoutInfo extends AbstractAnyFormBaseLayout<UserTO, UserForm> {
@@ -37,6 +37,8 @@ public class UserFormLayoutInfo extends AbstractAnyFormBaseLayout<UserTO, UserFo
private boolean passwordManagement = true;
+ private boolean detailsManagement = true;
+
public Map<String, CustomizationOption> getWhichPlainAttrs() {
return whichPlainAttrs;
}
@@ -51,7 +53,7 @@ public class UserFormLayoutInfo extends AbstractAnyFormBaseLayout<UserTO, UserFo
@Override
protected Class<? extends UserForm> getDefaultFormClass() {
- return UserWizardBuilder.class;
+ return UserFormPanel.class;
}
public boolean isPasswordManagement() {
@@ -61,4 +63,12 @@ public class UserFormLayoutInfo extends AbstractAnyFormBaseLayout<UserTO, UserFo
public void setPasswordManagement(final boolean passwordManagement) {
this.passwordManagement = passwordManagement;
}
+
+ public boolean isDetailsManagement() {
+ return detailsManagement;
+ }
+
+ public void setDetailsManagement(final boolean detailsManagement) {
+ this.detailsManagement = detailsManagement;
+ }
}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/markup/html/form/AjaxDownload.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/markup/html/form/AjaxDownload.java
deleted file mode 100644
index 8e1367a..0000000
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/markup/html/form/AjaxDownload.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.enduser.markup.html.form;
-
-import java.time.Duration;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.client.ui.commons.HttpResourceStream;
-import org.apache.syncope.client.ui.commons.MIMETypesLoader;
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.behavior.AbstractAjaxBehavior;
-import org.apache.wicket.request.handler.resource.ResourceStreamRequestHandler;
-import org.apache.wicket.request.resource.ContentDisposition;
-import org.apache.wicket.spring.injection.annot.SpringBean;
-
-public abstract class AjaxDownload extends AbstractAjaxBehavior {
-
- private static final long serialVersionUID = 7203445884857810583L;
-
- @SpringBean
- private MIMETypesLoader mimeTypesLoader;
-
- private final String name;
-
- private String fileKey;
-
- private String mimeType;
-
- private final boolean addAntiCache;
-
- public AjaxDownload(final String name, final boolean addAntiCache) {
- super();
- this.name = name;
- this.addAntiCache = addAntiCache;
- }
-
- public AjaxDownload(final String name, final String fileKey, final String mimeType, final boolean addAntiCache) {
- this(name, addAntiCache);
- this.fileKey = fileKey;
- this.mimeType = mimeType;
- }
-
- public void initiate(final AjaxRequestTarget target) {
-
- String url = getCallbackUrl().toString();
- if (addAntiCache) {
- url = url + (url.contains("?") ? "&" : "?");
- url = url + "antiCache=" + System.currentTimeMillis();
- }
- target.appendJavaScript("setTimeout(\"window.location.href='" + url + "'\", 100);");
- }
-
- @Override
- public void onRequest() {
- HttpResourceStream stream = getResourceStream();
- ResourceStreamRequestHandler handler = new ResourceStreamRequestHandler(stream);
- String key = StringUtils.isNotBlank(fileKey) ? fileKey + '_' : "";
- String ext = "";
- if (StringUtils.isNotBlank(mimeType)) {
- String extByMimeType = mimeTypesLoader.getFileExt(mimeType);
- ext = StringUtils.isBlank(extByMimeType) ? ".bin" : ('.' + extByMimeType);
- }
- String fileName = key + (stream.getFilename() == null ? name : stream.getFilename()) + ext;
-
- handler.setFileName(fileName);
- handler.setContentDisposition(ContentDisposition.ATTACHMENT);
- handler.setCacheDuration(Duration.ZERO);
- getComponent().getRequestCycle().scheduleRequestHandlerAfterCurrent(handler);
- }
-
- protected abstract HttpResourceStream getResourceStream();
-
-}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/markup/html/form/BinaryFieldPanel.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/markup/html/form/BinaryFieldPanel.java
index 512283d..f8d88a2 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/markup/html/form/BinaryFieldPanel.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/markup/html/form/BinaryFieldPanel.java
@@ -30,17 +30,19 @@ import java.util.ArrayList;
import java.util.Base64;
import java.util.Locale;
import java.util.Optional;
+import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.client.ui.commons.HttpResourceStream;
import org.apache.syncope.client.enduser.SyncopeWebApplication;
import org.apache.syncope.client.enduser.SyncopeEnduserSession;
import org.apache.syncope.client.enduser.commons.PreviewUtils;
import org.apache.syncope.client.ui.commons.Constants;
-import org.apache.syncope.client.ui.commons.markup.html.form.BaseBinaryFieldPanel;
+import org.apache.syncope.client.ui.commons.HttpResourceStream;
import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
import org.apache.syncope.client.ui.commons.markup.html.form.preview.BinaryPreviewer;
+import org.apache.syncope.client.ui.commons.markup.html.form.BaseBinaryFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.BinaryFieldDownload;
import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
import org.apache.syncope.client.ui.commons.rest.ResponseHolder;
import org.apache.wicket.Component;
@@ -82,7 +84,7 @@ public class BinaryFieldPanel extends BaseBinaryFieldPanel {
private final BootstrapFileInputField fileUpload;
- private final AjaxDownload fileDownload;
+ private final BinaryFieldDownload fileDownload;
private final BinaryPreviewer previewer;
@@ -100,6 +102,7 @@ public class BinaryFieldPanel extends BaseBinaryFieldPanel {
final IModel<String> model,
final String mimeType,
final String fileKey) {
+
super(id, name, model);
this.model = model;
this.fileKey = fileKey;
@@ -122,7 +125,7 @@ public class BinaryFieldPanel extends BaseBinaryFieldPanel {
public void renderHead(final IHeaderResponse response) {
if (previewer == null) {
FileinputJsReference.INSTANCE.renderHead(response);
- final JQuery fileinputJS = $(fileUpload).chain(new IFunction() {
+ JQuery fileinputJS = $(fileUpload).chain(new IFunction() {
private static final long serialVersionUID = -2285418135375523652L;
@@ -150,7 +153,7 @@ public class BinaryFieldPanel extends BaseBinaryFieldPanel {
uploadForm.add(new Label("preview", StringUtils.isBlank(mimeType) ? StringUtils.EMPTY : '(' + mimeType + ')'));
- fileDownload = new AjaxDownload(name, fileKey, mimeType, true) {
+ fileDownload = new BinaryFieldDownload(name, fileKey, mimeType, true) {
private static final long serialVersionUID = 7203445884857810583L;
@@ -185,10 +188,7 @@ public class BinaryFieldPanel extends BaseBinaryFieldPanel {
if (!Locale.ENGLISH.getLanguage().equals(language)) {
config.withLocale(language);
}
-
fileUpload = new BootstrapFileInputField("fileUpload", new ListModel<>(new ArrayList<>()), config);
- fileUpload.setOutputMarkupId(true);
-
fileUpload.add(new AjaxFormSubmitBehavior(Constants.ON_CHANGE) {
private static final long serialVersionUID = -1107858522700306810L;
@@ -253,11 +253,13 @@ public class BinaryFieldPanel extends BaseBinaryFieldPanel {
private Response buildResponse() {
return Response.ok(new ByteArrayInputStream(Base64.getMimeDecoder().decode(getModelObject()))).
- type(StringUtils.isBlank(mimeType) ? MediaType.APPLICATION_OCTET_STREAM : mimeType).build();
+ type(StringUtils.isBlank(mimeType) ? MediaType.APPLICATION_OCTET_STREAM : mimeType).
+ header(HttpHeaders.LOCATION, StringUtils.EMPTY).
+ build();
}
private void changePreviewer(final Component panelPreview) {
- final Fragment fragment = new Fragment("panelPreview", "previewFragment", container);
+ Fragment fragment = new Fragment("panelPreview", "previewFragment", container);
fragment.add(panelPreview);
container.addOrReplace(fragment);
uploadForm.addOrReplace(container);
@@ -297,4 +299,11 @@ public class BinaryFieldPanel extends BaseBinaryFieldPanel {
protected Integer getMaxUploadFileSizeMB() {
return SyncopeWebApplication.get().getMaxUploadFileSizeMB();
}
+
+ @Override
+ public FieldPanel<String> setReadOnly(final boolean readOnly) {
+ super.setReadOnly(readOnly);
+ fileUpload.setEnabled(!readOnly);
+ return this;
+ }
}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/navigation/Navbar.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/navigation/Navbar.java
deleted file mode 100644
index fa87444..0000000
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/navigation/Navbar.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.enduser.navigation;
-
-import java.util.ArrayList;
-import java.util.List;
-import org.apache.syncope.client.enduser.SyncopeEnduserSession;
-import org.apache.syncope.client.enduser.pages.BaseExtPage;
-import org.apache.syncope.client.enduser.pages.Logout;
-import org.apache.syncope.client.enduser.pages.Self;
-import org.apache.syncope.client.ui.commons.Constants;
-import org.apache.syncope.client.ui.commons.annotations.ExtPage;
-import org.apache.wicket.Component;
-import org.apache.wicket.Page;
-import org.apache.wicket.ajax.AjaxEventBehavior;
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.ajax.attributes.AjaxCallListener;
-import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
-import org.apache.wicket.ajax.markup.html.AjaxLink;
-import org.apache.wicket.behavior.Behavior;
-import org.apache.wicket.markup.ComponentTag;
-import org.apache.wicket.markup.html.WebMarkupContainer;
-import org.apache.wicket.markup.html.WebPage;
-import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.markup.html.link.BookmarkablePageLink;
-import org.apache.wicket.markup.html.list.ListItem;
-import org.apache.wicket.markup.html.list.ListView;
-import org.apache.wicket.markup.html.panel.Panel;
-
-public class Navbar extends Panel {
-
- private static final long serialVersionUID = 1323251762654401168L;
-
- private final ListView<Class<? extends BaseExtPage>> extPages;
-
- private final List<WebMarkupContainer> navbarItems = new ArrayList<>();
-
- public Navbar(final String id, final List<Class<? extends BaseExtPage>> extPageClasses) {
- super(id);
- setOutputMarkupId(true);
-
- WebMarkupContainer detailsLI = new WebMarkupContainer("detailsLI");
- detailsLI.setMarkupId("self");
- navbarItems.add(detailsLI);
- add(detailsLI);
-
- BookmarkablePageLink<Page> detailsLink = new BookmarkablePageLink<>("detailsLILink", Self.class);
- detailsLink.setOutputMarkupId(true);
- detailsLink.add(new Label("detailsLILabel", getString("details")));
- detailsLI.add(detailsLink);
-
- WebMarkupContainer extLI = new WebMarkupContainer("extensionsLI");
- extLI.setOutputMarkupPlaceholderTag(true);
- extLI.setVisible(!extPageClasses.isEmpty());
- add(extLI);
-
- extPages = new ListView<Class<? extends BaseExtPage>>("extPages", extPageClasses) {
-
- private static final long serialVersionUID = 4949588177564901031L;
-
- @Override
- protected void populateItem(final ListItem<Class<? extends BaseExtPage>> item) {
- WebMarkupContainer extPageLI = new WebMarkupContainer("extPageLI");
- item.add(extPageLI);
- extPageLI.setMarkupId(item.getModelObject().getSimpleName().toLowerCase());
- navbarItems.add(extPageLI);
-
- ExtPage ann = item.getModelObject().getAnnotation(ExtPage.class);
-
- BookmarkablePageLink<Page> extLIPageLink =
- new BookmarkablePageLink<>("extPageLILink", item.getModelObject());
- extLIPageLink.setOutputMarkupId(true);
- extLIPageLink.add(new Label("extPageLabel", ann.label()));
- extPageLI.add(extLIPageLink);
- }
- };
- extPages.setOutputMarkupId(true);
- extPages.setVisible(true);
- extLI.add(extPages);
-
- WebMarkupContainer logoLinkWmc = new WebMarkupContainer("logoIcon");
- logoLinkWmc.add(new AjaxEventBehavior("click") {
-
- private static final long serialVersionUID = -4255753643957306394L;
-
- @Override
- protected void onEvent(final AjaxRequestTarget target) {
- setResponsePage(getApplication().getHomePage());
- }
- });
- add(logoLinkWmc);
-
- @SuppressWarnings("unchecked")
- final Class<? extends WebPage> beforeLogout = (Class<? extends WebPage>) SyncopeEnduserSession.get().
- getAttribute(Constants.BEFORE_LOGOUT_PAGE);
- if (beforeLogout == null) {
- add(new BookmarkablePageLink<>("logout", Logout.class));
- } else {
- add(new AjaxLink<Page>("logout") {
-
- private static final long serialVersionUID = -4889563567201424183L;
-
- @Override
- protected void updateAjaxAttributes(final AjaxRequestAttributes attributes) {
- super.updateAjaxAttributes(attributes);
-
- AjaxCallListener ajaxCallListener = new AjaxCallListener();
- ajaxCallListener.onPrecondition("return confirm('" + getString("confirmGlobalLogout") + "');");
- attributes.getAjaxCallListeners().add(ajaxCallListener);
- }
-
- @Override
- public void onClick(final AjaxRequestTarget target) {
- setResponsePage(beforeLogout);
- }
- });
- }
- }
-
- public ListView<Class<? extends BaseExtPage>> getExtPages() {
- return extPages;
- }
-
- public void setActiveNavItem(final String id) {
- navbarItems.stream().
- filter(containingLI -> containingLI.getMarkupId().equals(id)).findFirst().
- ifPresent(found -> found.add(new Behavior() {
-
- private static final long serialVersionUID = -5775607340182293596L;
-
- @Override
- public void onComponentTag(final Component component, final ComponentTag tag) {
- tag.put("class", "active");
- }
- }));
- }
-}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/AbstractChangePassword.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/AbstractChangePassword.java
new file mode 100644
index 0000000..5944754
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/AbstractChangePassword.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.pages;
+
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.client.enduser.SyncopeWebApplication;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPasswordFieldPanel;
+import org.apache.syncope.client.enduser.panels.ChangePasswordPanel;
+import org.apache.syncope.client.ui.commons.Constants;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.wicket.AttributeModifier;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+
+public abstract class AbstractChangePassword extends BasePage {
+
+ private static final long serialVersionUID = 5889157642852559004L;
+
+ private static final String CHANGE_PASSWORD = "page.changePassword";
+
+ public AbstractChangePassword(final PageParameters parameters) {
+ super(parameters, CHANGE_PASSWORD);
+
+ WebMarkupContainer content = new WebMarkupContainer("content");
+ content.setOutputMarkupId(true);
+ contentWrapper.add(content);
+
+ ChangePasswordPanel changePasswordPanel = getPasswordPanel();
+ content.add(changePasswordPanel);
+ content.add(new AttributeModifier("style", "height: \"100%\""));
+ }
+
+ protected ChangePasswordPanel getPasswordPanel() {
+ ChangePasswordPanel changePasswordPanel = new ChangePasswordPanel("changePasswordPanel", notificationPanel) {
+
+ private static final long serialVersionUID = 5195544218030499386L;
+
+ @Override
+ protected void doSubmit(final AjaxRequestTarget target, final AjaxPasswordFieldPanel passwordField) {
+ boolean checked = true;
+ if (SyncopeWebApplication.get().isCaptchaEnabled()) {
+ checked = captcha.check();
+ }
+ if (!checked) {
+ SyncopeEnduserSession.get().error(getString(Constants.CAPTCHA_ERROR));
+ getNotificationPanel().refresh(target);
+ } else {
+ doPwdSubmit(target, passwordField);
+ }
+ }
+
+ @Override
+ protected void doCancel() {
+ doPwdCancel();
+ }
+
+ @Override
+ protected UserTO getLoggedUser() {
+ return getPwdLoggedUser();
+ }
+ };
+
+ changePasswordPanel.setOutputMarkupId(true);
+ return changePasswordPanel;
+ }
+
+ protected abstract void doPwdSubmit(AjaxRequestTarget target, AjaxPasswordFieldPanel passwordField);
+
+ protected abstract void doPwdCancel();
+
+ protected abstract UserTO getPwdLoggedUser();
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/BasePage.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/BasePage.java
new file mode 100644
index 0000000..eb238da
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/BasePage.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.pages;
+
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+import org.apache.syncope.client.enduser.SyncopeWebApplication;
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.client.enduser.commons.EnduserConstants;
+import org.apache.syncope.client.enduser.init.ClassPathScanImplementationLookup;
+import org.apache.syncope.client.enduser.wicket.markup.head.MetaHeaderItem;
+import org.apache.syncope.client.ui.commons.BaseSession;
+import org.apache.syncope.client.ui.commons.Constants;
+import org.apache.syncope.client.enduser.panels.Sidebar;
+import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
+import org.apache.wicket.AttributeModifier;
+import org.apache.wicket.Page;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.Session;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.attributes.AjaxCallListener;
+import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.behavior.AttributeAppender;
+import org.apache.wicket.markup.head.HeaderItem;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+
+public class BasePage extends BaseWebPage {
+
+ private static final long serialVersionUID = 1571997737305598502L;
+
+ @SpringBean
+ private ClassPathScanImplementationLookup lookup;
+
+ protected static final HeaderItem META_IE_EDGE = new MetaHeaderItem("X-UA-Compatible", "IE=edge");
+
+ protected final Sidebar sidebar;
+
+ protected final WebMarkupContainer contentWrapper;
+
+ protected final AjaxLink<Void> collapse;
+
+ public BasePage() {
+ this(null, null);
+ }
+
+ public BasePage(final PageParameters parameters, final String name) {
+ super(parameters);
+
+ Serializable leftMenuCollapse = SyncopeEnduserSession.get().getAttribute(Constants.MENU_COLLAPSE);
+ if ((leftMenuCollapse instanceof Boolean) && ((Boolean) leftMenuCollapse)) {
+ body.add(new AttributeAppender("class", " sidebar-collapse"));
+ }
+
+ // sidebar
+ Class<? extends Sidebar> clazz = SyncopeWebApplication.get().getSidebar();
+
+ try {
+ sidebar = clazz.getConstructor(
+ String.class,
+ PageReference.class,
+ List.class).
+ newInstance("sidebar", getPageReference(), lookup.getExtPageClasses());
+ } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException
+ | IllegalArgumentException | InvocationTargetException e) {
+ throw new IllegalArgumentException("Could not instantiate " + clazz.getName(), e);
+ }
+
+ sidebar.setOutputMarkupPlaceholderTag(true);
+ body.add(sidebar);
+
+ // contentWrapper
+ contentWrapper = new WebMarkupContainer("contentWrapper");
+ contentWrapper.setOutputMarkupPlaceholderTag(true);
+ body.add(contentWrapper);
+
+ //pageTitle
+ addPageTitle(name);
+
+ // collapse
+ collapse = new AjaxLink<Void>("collapse") {
+
+ private static final long serialVersionUID = -7978723352517770644L;
+
+ @Override
+ public void onClick(final AjaxRequestTarget target) {
+ Session.get().setAttribute(Constants.MENU_COLLAPSE,
+ Session.get().getAttribute(Constants.MENU_COLLAPSE) == null
+ ? true
+ : !(Boolean) Session.get().getAttribute(Constants.MENU_COLLAPSE));
+ }
+ };
+ collapse.setOutputMarkupPlaceholderTag(true);
+ body.add(collapse);
+
+ @SuppressWarnings("unchecked")
+ Class<? extends WebPage> beforeLogout = (Class<? extends WebPage>) Session.get().
+ getAttribute(Constants.BEFORE_LOGOUT_PAGE);
+ if (beforeLogout == null) {
+ body.add(new BookmarkablePageLink<>("logout", Logout.class));
+ } else {
+ body.add(new AjaxLink<Page>("logout") {
+
+ private static final long serialVersionUID = -7978723352517770644L;
+
+ @Override
+ protected void updateAjaxAttributes(final AjaxRequestAttributes attributes) {
+ super.updateAjaxAttributes(attributes);
+
+ AjaxCallListener ajaxCallListener = new AjaxCallListener();
+ ajaxCallListener.onPrecondition("return confirm('" + getString("confirmGlobalLogout") + "');");
+ attributes.getAjaxCallListeners().add(ajaxCallListener);
+ }
+
+ @Override
+ public void onClick(final AjaxRequestTarget target) {
+ setResponsePage(beforeLogout);
+ }
+ });
+ }
+ }
+
+ protected void addPageTitle(final String title) {
+ contentWrapper.addOrReplace(new Label(EnduserConstants.PAGE_TITLE, new ResourceModel(title, title)));
+ }
+
+ protected void disableSidebar() {
+ sidebar.setVisible(false);
+ collapse.setVisible(false);
+ contentWrapper.add(new AttributeModifier("style", "margin-left: 0px"));
+ }
+
+ protected void setDomain(final PageParameters parameters) {
+ if (parameters != null && !parameters.get("domain").isEmpty()) {
+ BaseSession.class.cast(Session.get()).setDomain(parameters.get("domain").toString());
+ }
+ }
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/BaseEnduserWebPage.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/Dashboard.java
similarity index 55%
rename from client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/BaseEnduserWebPage.java
rename to client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/Dashboard.java
index e7503bf..c99c789 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/BaseEnduserWebPage.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/Dashboard.java
@@ -18,31 +18,28 @@
*/
package org.apache.syncope.client.enduser.pages;
-import org.apache.syncope.client.enduser.init.ClassPathScanImplementationLookup;
-import org.apache.syncope.client.enduser.navigation.Navbar;
-import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
+import org.apache.syncope.client.enduser.widgets.UserProfileWidget;
+import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.request.mapper.parameter.PageParameters;
-import org.apache.wicket.spring.injection.annot.SpringBean;
-public class BaseEnduserWebPage extends BaseWebPage {
+public class Dashboard extends BasePage {
- private static final long serialVersionUID = 5760583420031293480L;
+ private static final long serialVersionUID = -1100228004207271270L;
- protected final Navbar navbar;
+ protected static final String HOME = "home";
- @SpringBean
- protected ClassPathScanImplementationLookup lookup;
+ protected final WebMarkupContainer content;
- public BaseEnduserWebPage() {
- this(null);
+ public Dashboard(final PageParameters parameters) {
+ super(parameters, HOME);
- body.add(navbar);
- }
+ content = new WebMarkupContainer("content");
+ content.setOutputMarkupId(true);
- public BaseEnduserWebPage(final PageParameters parameters) {
- super(parameters);
+ UserProfileWidget userProfileWidget = new UserProfileWidget("userProfileInfo");
+ userProfileWidget.setOutputMarkupId(true);
+ content.add(userProfileWidget);
- navbar = new Navbar("navbar", lookup.getExtPageClasses());
- body.add(navbar);
+ contentWrapper.add(content);
}
}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/EditChangePassword.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/EditChangePassword.java
new file mode 100644
index 0000000..fb54b7a
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/EditChangePassword.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.pages;
+
+import org.apache.syncope.client.enduser.SyncopeWebApplication;
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.client.enduser.commons.EnduserConstants;
+import org.apache.syncope.client.enduser.rest.UserSelfRestClient;
+import org.apache.syncope.client.ui.commons.Constants;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPasswordFieldPanel;
+import org.apache.syncope.common.lib.request.PasswordPatch;
+import org.apache.syncope.common.lib.request.UserUR;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+
+public class EditChangePassword extends AbstractChangePassword {
+
+ private static final long serialVersionUID = -537205681762708502L;
+
+ private final UserSelfRestClient userSelfRestClient = new UserSelfRestClient();
+
+ public EditChangePassword(final PageParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ protected void doPwdSubmit(final AjaxRequestTarget target, final AjaxPasswordFieldPanel passwordField) {
+ PageParameters parameters = new PageParameters();
+ try {
+ UserTO userTO = getPwdLoggedUser();
+
+ UserUR req = new UserUR();
+ req.setKey(userTO.getKey());
+ req.setPassword(new PasswordPatch.Builder().
+ value(passwordField.getModelObject()).onSyncope(true).resources(userTO.getResources()).build());
+ userSelfRestClient.update(userTO.getETagValue(), req);
+
+ parameters.add(EnduserConstants.STATUS, Constants.OPERATION_SUCCEEDED);
+ parameters.add(Constants.NOTIFICATION_TITLE_PARAM, getString("self.pwd.change.success.msg"));
+ parameters.add(Constants.NOTIFICATION_MSG_PARAM, getString("self.pwd.change.success"));
+ SyncopeEnduserSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
+ parameters.add(
+ EnduserConstants.LANDING_PAGE,
+ SyncopeWebApplication.get().getPageClass("profile", Dashboard.class).getName());
+ setResponsePage(SelfResult.class, parameters);
+ } catch (Exception e) {
+ LOG.error("While changing password for {}",
+ SyncopeEnduserSession.get().getSelfTO().getUsername(), e);
+ SyncopeEnduserSession.get().onException(e);
+ notificationPanel.refresh(target);
+ }
+ }
+
+ @Override
+ protected UserTO getPwdLoggedUser() {
+ return SyncopeEnduserSession.get().getSelfTO(true);
+ }
+
+ @Override
+ protected void doPwdCancel() {
+ setResponsePage(getApplication().getHomePage());
+ }
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/EditSecurityQuestion.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/EditSecurityQuestion.java
new file mode 100644
index 0000000..3a63cad
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/EditSecurityQuestion.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.pages;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.client.enduser.SyncopeWebApplication;
+import org.apache.syncope.client.enduser.commons.EnduserConstants;
+import org.apache.syncope.client.enduser.panels.captcha.CaptchaPanel;
+import org.apache.syncope.client.enduser.rest.SecurityQuestionRestClient;
+import org.apache.syncope.client.enduser.rest.UserSelfRestClient;
+import org.apache.syncope.client.ui.commons.Constants;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
+import org.apache.syncope.client.ui.commons.panels.CardPanel;
+import org.apache.syncope.common.lib.request.StringReplacePatchItem;
+import org.apache.syncope.common.lib.request.UserUR;
+import org.apache.syncope.common.lib.to.SecurityQuestionTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.wicket.ajax.AjaxEventBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.form.Button;
+import org.apache.wicket.markup.html.form.IChoiceRenderer;
+import org.apache.wicket.markup.html.form.StatelessForm;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+
+public class EditSecurityQuestion extends BasePage {
+
+ private static final long serialVersionUID = -537205681762708502L;
+
+ private static final String EDIT_SECURITY_QUESTION = "page.editSecurityQuestion";
+
+ private final UserSelfRestClient userSelfRestClient = new UserSelfRestClient();
+
+ private final AjaxDropDownChoicePanel<String> securityQuestion;
+
+ private final FieldPanel<String> securityAnswer;
+
+ private final UserTO userTO;
+
+ public EditSecurityQuestion(final PageParameters parameters) {
+ super(parameters, EDIT_SECURITY_QUESTION);
+
+ userTO = SyncopeEnduserSession.get().getSelfTO(true);
+
+ WebMarkupContainer content = new WebMarkupContainer("content");
+ content.setOutputMarkupId(true);
+ contentWrapper.add(content);
+
+ StatelessForm<Void> form = new StatelessForm<>("securityQuestionForm");
+ form.setOutputMarkupId(true);
+ content.add(form);
+
+ securityQuestion = new AjaxDropDownChoicePanel<>("securityQuestion",
+ "securityQuestion", new PropertyModel<>(userTO, "securityQuestion"));
+ securityQuestion.setNullValid(true);
+
+ List<SecurityQuestionTO> securityQuestions = SecurityQuestionRestClient.list();
+ securityQuestion.setChoices(securityQuestions.stream().
+ map(SecurityQuestionTO::getKey).collect(Collectors.toList()));
+ securityQuestion.setChoiceRenderer(new IChoiceRenderer<String>() {
+
+ private static final long serialVersionUID = -4421146737845000747L;
+
+ @Override
+ public Object getDisplayValue(final String value) {
+ return securityQuestions.stream().filter(sq -> value.equals(sq.getKey())).
+ map(SecurityQuestionTO::getContent).findFirst().orElse(null);
+ }
+
+ @Override
+ public String getIdValue(final String value, final int index) {
+ return value;
+ }
+
+ @Override
+ public String getObject(final String id, final IModel<? extends List<? extends String>> choices) {
+ return id;
+ }
+ });
+
+ securityQuestion.add(new AjaxEventBehavior(Constants.ON_CHANGE) {
+
+ private static final long serialVersionUID = 192359260308762078L;
+
+ @Override
+ protected void onEvent(final AjaxRequestTarget target) {
+ securityAnswer.setEnabled(StringUtils.isNotBlank(securityQuestion.getModelObject()));
+ target.add(securityAnswer);
+ }
+ });
+
+ form.add(securityQuestion);
+
+ securityAnswer = new AjaxTextFieldPanel("securityAnswer", "securityAnswer",
+ new PropertyModel<>(userTO, "securityAnswer"), false);
+ form.add(securityAnswer.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true).
+ setEnabled(StringUtils.isNotBlank(securityQuestion.getModelObject())));
+
+ CaptchaPanel<Void> captcha = new CaptchaPanel<>(EnduserConstants.CONTENT_PANEL);
+ captcha.setOutputMarkupPlaceholderTag(true);
+
+ form.add(new CardPanel.Builder<CaptchaPanel<Void>>()
+ .setName("captcha")
+ .setComponent(captcha)
+ .isVisible(SyncopeWebApplication.get().isCaptchaEnabled()).build("captchaPanelCard"));
+
+ AjaxButton submitButton = new AjaxButton("submit", new Model<>(getString("submit"))) {
+
+ private static final long serialVersionUID = 429178684321093953L;
+
+ @Override
+ protected void onSubmit(final AjaxRequestTarget target) {
+ if (StringUtils.isBlank(securityQuestion.getModelObject())
+ || StringUtils.isBlank(securityAnswer.getModelObject())) {
+
+ SyncopeEnduserSession.get().error(getString(Constants.CAPTCHA_ERROR));
+ ((BasePage) getPageReference().getPage()).getNotificationPanel().refresh(target);
+ } else {
+ boolean checked = true;
+ if (SyncopeWebApplication.get().isCaptchaEnabled()) {
+ checked = captcha.check();
+ }
+ if (!checked) {
+ SyncopeEnduserSession.get().error(getString(Constants.CAPTCHA_ERROR));
+ ((BasePage) getPageReference().getPage()).getNotificationPanel().refresh(target);
+ } else {
+ PageParameters parameters = new PageParameters();
+ try {
+ UserUR req = new UserUR();
+ req.setKey(userTO.getKey());
+ req.setSecurityQuestion(new StringReplacePatchItem.Builder().
+ value(securityQuestion.getModelObject()).build());
+ req.setSecurityAnswer(new StringReplacePatchItem.Builder().
+ value(securityAnswer.getModelObject()).build());
+ userSelfRestClient.update(userTO.getETagValue(), req);
+
+ parameters.add(EnduserConstants.STATUS, Constants.OPERATION_SUCCEEDED);
+ parameters.add(Constants.NOTIFICATION_TITLE_PARAM,
+ getString("self.securityquestion.change.success"));
+ parameters.add(Constants.NOTIFICATION_MSG_PARAM,
+ getString("self.securityquestion.change.success.msg"));
+ SyncopeEnduserSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
+ } catch (Exception e) {
+ LOG.error("While changing password for {}",
+ SyncopeEnduserSession.get().getSelfTO().getUsername(), e);
+ parameters.add(EnduserConstants.STATUS, Constants.OPERATION_ERROR);
+ parameters.add(Constants.NOTIFICATION_TITLE_PARAM,
+ getString("self.securityquestion.change.error"));
+ parameters.add(Constants.NOTIFICATION_MSG_PARAM,
+ getString("self.securityquestion.change.error.msg"));
+ SyncopeEnduserSession.get().onException(e);
+ notificationPanel.refresh(target);
+ }
+ parameters.add(
+ EnduserConstants.LANDING_PAGE,
+ SyncopeWebApplication.get().getPageClass("profile", Dashboard.class).getName());
+ setResponsePage(SelfResult.class, parameters);
+ }
+ }
+ }
+
+ @Override
+ protected void onError(final AjaxRequestTarget target) {
+ notificationPanel.refresh(target);
+ }
+ };
+ form.add(submitButton);
+
+ form.setDefaultButton(submitButton);
+
+ Button cancel = new Button("cancel") {
+
+ private static final long serialVersionUID = 3669569969172391336L;
+
+ @Override
+ public void onSubmit() {
+ setResponsePage(getApplication().getHomePage());
+ }
+ };
+
+ cancel.setOutputMarkupId(true);
+ cancel.setDefaultFormProcessing(false);
+ form.add(cancel);
+ }
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/EditUser.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/EditUser.java
new file mode 100644
index 0000000..341f185
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/EditUser.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.pages;
+
+import org.apache.syncope.client.enduser.SyncopeWebApplication;
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.client.enduser.layout.UserFormLayoutInfo;
+import org.apache.syncope.client.enduser.panels.UserFormPanel;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.rest.api.service.SyncopeService;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+
+public class EditUser extends BasePage {
+
+ private static final long serialVersionUID = -1100228004207271270L;
+
+ private static final String EDIT_USER = "page.edituser";
+
+ protected WebMarkupContainer content;
+
+ public EditUser(final PageParameters parameters) {
+ super(parameters, EDIT_USER);
+
+ content = new WebMarkupContainer("content");
+ content.setOutputMarkupId(true);
+ contentWrapper.add(content);
+
+ UserTO userTO = SyncopeEnduserSession.get().getSelfTO(true);
+
+ UserFormPanel editUserPanel = new UserFormPanel(
+ "editUserPanel",
+ userTO,
+ userTO,
+ SyncopeEnduserSession.get().getService(SyncopeService.class).platform().getUserClasses(),
+ buildFormLayout(),
+ getPageReference());
+ editUserPanel.setOutputMarkupId(true);
+ content.add(editUserPanel);
+ }
+
+ protected UserFormLayoutInfo buildFormLayout() {
+ UserFormLayoutInfo customlayoutInfo = SyncopeWebApplication.get().getCustomFormLayout();
+ return customlayoutInfo != null ? customlayoutInfo : new UserFormLayoutInfo();
+ }
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/Login.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/Login.java
index c9f9a91..bcc26ad 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/Login.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/Login.java
@@ -22,12 +22,10 @@ import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-import java.util.Locale;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import org.apache.syncope.client.enduser.SyncopeWebApplication;
import org.apache.syncope.client.enduser.SyncopeEnduserSession;
-import org.apache.syncope.client.enduser.init.ClassPathScanImplementationLookup;
+import org.apache.syncope.client.enduser.SyncopeWebApplication;
import org.apache.syncope.client.ui.commons.BaseLogin;
import org.apache.syncope.client.ui.commons.BaseSession;
import org.apache.wicket.Component;
@@ -35,27 +33,27 @@ import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
-import org.apache.wicket.spring.injection.annot.SpringBean;
public class Login extends BaseLogin {
- private static final long serialVersionUID = -3422492668689122688L;
+ private static final long serialVersionUID = 5889157642852559004L;
- @SpringBean
- private ClassPathScanImplementationLookup lookup;
+ private final BookmarkablePageLink<Void> selfPwdReset;
private final BookmarkablePageLink<Void> selfRegistration;
- private final BookmarkablePageLink<Void> selfPwdReset;
-
public Login(final PageParameters parameters) {
super(parameters);
- selfRegistration = new BookmarkablePageLink<>("self-registration", Self.class);
- add(selfRegistration.setOutputMarkupId(true));
-
selfPwdReset = new BookmarkablePageLink<>("self-pwd-reset", SelfPasswordReset.class);
- add(selfPwdReset.setOutputMarkupId(true));
+ selfPwdReset.getPageParameters().add("domain", SyncopeEnduserSession.get().getDomain());
+ selfPwdReset.setVisible(SyncopeEnduserSession.get().getPlatformInfo().isPwdResetAllowed());
+ add(selfPwdReset.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true));
+
+ selfRegistration = new BookmarkablePageLink<>("self-registration", SelfRegistration.class);
+ selfRegistration.getPageParameters().add("domain", SyncopeEnduserSession.get().getDomain());
+ selfRegistration.setVisible(SyncopeEnduserSession.get().getPlatformInfo().isSelfRegAllowed());
+ add(selfRegistration.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true));
}
@Override
@@ -74,10 +72,10 @@ public class Login extends BaseLogin {
@Override
protected List<Panel> getSSOLoginFormPanels() {
List<Panel> ssoLoginFormPanels = new ArrayList<>();
- lookup.getSSOLoginFormPanels().forEach(ssoLoginFormPanel -> {
+ SyncopeWebApplication.get().getLookup().getSSOLoginFormPanels().forEach(ssoLoginFormPanel -> {
try {
- ssoLoginFormPanels.add(ssoLoginFormPanel.getConstructor(String.class, BaseSession.class).
- newInstance("ssoLogin", SyncopeEnduserSession.get()));
+ ssoLoginFormPanels.add(ssoLoginFormPanel.getConstructor(String.class, BaseSession.class).newInstance(
+ "ssoLogin", SyncopeEnduserSession.get()));
} catch (Exception e) {
LOG.error("Could not initialize the provided SSO login form panel", e);
}
@@ -96,26 +94,22 @@ public class Login extends BaseLogin {
}
@Override
- protected List<Locale> getSupportedLocales() {
- return SyncopeWebApplication.SUPPORTED_LOCALES;
- }
+ protected void authenticate(final String username, final String password, final AjaxRequestTarget target)
+ throws AccessControlException {
- @Override
- protected void authenticate(
- final String username,
- final String password,
- final AjaxRequestTarget target) throws AccessControlException {
+ if (SyncopeWebApplication.get().getAnonymousUser().equals(username)
+ || SyncopeWebApplication.get().getAdminUser().equals(username)) {
- if (!SyncopeWebApplication.get().getAdminUser().equalsIgnoreCase(username)
- && !SyncopeWebApplication.get().getAnonymousUser().equalsIgnoreCase(username)
- && SyncopeEnduserSession.get().authenticate(username, password)) {
+ throw new AccessControlException("Illegal username");
+ }
- // user has been authenticated successfully
+ if (SyncopeEnduserSession.get().authenticate(username, password)) {
+ // If login has been called because the user was not yet logged in, than continue to the
+ // original destination, otherwise to the Home page
continueToOriginalDestination();
setResponsePage(getApplication().getHomePage());
} else {
- // not authenticated
- sendError(getString("login-error"));
+ SyncopeEnduserSession.get().error(getString("login-error"));
notificationPanel.refresh(target);
}
}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/MustChangePassword.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/MustChangePassword.java
index 0480618..4649c41 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/MustChangePassword.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/MustChangePassword.java
@@ -19,48 +19,57 @@
package org.apache.syncope.client.enduser.pages;
import org.apache.syncope.client.enduser.SyncopeEnduserSession;
-import org.apache.syncope.client.enduser.rest.UserSelfRestClient;
+import org.apache.syncope.client.enduser.commons.EnduserConstants;
import org.apache.syncope.client.ui.commons.Constants;
-import org.apache.syncope.client.ui.commons.pages.AbstractMustChangePassword;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPasswordFieldPanel;
+import org.apache.syncope.client.enduser.rest.UserSelfRestClient;
import org.apache.syncope.common.lib.to.UserTO;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.request.mapper.parameter.PageParameters;
-public class MustChangePassword extends AbstractMustChangePassword {
+public class MustChangePassword extends AbstractChangePassword {
private static final long serialVersionUID = 8581970794722709800L;
public MustChangePassword(final PageParameters parameters) {
super(parameters);
+
+ setDomain(parameters);
+ disableSidebar();
}
@Override
- protected void doSubmit(final AjaxRequestTarget target) {
+ protected void doPwdSubmit(final AjaxRequestTarget target, final AjaxPasswordFieldPanel passwordField) {
+ PageParameters parameters = new PageParameters();
try {
UserSelfRestClient.changePassword(passwordField.getModelObject());
SyncopeEnduserSession.get().invalidate();
-
- final PageParameters parameters = new PageParameters();
+ parameters.add(EnduserConstants.STATUS, Constants.OPERATION_SUCCEEDED);
+ parameters.add(Constants.NOTIFICATION_TITLE_PARAM, getString("self.pwd.change.success"));
parameters.add(Constants.NOTIFICATION_MSG_PARAM, getString("self.pwd.change.success"));
- setResponsePage(getApplication().getHomePage(), parameters);
-
- setResponsePage(getApplication().getHomePage(), parameters);
+ SyncopeEnduserSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
} catch (Exception e) {
LOG.error("While changing password for {}",
SyncopeEnduserSession.get().getSelfTO().getUsername(), e);
+ parameters.add(EnduserConstants.STATUS, Constants.OPERATION_ERROR);
+ parameters.add(Constants.NOTIFICATION_TITLE_PARAM, getString("self.pwd.change.error"));
+ parameters.add(Constants.NOTIFICATION_MSG_PARAM, getString("self.pwd.change.error.msg"));
SyncopeEnduserSession.get().onException(e);
- notificationPanel.refresh(target);
}
+ notificationPanel.refresh(target);
+ setResponsePage(SelfResult.class, parameters);
}
@Override
- protected UserTO getLoggedUser() {
+ protected UserTO getPwdLoggedUser() {
return SyncopeEnduserSession.get().getSelfTO();
}
@Override
- protected void doCancel() {
- setResponsePage(getApplication().getHomePage());
+ protected void doPwdCancel() {
+ SyncopeEnduserSession.get().invalidate();
+ final PageParameters parameters = new PageParameters();
+ setResponsePage(getApplication().getHomePage(), parameters);
}
}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/Self.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/Self.java
deleted file mode 100644
index aab4197..0000000
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/Self.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.enduser.pages;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.client.enduser.SyncopeEnduserSession;
-import org.apache.syncope.client.enduser.layout.AnyLayoutUtils;
-import org.apache.syncope.client.enduser.layout.UserFormLayoutInfo;
-import org.apache.syncope.client.ui.commons.Constants;
-import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
-import org.apache.syncope.client.ui.commons.wizards.AjaxWizardBuilder;
-import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
-import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
-import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
-import org.apache.syncope.common.lib.SyncopeConstants;
-import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.syncope.common.rest.api.service.SyncopeService;
-import org.apache.wicket.event.IEvent;
-import org.apache.wicket.event.IEventSource;
-import org.apache.wicket.markup.html.WebPage;
-import org.apache.wicket.request.mapper.parameter.PageParameters;
-import org.apache.wicket.spring.injection.annot.SpringBean;
-
-public class Self extends BaseEnduserWebPage implements IEventSource {
-
- private static final long serialVersionUID = 164651008547631054L;
-
- public static final String NEW_USER_PARAM = "newUser";
-
- private static final ObjectMapper MAPPER = new ObjectMapper();
-
- @SpringBean
- private ConfParamOps confParamOps;
-
- private AjaxWizardBuilder<AnyWrapper<UserTO>> wizardBuilder;
-
- protected static final String WIZARD_ID = "wizard";
-
- public Self(final PageParameters parameters) {
- super(parameters);
-
- body.add(buildWizard(SyncopeEnduserSession.get().isAuthenticated()
- ? SyncopeEnduserSession.get().getSelfTO()
- : buildNewUserTO(parameters),
- SyncopeEnduserSession.get().isAuthenticated()
- ? AjaxWizard.Mode.EDIT
- : AjaxWizard.Mode.CREATE));
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public void onEvent(final IEvent<?> event) {
- if (event.getPayload() instanceof AjaxWizard.NewItemEvent) {
- if (event.getPayload() instanceof AjaxWizard.NewItemCancelEvent) {
- @SuppressWarnings("unchecked")
- final Class<? extends WebPage> beforeLogout = (Class<? extends WebPage>) SyncopeEnduserSession.get().
- getAttribute(Constants.BEFORE_LOGOUT_PAGE);
- if (beforeLogout == null) {
- SyncopeEnduserSession.get().invalidate();
- setResponsePage(getApplication().getHomePage());
- } else {
- setResponsePage(beforeLogout);
- }
- } else if (event.getPayload() instanceof AjaxWizard.NewItemFinishEvent) {
- SyncopeEnduserSession.get().invalidate();
-
- final PageParameters parameters = new PageParameters();
- parameters.add(Constants.NOTIFICATION_MSG_PARAM, getString("self.wizard.success"));
- setResponsePage(getApplication().getHomePage(), parameters);
- }
- }
- super.onEvent(event);
- }
-
- @Override
- protected void onBeforeRender() {
- super.onBeforeRender();
- navbar.setActiveNavItem(getClass().getSimpleName().toLowerCase());
- }
-
- protected final AjaxWizard<AnyWrapper<UserTO>> buildWizard(final UserTO userTO, final AjaxWizard.Mode mode) {
- String formLayoutConfParam = confParamOps.get(
- SyncopeEnduserSession.get().getDomain(),
- Constants.ENDUSER_ANYLAYOUT,
- AnyLayoutUtils.getDefaultValue(),
- String.class);
-
- UserFormLayoutInfo formLayoutInfo =
- StringUtils.isBlank(formLayoutConfParam)
- ? new UserFormLayoutInfo()
- : AnyLayoutUtils.fromJsonString(formLayoutConfParam);
-
- wizardBuilder = (AjaxWizardBuilder<AnyWrapper<UserTO>>) AnyLayoutUtils.newUserWizardBuilder(
- userTO,
- SyncopeEnduserSession.get().getService(SyncopeService.class).platform().getUserClasses(),
- formLayoutInfo,
- this.getPageReference());
- wizardBuilder.setItem(new UserWrapper(userTO));
- return wizardBuilder.build(WIZARD_ID, mode);
- }
-
- private static UserTO buildNewUserTO(final PageParameters parameters) {
- UserTO userTO = null;
- if (parameters != null) {
- if (!parameters.get(NEW_USER_PARAM).isNull()) {
- try {
- userTO = MAPPER.readValue(parameters.get(NEW_USER_PARAM).toString(), UserTO.class);
- } catch (JsonProcessingException e) {
- LOG.error("While reading user data from social registration", e);
- }
- }
- }
- if (userTO == null) {
- userTO = new UserTO();
- }
- userTO.setRealm(SyncopeConstants.ROOT_REALM);
- return userTO;
- }
-}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfConfirmPasswordReset.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfConfirmPasswordReset.java
index f937507..6038d73 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfConfirmPasswordReset.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfConfirmPasswordReset.java
@@ -18,127 +18,95 @@
*/
package org.apache.syncope.client.enduser.pages;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.password.strength.PasswordStrengthBehavior;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.password.strength.PasswordStrengthConfig;
import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.client.enduser.commons.EnduserConstants;
import org.apache.syncope.client.ui.commons.Constants;
-import org.apache.syncope.client.ui.commons.DomainDropDown;
-import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPasswordFieldPanel;
-import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
-import org.apache.syncope.common.keymaster.client.api.DomainOps;
-import org.apache.syncope.common.keymaster.client.api.model.Domain;
+import org.apache.syncope.client.ui.commons.panels.CardPanel;
+import org.apache.syncope.client.ui.commons.wizards.any.PasswordPanel;
+import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.to.UserTO;
import org.apache.syncope.common.rest.api.service.UserSelfService;
import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.Form;
-import org.apache.wicket.markup.html.form.PasswordTextField;
import org.apache.wicket.markup.html.form.StatelessForm;
-import org.apache.wicket.markup.html.form.validation.EqualPasswordInputValidator;
-import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.request.mapper.parameter.PageParameters;
-import org.apache.wicket.spring.injection.annot.SpringBean;
-public class SelfConfirmPasswordReset extends BaseEnduserWebPage {
+public class SelfConfirmPasswordReset extends BasePage {
private static final long serialVersionUID = -2166782304542750726L;
- @SpringBean
- private DomainOps domainOps;
+ private static final String CONFIRM_PASSWORD_RESET = "confirmPasswordReset";
- private final LoadableDetachableModel<List<String>> domains = new LoadableDetachableModel<List<String>>() {
+ public SelfConfirmPasswordReset(final PageParameters parameters) {
+ super(parameters, CONFIRM_PASSWORD_RESET);
- private static final long serialVersionUID = 4659376149825914247L;
+ setDomain(parameters);
+ disableSidebar();
- @Override
- protected List<String> load() {
- List<String> current = new ArrayList<>();
- current.addAll(domainOps.list().stream().map(Domain::getKey).sorted().collect(Collectors.toList()));
- current.add(0, SyncopeConstants.MASTER_DOMAIN);
- return current;
- }
- };
+ if (parameters == null || parameters.get("token").isEmpty()) {
+ LOG.error("No token parameter found in the request url");
- public SelfConfirmPasswordReset(final PageParameters parameters) {
- super(parameters);
-
- if (parameters.get("token").isEmpty()) {
- LOG.debug("No token parameter found in the request url");
- parameters.add("errorMessage", getString("self.confirm.pwd.reset.error.empty"));
- setResponsePage(getApplication().getHomePage(), parameters);
+ PageParameters homeParameters = new PageParameters();
+ homeParameters.add("errorMessage", getString("self.confirm.pwd.reset.error.empty"));
+ setResponsePage(getApplication().getHomePage(), homeParameters);
}
- navbar.setEnabled(false);
- navbar.setVisible(false);
-
WebMarkupContainer content = new WebMarkupContainer("content");
content.setOutputMarkupId(true);
- body.add(content);
+ contentWrapper.add(content);
Form<?> form = new StatelessForm<>("selfConfirmPwdResetForm");
form.setOutputMarkupId(true);
content.add(form);
- DomainDropDown domainSelect = new DomainDropDown("domain", domains);
- domainSelect.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_BLUR) {
-
- private static final long serialVersionUID = -1107858522700306810L;
-
- @Override
- protected void onUpdate(final AjaxRequestTarget target) {
- // nothing to do
- }
- }).add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
-
- private static final long serialVersionUID = -1107858522700306810L;
-
- @Override
- protected void onUpdate(final AjaxRequestTarget target) {
- // nothing to do
- }
- });
- form.add(domainSelect);
-
- AjaxPasswordFieldPanel passwordField = new AjaxPasswordFieldPanel(
- "password", getString("password"), new Model<>());
- passwordField.setRequired(true);
- passwordField.setMarkupId("password");
- passwordField.setPlaceholder(getString("password"));
- ((PasswordTextField) passwordField.getField()).setResetPassword(false);
- form.add(passwordField);
-
- FieldPanel<String> confirmPasswordField = new AjaxPasswordFieldPanel(
- "confirmPassword", getString("confirm-password"), new Model<>());
- confirmPasswordField.setRequired(true);
- confirmPasswordField.setMarkupId("confirmPassword");
- confirmPasswordField.setPlaceholder(getString("confirm-password"));
- ((PasswordTextField) confirmPasswordField.getField()).setResetPassword(false);
- form.add(confirmPasswordField);
-
- form.add(new EqualPasswordInputValidator(passwordField.getField(), confirmPasswordField.getField()));
-
- AjaxButton submitButton = new AjaxButton("submit", new Model<>(getString("submit"))) {
+ UserTO fakeUserTO = new UserTO();
+ PasswordPanel passwordPanel = new PasswordPanel(
+ EnduserConstants.CONTENT_PANEL,
+ new UserWrapper(fakeUserTO),
+ false,
+ false,
+ new PasswordStrengthBehavior(new PasswordStrengthConfig().
+ withDebug(false).
+ withShowVerdictsInsideProgressBar(true).
+ withShowProgressBar(true)));
+ passwordPanel.setOutputMarkupId(true);
+
+ form.add(new CardPanel.Builder<PasswordPanel>()
+ .setName("selfConfirmPasswordResetPanel")
+ .setComponent(passwordPanel)
+ .isVisible(true)
+ .build("selfConfirmPasswordResetPanelCard"));
+
+ AjaxButton submit = new AjaxButton("submit", new Model<>(getString("submit"))) {
private static final long serialVersionUID = 509325877101838812L;
@Override
protected void onSubmit(final AjaxRequestTarget target) {
+ PageParameters params = new PageParameters();
try {
SyncopeEnduserSession.get().getService(UserSelfService.class).confirmPasswordReset(
- parameters.get("token").toString(), passwordField.getDefaultModelObjectAsString());
- PageParameters parameters = new PageParameters();
- parameters.add(Constants.NOTIFICATION_MSG_PARAM, getString("self.confirm.pwd.reset.success"));
- setResponsePage(getApplication().getHomePage(), parameters);
+ parameters.get("token").toString(), fakeUserTO.getPassword());
+ params.add(EnduserConstants.STATUS, Constants.OPERATION_SUCCEEDED);
+ params.add(Constants.NOTIFICATION_TITLE_PARAM, getString("self.confirm.pwd.reset.success"));
+ params.add(Constants.NOTIFICATION_MSG_PARAM, getString("self.confirm.pwd.reset.success.msg"));
+ SyncopeEnduserSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
+ parameters.add(EnduserConstants.LANDING_PAGE, Login.class.getName());
+ setResponsePage(SelfResult.class, params);
} catch (SyncopeClientException sce) {
LOG.error("Unable to complete the 'Password Reset Confirmation' process", sce);
+ params.add(EnduserConstants.STATUS, Constants.OPERATION_ERROR);
+ params.add(Constants.NOTIFICATION_TITLE_PARAM, getString("self.confirm.pwd.reset.error"));
+ params.add(Constants.NOTIFICATION_MSG_PARAM, getString("self.confirm.pwd.reset.error.msg"));
SyncopeEnduserSession.get().onException(sce);
- ((BaseEnduserWebPage) getPageReference().getPage()).getNotificationPanel().refresh(target);
+ ((BasePage) getPageReference().getPage()).getNotificationPanel().refresh(target);
}
}
@@ -147,8 +115,8 @@ public class SelfConfirmPasswordReset extends BaseEnduserWebPage {
notificationPanel.refresh(target);
}
};
- form.setDefaultButton(submitButton);
- form.add(submitButton);
+ form.setDefaultButton(submit);
+ form.add(submit);
Button cancel = new Button("cancel") {
@@ -158,7 +126,6 @@ public class SelfConfirmPasswordReset extends BaseEnduserWebPage {
public void onSubmit() {
setResponsePage(getApplication().getHomePage());
}
-
};
cancel.setOutputMarkupId(true);
cancel.setDefaultFormProcessing(false);
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfPasswordReset.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfPasswordReset.java
index 0d53e99..3d34ed0 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfPasswordReset.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfPasswordReset.java
@@ -18,33 +18,219 @@
*/
package org.apache.syncope.client.enduser.pages;
-import org.apache.syncope.client.enduser.panels.SelfPwdResetPanel;
+import org.apache.syncope.client.enduser.SyncopeWebApplication;
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.client.enduser.commons.EnduserConstants;
+import org.apache.syncope.client.enduser.panels.captcha.CaptchaPanel;
+import org.apache.syncope.client.enduser.rest.UserSelfRestClient;
+import org.apache.syncope.client.ui.commons.Constants;
+import org.apache.syncope.client.ui.commons.panels.CardPanel;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.SecurityQuestionTO;
+import org.apache.syncope.common.rest.api.service.SecurityQuestionService;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.ResourceModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
-public class SelfPasswordReset extends BaseEnduserWebPage {
+public class SelfPasswordReset extends BasePage {
private static final long serialVersionUID = 164651008547631054L;
+ private static final String SELF_PWD_RESET = "page.selfPwdReset";
+
+ private String usernameValue;
+
+ private String securityAnswerValue;
+
+ private final CaptchaPanel<Void> captcha;
+
private final SelfPwdResetPanel pwdResetPanel;
public SelfPasswordReset(final PageParameters parameters) {
- super(parameters);
+ super(parameters, SELF_PWD_RESET);
+
+ setDomain(parameters);
+ disableSidebar();
- navbar.setEnabled(false);
- navbar.setVisible(false);
+ captcha = new CaptchaPanel<>("captchaPanel");
+ captcha.setOutputMarkupPlaceholderTag(true);
+ captcha.setVisible(SyncopeWebApplication.get().isCaptchaEnabled());
WebMarkupContainer content = new WebMarkupContainer("content");
content.setOutputMarkupId(true);
- body.add(content);
+ contentWrapper.add(content);
Form<?> form = new Form<>("selfPwdResetForm");
content.add(form);
- pwdResetPanel = new SelfPwdResetPanel("selfPwdResetPanel", getPageReference());
+ pwdResetPanel = new SelfPwdResetPanel(EnduserConstants.CONTENT_PANEL, captcha, getPageReference());
pwdResetPanel.setOutputMarkupId(true);
- form.add(pwdResetPanel);
+ form.add(new CardPanel.Builder<SelfPwdResetPanel>()
+ .setName("selfPasswordResetPanel")
+ .setComponent(pwdResetPanel)
+ .isVisible(true)
+ .build("selfPasswordResetPanelCard"));
+
+ AjaxButton submitButton = new AjaxButton("submit") {
+
+ private static final long serialVersionUID = 4284361595033427185L;
+
+ @Override
+ protected void onSubmit(final AjaxRequestTarget target) {
+ boolean checked = true;
+ if (SyncopeWebApplication.get().isCaptchaEnabled()) {
+ checked = captcha.check();
+ }
+ if (!checked) {
+ SyncopeEnduserSession.get().error(getString(Constants.CAPTCHA_ERROR));
+ SelfPasswordReset.this.getNotificationPanel().refresh(target);
+ } else {
+ PageParameters parameters = new PageParameters();
+ try {
+ UserSelfRestClient.requestPasswordReset(usernameValue, securityAnswerValue);
+ parameters.add(EnduserConstants.STATUS, Constants.OPERATION_SUCCEEDED);
+ parameters.add(Constants.NOTIFICATION_TITLE_PARAM, getString("self.pwd.reset.success"));
+ parameters.add(Constants.NOTIFICATION_MSG_PARAM, getString("self.pwd.reset.success.msg"));
+ parameters.add(EnduserConstants.LANDING_PAGE, Login.class.getName());
+ setResponsePage(SelfResult.class, parameters);
+ } catch (SyncopeClientException sce) {
+ LOG.error("Unable to reset password of [{}]", usernameValue, sce);
+ SyncopeEnduserSession.get().onException(sce);
+ SelfPasswordReset.this.getNotificationPanel().refresh(target);
+ }
+ }
+ }
+
+ };
+ submitButton.setOutputMarkupId(true);
+ submitButton.setDefaultFormProcessing(false);
+ form.add(submitButton);
+
+ Button cancel = new Button("cancel") {
+
+ private static final long serialVersionUID = 3669569969172391336L;
+
+ @Override
+ public void onSubmit() {
+ setResponsePage(getApplication().getHomePage());
+ }
+
+ };
+ cancel.setOutputMarkupId(true);
+ cancel.setDefaultFormProcessing(false);
+ form.add(cancel);
+ }
+
+ public class SelfPwdResetPanel extends Panel {
+
+ private static final long serialVersionUID = -2841210052053545578L;
+
+ private final TextField<String> securityQuestion;
+
+ SelfPwdResetPanel(final String id, final CaptchaPanel<Void> captcha, final PageReference pageRef) {
+ super(id);
+
+ boolean isSecurityQuestionEnabled =
+ SyncopeEnduserSession.get().getPlatformInfo().isPwdResetRequiringSecurityQuestions();
+
+ TextField<String> username = new TextField<>("username",
+ new PropertyModel<>(SelfPasswordReset.this, "usernameValue"), String.class);
+ username.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_BLUR) {
+
+ private static final long serialVersionUID = -1107858522700306810L;
+
+ @Override
+ protected void onUpdate(final AjaxRequestTarget target) {
+ if (isSecurityQuestionEnabled) {
+ loadSecurityQuestion(pageRef, target);
+ }
+ }
+ });
+ username.setRequired(true);
+ add(username);
+
+ Label sqLabel =
+ new Label("securityQuestionLabel", new ResourceModel("securityQuestion", "securityQuestion"));
+ sqLabel.setOutputMarkupPlaceholderTag(true);
+ sqLabel.setVisible(isSecurityQuestionEnabled);
+ add(sqLabel);
+
+ securityQuestion =
+ new TextField<>("securityQuestion", new PropertyModel<>(Model.of(), "content"), String.class);
+ securityQuestion.setOutputMarkupId(true);
+ securityQuestion.setEnabled(false);
+ securityQuestion.setOutputMarkupPlaceholderTag(true);
+ securityQuestion.setVisible(isSecurityQuestionEnabled);
+ add(securityQuestion);
+
+ Label notLoading = new Label("not.loading", new ResourceModel("not.loading", "not.loading"));
+ notLoading.setOutputMarkupPlaceholderTag(true);
+ notLoading.setVisible(isSecurityQuestionEnabled);
+ add(notLoading);
+
+ AjaxLink<Void> reloadLink = new AjaxLink<Void>("reloadLink") {
+
+ private static final long serialVersionUID = -817438685948164787L;
+
+ @Override
+ public void onClick(final AjaxRequestTarget target) {
+ loadSecurityQuestion(pageRef, target);
+ }
+ };
+ reloadLink.setOutputMarkupPlaceholderTag(true);
+ reloadLink.setVisible(isSecurityQuestionEnabled);
+ add(reloadLink);
+
+ Label saLabel = new Label("securityAnswerLabel", new ResourceModel("securityAnswer", "securityAnswer"));
+ saLabel.setOutputMarkupPlaceholderTag(true);
+ saLabel.setVisible(isSecurityQuestionEnabled);
+ add(saLabel);
+
+ TextField<String> securityAnswer =
+ new TextField<>("securityAnswer", new PropertyModel<>(SelfPasswordReset.this,
+ "securityAnswerValue"), String.class);
+ securityAnswer.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+ private static final long serialVersionUID = -1107858522700306810L;
+
+ @Override
+ protected void onUpdate(final AjaxRequestTarget target) {
+ // do nothing
+ }
+ });
+ securityAnswer.setRequired(isSecurityQuestionEnabled);
+ securityAnswer.setOutputMarkupPlaceholderTag(true);
+ securityAnswer.setVisible(isSecurityQuestionEnabled);
+ add(securityAnswer);
+
+ add(captcha);
+ }
+
+ protected void loadSecurityQuestion(final PageReference pageRef, final AjaxRequestTarget target) {
+ try {
+ SecurityQuestionTO securityQuestionTO = SyncopeEnduserSession.get().getService(
+ SecurityQuestionService.class).readByUser(usernameValue);
+ // set security question field model
+ securityQuestion.setModel(Model.of(securityQuestionTO.getContent()));
+ target.add(securityQuestion);
+ } catch (Exception e) {
+ LOG.error("Unable to get security question for [{}]", usernameValue, e);
+ SyncopeEnduserSession.get().onException(e);
+ ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+ }
+ }
}
}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfRegistration.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfRegistration.java
new file mode 100644
index 0000000..ec85d59
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfRegistration.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.pages;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.syncope.client.enduser.SyncopeWebApplication;
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.client.enduser.layout.UserFormLayoutInfo;
+import org.apache.syncope.client.enduser.panels.UserSelfFormPanel;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.rest.api.service.SyncopeService;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+
+public class SelfRegistration extends BasePage {
+
+ private static final long serialVersionUID = -1100228004207271270L;
+
+ private static final String SELF_REGISTRATION = "page.selfRegistration";
+
+ public static final String NEW_USER_PARAM = "newUser";
+
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ public SelfRegistration(final PageParameters parameters) {
+ super(parameters, SELF_REGISTRATION);
+
+ setDomain(parameters);
+ disableSidebar();
+
+ WebMarkupContainer content = new WebMarkupContainer("content");
+ content.setOutputMarkupId(true);
+ contentWrapper.add(content);
+
+ UserSelfFormPanel selfRegistrationPanel = new UserSelfFormPanel(
+ "selfRegistrationPanel",
+ buildNewUserTO(parameters),
+ buildNewUserTO(parameters),
+ SyncopeEnduserSession.get().getService(SyncopeService.class).platform().getUserClasses(),
+ buildFormLayout(),
+ getPageReference());
+ selfRegistrationPanel.setOutputMarkupId(true);
+ content.add(selfRegistrationPanel);
+ }
+
+ private UserFormLayoutInfo buildFormLayout() {
+ UserFormLayoutInfo customlayoutInfo = SyncopeWebApplication.get().getCustomFormLayout();
+ return customlayoutInfo != null ? customlayoutInfo : new UserFormLayoutInfo();
+ }
+
+ private static UserTO buildNewUserTO(final PageParameters parameters) {
+ UserTO userTO = null;
+ if (parameters != null) {
+ if (!parameters.get(NEW_USER_PARAM).isNull()) {
+ try {
+ userTO = MAPPER.readValue(parameters.get(NEW_USER_PARAM).toString(), UserTO.class);
+ } catch (JsonProcessingException e) {
+ LOG.error("While reading user data from social registration", e);
+ }
+ }
+ }
+ if (userTO == null) {
+ userTO = new UserTO();
+ }
+ userTO.setRealm(SyncopeConstants.ROOT_REALM);
+ return userTO;
+ }
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfResult.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfResult.java
new file mode 100644
index 0000000..4d415c4
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfResult.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.pages;
+
+import org.apache.syncope.client.enduser.BookmarkablePageLinkBuilder;
+import org.apache.syncope.client.enduser.commons.EnduserConstants;
+import org.apache.syncope.client.ui.commons.Constants;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+
+public class SelfResult extends BasePage {
+
+ private static final long serialVersionUID = 3804053409052140145L;
+
+ private static final String RESULT_PAGE = "page.resultPage";
+
+ @SuppressWarnings("unchecked")
+ public SelfResult(final PageParameters parameters) {
+ super(parameters, RESULT_PAGE);
+
+ WebMarkupContainer content = new WebMarkupContainer("content");
+ content.setOutputMarkupId(true);
+ contentWrapper.add(content);
+ Class<? extends WebPage> page;
+ try {
+ page = (Class<? extends WebPage>) Class.forName(parameters.get(EnduserConstants.LANDING_PAGE).
+ toString("org.apache.syncope.client.enduser.pages.Login"));
+ } catch (ClassNotFoundException e) {
+ LOG.debug("Login page not found", e);
+ page = Login.class;
+ }
+ if (page.equals(Login.class)) {
+ BookmarkablePageLink<WebPage> login =
+ new BookmarkablePageLink<>("login", Login.class);
+ content.add(login.setOutputMarkupId(true));
+ disableSidebar();
+ } else {
+ content.add(BookmarkablePageLinkBuilder.build("login", page));
+ }
+
+ content.add(new Label("resultTitle", parameters.get(Constants.NOTIFICATION_TITLE_PARAM).toString()));
+ content.add(new Label("resultMessage", parameters.get(Constants.NOTIFICATION_MSG_PARAM).toString()));
+ content.add(new Fragment("statusIcon",
+ Constants.OPERATION_SUCCEEDED.equals(parameters.get(EnduserConstants.STATUS).toString())
+ ? "successIcon" : "errorIcon", content));
+ }
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/AbstractAnyFormPanel.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/AbstractAnyFormPanel.java
new file mode 100644
index 0000000..18c461a
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/AbstractAnyFormPanel.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.panels;
+
+import java.io.Serializable;
+import org.apache.syncope.client.enduser.pages.BasePage;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.markup.html.form.Button;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.model.CompoundPropertyModel;
+
+public abstract class AbstractAnyFormPanel<T extends Serializable> extends AbstractFormPanel<T> {
+
+ private static final long serialVersionUID = -5976166731584959275L;
+
+ protected final Form<T> form;
+
+ public AbstractAnyFormPanel(final String id, final T defaultItem, final PageReference pageReference) {
+ super(id, defaultItem, pageReference);
+
+ form = new Form<>("form");
+ form.setOutputMarkupId(true);
+ add(form);
+ AjaxButton submitButton = new AjaxButton("submit") {
+
+ private static final long serialVersionUID = 4284361595033427185L;
+
+ @Override
+ protected void onSubmit(final AjaxRequestTarget target) {
+ onFormSubmit(target);
+ }
+
+ @Override
+ protected void onError(final AjaxRequestTarget target) {
+ ((BasePage) getPage()).getNotificationPanel().refresh(target);
+ }
+ };
+
+ submitButton.setOutputMarkupId(true);
+ submitButton.setDefaultFormProcessing(true);
+ form.add(submitButton);
+
+ Button cancel = new Button("cancel") {
+
+ private static final long serialVersionUID = 3669569969172391336L;
+
+ @Override
+ public void onSubmit() {
+ setResponsePage(getApplication().getHomePage());
+ }
+
+ };
+ cancel.setOutputMarkupId(true);
+ cancel.setDefaultFormProcessing(false);
+ form.add(cancel);
+ }
+
+ public Form<T> getForm() {
+ return form;
+ }
+
+ public void setFormModel(final T modelObject) {
+ form.setModel(new CompoundPropertyModel<>(modelObject));
+ }
+
+ protected void onCancelInternal(final T modelObject) {
+ }
+
+ protected Serializable onApplyInternal(final T modelObject) {
+ // do nothing
+ return null;
+ }
+
+ protected abstract void buildLayout(T modelObject);
+
+ protected abstract void onFormSubmit(AjaxRequestTarget target);
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/AbstractFormPanel.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/AbstractFormPanel.java
new file mode 100644
index 0000000..312ea27
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/AbstractFormPanel.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.panels;
+
+import java.io.Serializable;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractFormPanel<T extends Serializable> extends Panel {
+
+ private static final long serialVersionUID = 6650311507433421554L;
+
+ protected static final Logger LOG = LoggerFactory.getLogger(AbstractFormPanel.class);
+
+ protected final PageReference pageRef;
+
+ protected final T defaultItem;
+
+ protected T item;
+
+ public AbstractFormPanel(final String id, final T defaultItem, final PageReference pageReference) {
+ super(id);
+ this.defaultItem = defaultItem;
+ this.pageRef = pageReference;
+ }
+
+ protected T getOriginalItem() {
+ return item;
+ }
+
+ protected T newModelObject() {
+ if (item == null) {
+ // keep the original item: the which one before the changes performed during wizard browsing
+ item = SerializationUtils.clone(defaultItem);
+ }
+
+ // instantiate a new model object and return it
+ return SerializationUtils.clone(item);
+ }
+
+ public PageReference getPageReference() {
+ return pageRef;
+ }
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/AnyFormPanel.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/AnyFormPanel.java
new file mode 100644
index 0000000..ed9e79b
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/AnyFormPanel.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.panels;
+
+import org.apache.syncope.client.enduser.SyncopeWebApplication;
+import org.apache.syncope.client.enduser.layout.UserFormLayoutInfo;
+import org.apache.syncope.client.enduser.panels.captcha.CaptchaPanel;
+import org.apache.syncope.client.ui.commons.panels.CardPanel;
+import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
+import org.apache.syncope.common.lib.Attr;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.GroupableRelatableTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.wicket.PageReference;
+import java.util.List;
+import org.apache.syncope.client.enduser.commons.EnduserConstants;
+import org.apache.syncope.client.enduser.panels.any.DerAttrs;
+import org.apache.syncope.client.enduser.panels.any.Details;
+import org.apache.syncope.client.enduser.panels.any.Groups;
+import org.apache.syncope.client.enduser.panels.any.PlainAttrs;
+import org.apache.syncope.client.enduser.panels.any.Resources;
+import org.apache.syncope.client.enduser.panels.any.VirAttrs;
+
+public abstract class AnyFormPanel extends AbstractAnyFormPanel<UserWrapper> {
+
+ private static final long serialVersionUID = -2720486919461006370L;
+
+ protected final List<String> anyTypeClasses;
+
+ protected CaptchaPanel<Void> captcha;
+
+ protected UserFormLayoutInfo formLayoutInfo;
+
+ public AnyFormPanel(final String id,
+ final UserTO anyTO,
+ final List<String> anyTypeClasses,
+ final UserFormLayoutInfo formLayoutInfo,
+ final PageReference pageReference) {
+
+ super(id, new UserWrapper(anyTO), pageReference);
+
+ this.formLayoutInfo = formLayoutInfo;
+ this.anyTypeClasses = anyTypeClasses;
+ }
+
+ @SuppressWarnings("unchecked")
+ public AnyFormPanel(final String id,
+ final UserWrapper wrapper,
+ final List<String> anyTypeClasses,
+ final UserFormLayoutInfo formLayoutInfo,
+ final PageReference pageReference) {
+
+ super(id, wrapper, pageReference);
+
+ this.formLayoutInfo = formLayoutInfo;
+ this.anyTypeClasses = anyTypeClasses;
+ }
+
+ protected Details<UserTO> addOptionalDetailsPanel(final UserWrapper modelObject) {
+ Details<UserTO> details = new Details<>(EnduserConstants.CONTENT_PANEL, modelObject, false, true, pageRef);
+ details.setOutputMarkupId(true);
+ return details;
+ }
+
+ @Override
+ protected void buildLayout(final UserWrapper modelObject) {
+ form.add(new CardPanel.Builder<>()
+ .setName("details")
+ .setComponent(addOptionalDetailsPanel(modelObject))
+ .isVisible(formLayoutInfo.isDetailsManagement()).build("userDetailsPanelCard"));
+
+ Groups groups = new Groups(EnduserConstants.CONTENT_PANEL, modelObject, false);
+ setOutputMarkupId(true);
+
+ form.add(new CardPanel.Builder<Groups>()
+ .setName("groups")
+ .setComponent(groups)
+ .isVisible(formLayoutInfo.isGroups()).build("groupsPanelCard"));
+
+ PlainAttrs plainAttrs = new PlainAttrs(EnduserConstants.CONTENT_PANEL,
+ modelObject, anyTypeClasses, formLayoutInfo.getWhichPlainAttrs());
+ plainAttrs.setOutputMarkupId(true);
+
+ form.add(new CardPanel.Builder<PlainAttrs>()
+ .setName("attributes.plain")
+ .setComponent(plainAttrs)
+ .isVisible(formLayoutInfo.isPlainAttrs() && plainAttrs.isPanelVisible()).build("plainAttrsPanelCard"));
+
+ DerAttrs derAttrs = new DerAttrs(EnduserConstants.CONTENT_PANEL,
+ modelObject, anyTypeClasses, formLayoutInfo.getWhichDerAttrs());
+ derAttrs.setOutputMarkupId(true);
+
+ form.add(new CardPanel.Builder<DerAttrs>()
+ .setName("attributes.derived")
+ .setComponent(derAttrs)
+ .isVisible(formLayoutInfo.isVirAttrs() && derAttrs.isPanelVisible()).build("derAttrsPanelCard"));
+
+ VirAttrs virAttrs = new VirAttrs(EnduserConstants.CONTENT_PANEL,
+ modelObject, anyTypeClasses, formLayoutInfo.getWhichVirAttrs());
+ virAttrs.setOutputMarkupId(true);
+
+ form.add(new CardPanel.Builder<VirAttrs>()
+ .setName("attributes.virtual")
+ .setComponent(virAttrs)
+ .isVisible(formLayoutInfo.isVirAttrs() && virAttrs.isPanelVisible()).build("virAttrsPanelCard"));
+
+ Resources resources = new Resources(EnduserConstants.CONTENT_PANEL, modelObject);
+ resources.setOutputMarkupId(true);
+
+ form.add(new CardPanel.Builder<Resources>()
+ .setName("resources")
+ .setComponent(resources)
+ .isVisible(formLayoutInfo.isResources()).build("resourcesPanelCard"));
+
+ // add captcha
+ captcha = new CaptchaPanel<>(EnduserConstants.CONTENT_PANEL);
+ captcha.setOutputMarkupPlaceholderTag(true);
+
+ form.add(new CardPanel.Builder<CaptchaPanel<Void>>()
+ .setName("captcha")
+ .setComponent(captcha)
+ .isVisible(SyncopeWebApplication.get().isCaptchaEnabled()).build("captchaPanelCard"));
+ }
+
+ protected void fixPlainAndVirAttrs(final AnyTO updated, final AnyTO original) {
+ // re-add to the updated object any missing plain or virtual attribute (compared to original): this to cope with
+ // form layout, which might have not included some plain or virtual attributes
+ for (Attr plainAttr : original.getPlainAttrs()) {
+ if (!updated.getPlainAttr(plainAttr.getSchema()).isPresent()) {
+ updated.getPlainAttrs().add(plainAttr);
+ }
+ }
+ for (Attr virAttr : original.getVirAttrs()) {
+ if (!updated.getVirAttr(virAttr.getSchema()).isPresent()) {
+ updated.getVirAttrs().add(virAttr);
+ }
+ }
+
+ if (updated instanceof GroupableRelatableTO && original instanceof GroupableRelatableTO) {
+ GroupableRelatableTO.class
+ .cast(original).getMemberships().forEach(oMemb -> {
+ GroupableRelatableTO.class
+ .cast(updated).getMembership(oMemb.getGroupKey()).ifPresent(uMemb -> {
+ oMemb.getPlainAttrs()
+ .stream().
+ filter(attr -> !uMemb.getPlainAttr(attr.getSchema()).isPresent()).
+ forEach(attr -> uMemb.getPlainAttrs().add(attr));
+ oMemb.getVirAttrs()
+ .stream().
+ filter(attr -> !uMemb.getVirAttr(attr.getSchema()).isPresent()).
+ forEach(attr -> uMemb.getVirAttrs().add(attr));
+ }
+ );
+ });
+ }
+
+ // remove from the updated object any plain or virtual attribute without values, thus triggering for removal in
+ // the generated patch
+ updated.getPlainAttrs().removeIf(attr -> attr.getValues().isEmpty());
+ updated.getVirAttrs().removeIf(attr -> attr.getValues().isEmpty());
+ if (updated instanceof GroupableRelatableTO) {
+ GroupableRelatableTO.class.cast(updated).getMemberships().forEach(memb -> {
+ memb.getPlainAttrs().removeIf(attr -> attr.getValues().isEmpty());
+ memb.getVirAttrs().removeIf(attr -> attr.getValues().isEmpty());
+ });
+ }
+ }
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/ChangePasswordPanel.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/ChangePasswordPanel.java
new file mode 100644
index 0000000..c5e7fc1
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/ChangePasswordPanel.java
@@ -0,0 +1,199 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.panels;
+
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.password.strength.PasswordStrengthBehavior;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.password.strength.PasswordStrengthConfig;
+import org.apache.syncope.client.enduser.SyncopeWebApplication;
+import org.apache.syncope.client.enduser.commons.EnduserConstants;
+import org.apache.syncope.client.enduser.panels.captcha.CaptchaPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.AbstractFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPasswordFieldPanel;
+import org.apache.syncope.client.ui.commons.panels.CardPanel;
+import org.apache.syncope.client.ui.commons.panels.NotificationPanel;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.wicket.AttributeModifier;
+import org.apache.wicket.Component;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.core.util.string.CssUtils;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Button;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.PasswordTextField;
+import org.apache.wicket.markup.html.form.StatelessForm;
+import org.apache.wicket.markup.html.form.validation.EqualPasswordInputValidator;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.util.string.AppendingStringBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class ChangePasswordPanel extends Panel {
+
+ protected static final Logger LOG = LoggerFactory.getLogger(ChangePasswordPanel.class);
+
+ private static final long serialVersionUID = -8937593602426944714L;
+
+ protected static final String FORM_SUFFIX = "form_";
+
+ protected StatelessForm<Void> form;
+
+ protected AjaxPasswordFieldPanel passwordField;
+
+ protected AjaxPasswordFieldPanel confirmPasswordField;
+
+ protected CaptchaPanel<Void> captcha;
+
+ public ChangePasswordPanel(final String id, final NotificationPanel notificationPanel) {
+ super(id);
+ form = new StatelessForm<Void>("changePassword") {
+
+ private static final long serialVersionUID = 418292023846536149L;
+
+ @Override
+ protected void appendDefaultButtonField() {
+ AppendingStringBuffer buffer = new AppendingStringBuffer();
+
+ String cssClass = getString(CssUtils.key(Form.class, "hidden-fields"));
+
+ // div that is not visible (but not display:none either)
+ buffer.append(String.format(
+ "<div style=\"width:0px;height:0px;position:absolute;"
+ + "left:-100px;top:-100px;overflow:hidden\" class=\"%s\">",
+ cssClass));
+
+ // add an empty textfield (otherwise IE doesn't work)
+ buffer.append("<input title=\"text_hidden\" "
+ + "aria-label=\"text_hidden\" type=\"text\" "
+ + "tabindex=\"-1\" autocomplete=\"off\"/>");
+
+ // add the submitting component
+ final Component submittingComponent = (Component) getDefaultButton();
+ buffer.append("<input title=\"submit_hidden\" aria-label=\"submit_hidden\" "
+ + "type=\"submit\" tabindex=\"-1\" name=\"");
+ buffer.append(getDefaultButton().getInputName());
+ buffer.append("\" onclick=\" var b=document.getElementById('");
+ buffer.append(submittingComponent.getMarkupId());
+ buffer.append(
+ "'); if (b!=null&&b.onclick!=null&&typeof(b.onclick) != 'undefined') "
+ + "{ var r = Wicket.bind(b.onclick, b)(); if (r != false) b.click(); } "
+ + "else { b.click(); }; return false;\" ");
+ buffer.append(" />");
+
+ // close div
+ buffer.append("</div>");
+
+ getResponse().write(buffer);
+ }
+ };
+ form.setOutputMarkupId(true);
+ add(form);
+
+ passwordField = new AjaxPasswordFieldPanel(
+ "password",
+ getString("password"),
+ new Model<>(),
+ false,
+ new PasswordStrengthBehavior(
+ new PasswordStrengthConfig()
+ .withDebug(true)
+ .withShowVerdictsInsideProgressBar(true)
+ .withShowProgressBar(true)));
+ passwordField.setRequired(true);
+ passwordField.setMarkupId("password");
+ passwordField.setPlaceholder("password");
+
+ Label passwordLabel = (Label) passwordField.get(AbstractFieldPanel.LABEL);
+ passwordLabel.add(new AttributeModifier("for", FORM_SUFFIX + "password"));
+
+ ((PasswordTextField) passwordField.getField()).setResetPassword(true);
+ form.add(passwordField);
+
+ confirmPasswordField = new AjaxPasswordFieldPanel("confirmPassword",
+ getString("confirmPassword"), new Model<>());
+ confirmPasswordField.setRequired(true);
+ confirmPasswordField.setMarkupId("confirmPassword");
+ confirmPasswordField.setPlaceholder("confirmPassword");
+
+ Label confirmPasswordLabel = (Label) confirmPasswordField.get(AbstractFieldPanel.LABEL);
+ confirmPasswordLabel.add(new AttributeModifier("for", FORM_SUFFIX + "confirmPassword"));
+
+ ((PasswordTextField) confirmPasswordField.getField()).setResetPassword(true);
+ form.add(confirmPasswordField);
+
+ form.add(new EqualPasswordInputValidator(passwordField.getField(), confirmPasswordField.getField()));
+
+ captcha = new CaptchaPanel<>(EnduserConstants.CONTENT_PANEL);
+ captcha.setOutputMarkupPlaceholderTag(true);
+
+ form.add(new CardPanel.Builder<CaptchaPanel<Void>>()
+ .setName("captcha")
+ .setComponent(captcha)
+ .isVisible(SyncopeWebApplication.get().isCaptchaEnabled()).build("captchaPanelCard"));
+
+ AjaxButton submitButton = new AjaxButton("submit", new Model<>(getString("submit"))) {
+
+ private static final long serialVersionUID = 429178684321093953L;
+
+ @Override
+ protected void onSubmit(final AjaxRequestTarget target) {
+ doSubmit(target, passwordField);
+ }
+
+ @Override
+ protected void onError(final AjaxRequestTarget target) {
+ notificationPanel.refresh(target);
+ }
+ };
+ form.add(submitButton);
+ form.setDefaultButton(submitButton);
+
+ Button cancel = new Button("cancel") {
+
+ private static final long serialVersionUID = 3669569969172391336L;
+
+ @Override
+ public void onSubmit() {
+ doCancel();
+ }
+ };
+ cancel.setOutputMarkupId(true);
+ cancel.setDefaultFormProcessing(false);
+ form.add(cancel);
+ }
+
+ public StatelessForm<Void> getForm() {
+ return form;
+ }
+
+ public AjaxPasswordFieldPanel getPasswordField() {
+ return passwordField;
+ }
+
+ public AjaxPasswordFieldPanel getConfirmPasswordField() {
+ return confirmPasswordField;
+ }
+
+ protected abstract void doSubmit(AjaxRequestTarget target, AjaxPasswordFieldPanel passwordField);
+
+ protected abstract void doCancel();
+
+ protected abstract UserTO getLoggedUser();
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/SelfPwdResetPanel.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/SelfPwdResetPanel.java
deleted file mode 100644
index 595ff0c..0000000
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/SelfPwdResetPanel.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.enduser.panels;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-import org.apache.syncope.client.enduser.SyncopeEnduserSession;
-import org.apache.syncope.client.enduser.SyncopeWebApplication;
-import org.apache.syncope.client.enduser.pages.BaseEnduserWebPage;
-import org.apache.syncope.client.enduser.rest.UserSelfRestClient;
-import org.apache.syncope.client.enduser.wizards.any.CaptchaPanel;
-import org.apache.syncope.client.ui.commons.Constants;
-import org.apache.syncope.client.ui.commons.DomainDropDown;
-import org.apache.syncope.common.keymaster.client.api.DomainOps;
-import org.apache.syncope.common.keymaster.client.api.model.Domain;
-import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.SyncopeConstants;
-import org.apache.syncope.common.lib.to.SecurityQuestionTO;
-import org.apache.syncope.common.rest.api.service.SecurityQuestionService;
-import org.apache.wicket.PageReference;
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
-import org.apache.wicket.ajax.markup.html.AjaxLink;
-import org.apache.wicket.ajax.markup.html.form.AjaxButton;
-import org.apache.wicket.event.IEventSource;
-import org.apache.wicket.markup.html.form.Button;
-import org.apache.wicket.markup.html.form.TextField;
-import org.apache.wicket.markup.html.panel.Panel;
-import org.apache.wicket.model.LoadableDetachableModel;
-import org.apache.wicket.model.Model;
-import org.apache.wicket.model.PropertyModel;
-import org.apache.wicket.request.mapper.parameter.PageParameters;
-import org.apache.wicket.spring.injection.annot.SpringBean;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class SelfPwdResetPanel extends Panel implements IEventSource {
-
- private static final long serialVersionUID = -2841210052053545578L;
-
- private static final Logger LOG = LoggerFactory.getLogger(SelfPwdResetPanel.class);
-
- @SpringBean
- private DomainOps domainOps;
-
- private final LoadableDetachableModel<List<String>> domains = new LoadableDetachableModel<List<String>>() {
-
- private static final long serialVersionUID = 4659376149825914247L;
-
- @Override
- protected List<String> load() {
- List<String> current = new ArrayList<>();
- current.addAll(domainOps.list().stream().map(Domain::getKey).sorted().collect(Collectors.toList()));
- current.add(0, SyncopeConstants.MASTER_DOMAIN);
- return current;
- }
- };
-
- private String usernameText;
-
- private String securityAnswerText;
-
- private final TextField<String> securityQuestion;
-
- private final CaptchaPanel<Void> captcha;
-
- public SelfPwdResetPanel(final String id, final PageReference pageRef) {
- super(id);
-
- DomainDropDown domainSelect = new DomainDropDown("domain", domains);
- domainSelect.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_BLUR) {
-
- private static final long serialVersionUID = -1107858522700306810L;
-
- @Override
- protected void onUpdate(final AjaxRequestTarget target) {
- // nothing to do
- }
- }).add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
-
- private static final long serialVersionUID = -1107858522700306810L;
-
- @Override
- protected void onUpdate(final AjaxRequestTarget target) {
- // nothing to do
- }
- });
- add(domainSelect);
-
- TextField<String> username =
- new TextField<>("username", new PropertyModel<>(this, "usernameText"), String.class);
- username.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_BLUR) {
-
- private static final long serialVersionUID = -1107858522700306810L;
-
- @Override
- protected void onUpdate(final AjaxRequestTarget target) {
- loadSecurityQuestion(pageRef, target);
- }
- });
- username.setRequired(true);
- add(username);
-
- securityQuestion =
- new TextField<>("securityQuestion", new PropertyModel<>(Model.of(), "content"), String.class);
- securityQuestion.setOutputMarkupId(true);
- securityQuestion.setEnabled(false);
- add(securityQuestion);
-
- AjaxLink<Void> reloadLink = new AjaxLink<>("reloadLink") {
-
- private static final long serialVersionUID = -817438685948164787L;
-
- @Override
- public void onClick(final AjaxRequestTarget target) {
- loadSecurityQuestion(pageRef, target);
- }
- };
- add(reloadLink);
-
- TextField<String> securityAnswer =
- new TextField<>("securityAnswer", new PropertyModel<>(this, "securityAnswerText"), String.class);
- securityAnswer.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
-
- private static final long serialVersionUID = -1107858522700306810L;
-
- @Override
- protected void onUpdate(final AjaxRequestTarget target) {
- // do nothing
- }
- });
- securityAnswer.setRequired(true);
- add(securityAnswer);
-
- captcha = new CaptchaPanel<>("captchaPanel");
- captcha.setOutputMarkupPlaceholderTag(true);
- captcha.setVisible(SyncopeWebApplication.get().isCaptchaEnabled());
- add(captcha);
-
- AjaxButton submitButton = new AjaxButton("submit") {
-
- private static final long serialVersionUID = 4284361595033427185L;
-
- @Override
- protected void onSubmit(final AjaxRequestTarget target) {
- boolean checked = true;
- if (SyncopeWebApplication.get().isCaptchaEnabled()) {
- checked = captcha.captchaCheck();
- }
- if (!checked) {
- SyncopeEnduserSession.get().error(getString(Constants.CAPTCHA_ERROR));
- ((BaseEnduserWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
- } else {
- try {
- UserSelfRestClient.requestPasswordReset(usernameText, securityAnswerText);
- PageParameters parameters = new PageParameters();
- parameters.add(Constants.NOTIFICATION_MSG_PARAM, getString("self.pwd.reset.success"));
- setResponsePage(getApplication().getHomePage(), parameters);
- } catch (SyncopeClientException sce) {
- LOG.error("Unable to reset password of [{}]", usernameText, sce);
- SyncopeEnduserSession.get().onException(sce);
- ((BaseEnduserWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
- }
- }
- }
-
- };
- submitButton.setOutputMarkupId(true);
- submitButton.setDefaultFormProcessing(false);
- add(submitButton);
-
- Button cancel = new Button("cancel") {
-
- private static final long serialVersionUID = 3669569969172391336L;
-
- @Override
- public void onSubmit() {
- setResponsePage(getApplication().getHomePage());
- }
-
- };
- cancel.setOutputMarkupId(true);
- cancel.setDefaultFormProcessing(false);
- add(cancel);
- }
-
- protected void loadSecurityQuestion(final PageReference pageRef, final AjaxRequestTarget target) {
- try {
- SecurityQuestionTO securityQuestionTO = SyncopeEnduserSession.get().getService(
- SecurityQuestionService.class).readByUser(usernameText);
- // set security question field model
- securityQuestion.setModel(Model.of(securityQuestionTO.getContent()));
- target.add(securityQuestion);
- } catch (Exception e) {
- LOG.error("Unable to get security question for [{}]", usernameText, e);
- SyncopeEnduserSession.get().onException(e);
- ((BaseEnduserWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
- }
- }
-}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/Sidebar.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/Sidebar.java
new file mode 100644
index 0000000..bd31754
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/Sidebar.java
@@ -0,0 +1,200 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.panels;
+
+import java.util.List;
+import java.util.stream.StreamSupport;
+import org.apache.syncope.client.enduser.BookmarkablePageLinkBuilder;
+import org.apache.syncope.client.enduser.SyncopeWebApplication;
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.client.enduser.pages.BasePage;
+import org.apache.syncope.client.enduser.pages.Dashboard;
+import org.apache.syncope.client.enduser.pages.EditChangePassword;
+import org.apache.syncope.client.enduser.pages.EditSecurityQuestion;
+import org.apache.syncope.client.enduser.pages.EditUser;
+import org.apache.syncope.client.ui.commons.annotations.ExtPage;
+import org.apache.wicket.AttributeModifier;
+import org.apache.wicket.Component;
+import org.apache.wicket.Page;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.behavior.Behavior;
+import org.apache.wicket.markup.ComponentTag;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.link.Link;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.markup.html.panel.Panel;
+
+public class Sidebar extends Panel {
+
+ private static final long serialVersionUID = 8091307811313529503L;
+
+ protected WebMarkupContainer dashboardLIContainer;
+
+ protected WebMarkupContainer profileULContainer;
+
+ protected WebMarkupContainer profileLIContainer;
+
+ public Sidebar(
+ final String id,
+ final PageReference pageRef,
+ final List<Class<? extends BasePage>> extPageClasses) {
+
+ super(id);
+
+ buildBaseSidebar();
+
+ // set 'active' menu item for everything but extensions
+ // 1. check if current class is set to top-level menu
+ WebMarkupContainer containingLI = null;
+ if (dashboardLIContainer.getId().equals(
+ getLIContainerId(pageRef.getPage().getClass().getSimpleName().toLowerCase()))) {
+
+ containingLI = dashboardLIContainer;
+ }
+ // 2. if not, check if it is under 'Configuration'
+ if (containingLI == null) {
+ containingLI = (WebMarkupContainer) profileULContainer.get(
+ getLIContainerId(pageRef.getPage().getClass().getSimpleName().toLowerCase()));
+ }
+ // 3. when found, set CSS coordinates for menu
+ if (containingLI != null) {
+ StreamSupport.stream(containingLI.spliterator(), false).filter(Link.class::isInstance).
+ forEach(child -> child.add(new Behavior() {
+
+ private static final long serialVersionUID = -5775607340182293596L;
+
+ @Override
+ public void onComponentTag(final Component component, final ComponentTag tag) {
+ tag.append("class", "active", " ");
+ }
+ }));
+
+ if (profileULContainer.getId().equals(containingLI.getParent().getId())) {
+ profileULContainer.add(new Behavior() {
+
+ private static final long serialVersionUID = 3109256773218160485L;
+
+ @Override
+ public void renderHead(final Component component, final IHeaderResponse response) {
+ response.render(OnDomReadyHeaderItem.forScript(
+ "$('#profileLink').addClass('active')"));
+ }
+
+ @Override
+ public void onComponentTag(final Component component, final ComponentTag tag) {
+ tag.put("class", "nav nav-treeview");
+ tag.put("style", "display: block;");
+ }
+ });
+
+ profileLIContainer.add(new Behavior() {
+
+ private static final long serialVersionUID = 3109256773218160485L;
+
+ @Override
+ public void onComponentTag(final Component component, final ComponentTag tag) {
+ tag.put("class", "nav-item has-treeview menu-open");
+ }
+ });
+ }
+ }
+
+ ListView<Class<? extends BasePage>> extPages =
+ new ListView<Class<? extends BasePage>>("extPages", extPageClasses) {
+
+ private static final long serialVersionUID = 4949588177564901031L;
+
+ @Override
+ protected void populateItem(final ListItem<Class<? extends BasePage>> item) {
+ WebMarkupContainer containingLI = new WebMarkupContainer("extPageLI");
+ item.add(containingLI);
+
+ ExtPage ann = item.getModelObject().getAnnotation(ExtPage.class);
+
+ BookmarkablePageLink<Page> link = new BookmarkablePageLink<>("extPage", item.getModelObject());
+
+ link.add(new Label("extPageLabel", ann.label()));
+
+ if (item.getModelObject().equals(pageRef.getPage().getClass())) {
+ link.add(new Behavior() {
+
+ private static final long serialVersionUID = 1469628524240283489L;
+
+ @Override
+ public void renderHead(final Component component, final IHeaderResponse response) {
+ response.render(OnDomReadyHeaderItem.forScript(
+ "$('#extensionsLink').addClass('active')"));
+ }
+
+ @Override
+ public void onComponentTag(final Component component, final ComponentTag tag) {
+ tag.append("class", "active", " ");
+ }
+ });
+ }
+ containingLI.add(link);
+
+ Label extPageIcon = new Label("extPageIcon");
+ extPageIcon.add(new AttributeModifier("class", "nav-icon " + ann.icon()));
+ link.add(extPageIcon);
+ }
+ };
+
+ add(extPages.setRenderBodyOnly(true).setOutputMarkupId(true));
+ }
+
+ protected void buildBaseSidebar() {
+ dashboardLIContainer = new WebMarkupContainer(getLIContainerId("dashboard"));
+ add(dashboardLIContainer);
+ dashboardLIContainer.add(BookmarkablePageLinkBuilder.build(
+ "home", SyncopeWebApplication.get().getPageClass("profile", Dashboard.class)));
+
+ profileLIContainer = new WebMarkupContainer(getLIContainerId("profile"));
+ add(profileLIContainer);
+ profileULContainer = new WebMarkupContainer(getULContainerId("profile"));
+ profileLIContainer.add(profileULContainer);
+
+ WebMarkupContainer liContainer = new WebMarkupContainer(getLIContainerId("edituser"));
+ profileULContainer.add(liContainer);
+ liContainer.add(BookmarkablePageLinkBuilder.build("edituser", EditUser.class));
+
+ liContainer = new WebMarkupContainer(getLIContainerId("editchangepassword"));
+ profileULContainer.add(liContainer);
+ liContainer.add(BookmarkablePageLinkBuilder.build("editchangepassword", EditChangePassword.class));
+
+ liContainer = new WebMarkupContainer(getLIContainerId("editsecurityquestion"));
+ profileULContainer.add(liContainer);
+ liContainer.add(BookmarkablePageLinkBuilder.build("editsecurityquestion", EditSecurityQuestion.class));
+ liContainer.setOutputMarkupPlaceholderTag(true);
+ liContainer.setVisible(SyncopeEnduserSession.get().getPlatformInfo().isPwdResetRequiringSecurityQuestions());
+ }
+
+ protected String getLIContainerId(final String linkId) {
+ return linkId + "LI";
+ }
+
+ protected String getULContainerId(final String linkId) {
+ return linkId + "UL";
+ }
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/UserFormPanel.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/UserFormPanel.java
new file mode 100644
index 0000000..0a91fa7
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/UserFormPanel.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.panels;
+
+import java.util.List;
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.client.enduser.SyncopeWebApplication;
+import org.apache.syncope.client.enduser.commons.EnduserConstants;
+import org.apache.syncope.client.enduser.layout.UserFormLayoutInfo;
+import org.apache.syncope.client.enduser.pages.BasePage;
+import org.apache.syncope.client.enduser.pages.Dashboard;
+import org.apache.syncope.client.enduser.pages.SelfResult;
+import org.apache.syncope.client.enduser.panels.any.Details;
+import org.apache.syncope.client.enduser.panels.any.UserDetails;
+import org.apache.syncope.client.enduser.rest.UserSelfRestClient;
+import org.apache.syncope.client.ui.commons.Constants;
+import org.apache.syncope.client.ui.commons.layout.UserForm;
+import org.apache.syncope.client.ui.commons.panels.WizardModalPanel;
+import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
+import org.apache.syncope.client.ui.commons.wizards.ModalPanelBuilder;
+import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
+import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
+import org.apache.syncope.common.lib.AnyOperations;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.request.UserUR;
+import org.apache.syncope.common.lib.to.ProvisioningResult;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.event.IEventSink;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+
+public class UserFormPanel extends AnyFormPanel implements UserForm {
+
+ private static final long serialVersionUID = 6763365006334514387L;
+
+ private final UserSelfRestClient userSelfRestClient = new UserSelfRestClient();
+
+ public UserFormPanel(
+ final String id,
+ final UserTO userTO,
+ final List<String> anyTypeClasses,
+ final UserFormLayoutInfo formLayoutInfo,
+ final PageReference pageReference) {
+ super(id, new UserWrapper(userTO), anyTypeClasses, formLayoutInfo, pageReference);
+
+ UserWrapper modelObj = newModelObject();
+ buildLayout(modelObj);
+ }
+
+ public UserFormPanel(
+ final String id,
+ final UserTO previousUserTO,
+ final UserTO userTO,
+ final List<String> anyTypeClasses,
+ final UserFormLayoutInfo formLayoutInfo,
+ final PageReference pageReference) {
+ super(id, new UserWrapper(previousUserTO, userTO), anyTypeClasses, formLayoutInfo, pageReference);
+
+ UserWrapper modelObj = newModelObject();
+ setFormModel(modelObj);
+ buildLayout(modelObj);
+
+ }
+
+ @Override
+ protected Details<UserTO> addOptionalDetailsPanel(final UserWrapper modelObject) {
+ return new UserDetails(
+ EnduserConstants.CONTENT_PANEL,
+ UserWrapper.class.cast(modelObject),
+ false,
+ false,
+ pageRef);
+ }
+
+ @Override
+ protected void onFormSubmit(final AjaxRequestTarget target) {
+ // captcha check
+ boolean checked = true;
+ if (SyncopeWebApplication.get().isCaptchaEnabled()) {
+ checked = captcha.check();
+ }
+ if (!checked) {
+ SyncopeEnduserSession.get().error(getString(Constants.CAPTCHA_ERROR));
+ ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+ } else {
+ ProvisioningResult<UserTO> result;
+ PageParameters parameters = new PageParameters();
+ try {
+ AnyWrapper<UserTO> updatedWrapper = form.getModelObject();
+ UserTO userTO = updatedWrapper.getInnerObject();
+
+ fixPlainAndVirAttrs(userTO, getOriginalItem().getInnerObject());
+ UserUR req = AnyOperations.diff(userTO, getOriginalItem().getInnerObject(), false);
+
+ // update just if it is changed
+ if (req.isEmpty()) {
+ result = new ProvisioningResult<>();
+ result.setEntity(userTO);
+ } else {
+ result = userSelfRestClient.update(getOriginalItem().getInnerObject().getETagValue(), req);
+ LOG.debug("User {} has been modified", result.getEntity().getUsername());
+ }
+ parameters.add(EnduserConstants.STATUS, Constants.OPERATION_SUCCEEDED);
+ parameters.add(Constants.NOTIFICATION_TITLE_PARAM, getString("self.profile.change.success"));
+ parameters.add(Constants.NOTIFICATION_MSG_PARAM, getString("self.profile.change.success.msg"));
+ } catch (SyncopeClientException sce) {
+ parameters.add(EnduserConstants.STATUS, Constants.ERROR);
+ parameters.add(Constants.NOTIFICATION_TITLE_PARAM, getString("self.profile.change.error"));
+ parameters.add(Constants.NOTIFICATION_MSG_PARAM, getString("self.profile.change.error.msg"));
+ SyncopeEnduserSession.get().onException(sce);
+ ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+ }
+ parameters.add(
+ EnduserConstants.LANDING_PAGE,
+ SyncopeWebApplication.get().getPageClass("profile", Dashboard.class).getName());
+ setResponsePage(SelfResult.class, parameters);
+ }
+ }
+
+ @Override
+ public IEventSink getEventSink() {
+ return null;
+ }
+
+ @Override
+ public ModalPanelBuilder<AnyWrapper<UserTO>> setEventSink(final IEventSink eventSink) {
+ return null;
+ }
+
+ @Override
+ public ModalPanelBuilder<AnyWrapper<UserTO>> setItem(final AnyWrapper<UserTO> item) {
+ return null;
+ }
+
+ @Override
+ public AnyWrapper<UserTO> getDefaultItem() {
+ return null;
+ }
+
+ @Override
+ public WizardModalPanel<AnyWrapper<UserTO>> build(final String id, final int index, final AjaxWizard.Mode mode) {
+ return null;
+ }
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/UserSelfFormPanel.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/UserSelfFormPanel.java
new file mode 100644
index 0000000..a3a610d
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/UserSelfFormPanel.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.panels;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.enduser.SyncopeWebApplication;
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.client.enduser.layout.UserFormLayoutInfo;
+import org.apache.syncope.client.enduser.pages.BasePage;
+import org.apache.syncope.client.enduser.pages.Login;
+import org.apache.syncope.client.enduser.pages.SelfResult;
+import org.apache.syncope.client.enduser.panels.any.Details;
+import org.apache.syncope.client.enduser.panels.any.SelfUserDetails;
+import org.apache.syncope.client.enduser.rest.UserSelfRestClient;
+import org.apache.syncope.client.ui.commons.Constants;
+import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
+import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
+import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
+import org.apache.syncope.common.lib.EntityTOUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.request.UserCR;
+import org.apache.syncope.common.lib.to.ProvisioningResult;
+import org.apache.syncope.common.lib.to.SecurityQuestionTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.rest.api.service.SecurityQuestionService;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import java.util.List;
+import org.apache.syncope.client.enduser.commons.EnduserConstants;
+
+public class UserSelfFormPanel extends UserFormPanel {
+
+ private static final long serialVersionUID = 6763365006334514387L;
+
+ private final UserSelfRestClient userSelfRestClient = new UserSelfRestClient();
+
+ private TextField<String> securityQuestion;
+
+ private String usernameText;
+
+ public UserSelfFormPanel(
+ final String id,
+ final UserTO previousUserTO,
+ final UserTO userTO,
+ final List<String> anyTypeClasses,
+ final UserFormLayoutInfo formLayoutInfo,
+ final PageReference pageReference) {
+ super(id, previousUserTO, userTO, anyTypeClasses, formLayoutInfo, pageReference);
+ }
+
+ @Override
+ protected Details<UserTO> addOptionalDetailsPanel(final UserWrapper modelObject) {
+ return new SelfUserDetails(
+ EnduserConstants.CONTENT_PANEL,
+ UserWrapper.class.cast(modelObject),
+ false,
+ false,
+ UserFormLayoutInfo.class.cast(formLayoutInfo).isPasswordManagement(),
+ pageRef);
+ }
+
+ @Override
+ protected void onFormSubmit(final AjaxRequestTarget target) {
+ // captcha check
+ boolean checked = true;
+ if (SyncopeWebApplication.get().isCaptchaEnabled()) {
+ checked = captcha.check();
+ }
+ if (!checked) {
+ SyncopeEnduserSession.get().error(getString(Constants.CAPTCHA_ERROR));
+ ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+ } else {
+ ProvisioningResult<UserTO> result;
+ PageParameters parameters = new PageParameters();
+ try {
+ AnyWrapper<UserTO> updatedWarapper = form.getModelObject();
+ UserTO userTO = updatedWarapper.getInnerObject();
+
+ UserCR req = new UserCR();
+ EntityTOUtils.toAnyCR(userTO, req);
+ req.setStorePassword(updatedWarapper instanceof UserWrapper
+ ? UserWrapper.class.cast(updatedWarapper).isStorePasswordInSyncope()
+ : StringUtils.isNotBlank(userTO.getPassword()));
+
+ result = userSelfRestClient.create(req, true);
+ LOG.debug("User {} has been created", result.getEntity().getUsername());
+
+ parameters.add(EnduserConstants.STATUS, Constants.OPERATION_SUCCEEDED);
+ parameters.add(Constants.NOTIFICATION_TITLE_PARAM, getString("self.profile.change.success"));
+ parameters.add(Constants.NOTIFICATION_MSG_PARAM, getString("self.profile.change.success.msg"));
+ } catch (SyncopeClientException sce) {
+ parameters.add(EnduserConstants.STATUS, Constants.ERROR);
+ parameters.add(Constants.NOTIFICATION_TITLE_PARAM, getString("self.profile.change.error"));
+ parameters.add(Constants.NOTIFICATION_MSG_PARAM, getString("self.profile.change.error.msg"));
+ SyncopeEnduserSession.get().onException(sce);
+ ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+ }
+ parameters.add(EnduserConstants.LANDING_PAGE, Login.class);
+ setResponsePage(SelfResult.class, parameters);
+ }
+ }
+
+ protected void loadSecurityQuestion(final PageReference pageRef, final AjaxRequestTarget target) {
+ try {
+ SecurityQuestionTO securityQuestionTO = SyncopeEnduserSession.get().getService(
+ SecurityQuestionService.class).readByUser(usernameText);
+ // set security question field model
+ securityQuestion.setModel(Model.of(securityQuestionTO.getContent()));
+ target.add(securityQuestion);
+ } catch (Exception e) {
+ LOG.error("Unable to get security question for [{}]", usernameText, e);
+ SyncopeEnduserSession.get().onException(e);
+ ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
+ }
+ }
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/AbstractAttrs.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/AbstractAttrs.java
similarity index 85%
rename from client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/AbstractAttrs.java
rename to client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/AbstractAttrs.java
index be2c63d..9f6e4ec 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/AbstractAttrs.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/AbstractAttrs.java
@@ -16,41 +16,41 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.client.enduser.wizards.any;
+package org.apache.syncope.client.enduser.panels.any;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.cxf.common.util.StringUtils;
import org.apache.syncope.client.enduser.layout.CustomizationOption;
import org.apache.syncope.client.enduser.rest.SchemaRestClient;
import org.apache.syncope.client.enduser.rest.SyncopeRestClient;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel;
import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
-import org.apache.syncope.common.lib.to.SchemaTO;
-import org.apache.syncope.common.lib.to.AnyTO;
import org.apache.syncope.common.lib.Attr;
+import org.apache.syncope.common.lib.to.AnyTO;
import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.SchemaTO;
import org.apache.syncope.common.lib.types.SchemaType;
-import org.apache.wicket.PageReference;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.core.util.lang.PropertyResolver;
-import org.apache.wicket.extensions.wizard.WizardModel.ICondition;
-import org.apache.wicket.extensions.wizard.WizardStep;
-import org.apache.wicket.markup.head.IHeaderResponse;
-import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.event.IEvent;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.util.ListModel;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
-public abstract class AbstractAttrs<S extends SchemaTO> extends WizardStep implements ICondition {
+public abstract class AbstractAttrs<S extends SchemaTO> extends Panel {
private static final long serialVersionUID = -5387344116983102292L;
+ protected static final String FORM_SUFFIX = "form_";
+
protected final Comparator<Attr> attrComparator = new AttrComparator();
protected final AnyTO anyTO;
@@ -68,18 +68,21 @@ public abstract class AbstractAttrs<S extends SchemaTO> extends WizardStep imple
private final List<String> anyTypeClasses;
public AbstractAttrs(
+ final String id,
final AnyWrapper<?> modelObject,
final List<String> anyTypeClasses,
final Map<String, CustomizationOption> whichAttrs) {
- super();
+ super(id);
this.anyTypeClasses = anyTypeClasses;
- this.attrs = new ListModel<>(List.of());
- this.membershipTOs = new ListModel<>(List.of());
+ this.attrs = new ListModel<>(Collections.emptyList());
+ this.membershipTOs = new ListModel<>(Collections.emptyList());
this.setOutputMarkupId(true);
this.anyTO = modelObject.getInnerObject();
this.whichAttrs = whichAttrs;
+
+ evaluate();
}
private List<Attr> loadAttrs() {
@@ -99,9 +102,8 @@ public abstract class AbstractAttrs<S extends SchemaTO> extends WizardStep imple
for (MembershipTO membership : (List<MembershipTO>) PropertyResolver.getPropertyField(
"memberships", anyTO).get(anyTO)) {
- setSchemas(
- Pair.of(membership.getGroupKey(), membership.getGroupName()),
- getMembershipAuxClasses(membership));
+ setSchemas(Pair.of(membership.getGroupKey(), membership.getGroupName()), getMembershipAuxClasses(
+ membership, anyTO.getType()));
setAttrs(membership);
if (AbstractAttrs.this instanceof PlainAttrs && !membership.getPlainAttrs().isEmpty()) {
@@ -142,7 +144,7 @@ public abstract class AbstractAttrs<S extends SchemaTO> extends WizardStep imple
: groupName + '#')
+ schema;
return whichAttrs.get(schemaName) == null
- ? List.of()
+ ? Collections.emptyList()
: whichAttrs.get(schemaName).getDefaultValues();
}
@@ -185,15 +187,8 @@ public abstract class AbstractAttrs<S extends SchemaTO> extends WizardStep imple
allSchemas.forEach(schemaTO -> scs.put(schemaTO.getKey(), schemaTO));
}
- @Override
- public void renderHead(final IHeaderResponse response) {
- super.renderHead(response);
- if (org.apache.cxf.common.util.CollectionUtils.isEmpty(attrs.getObject())
- && org.apache.cxf.common.util.CollectionUtils.isEmpty(membershipTOs.getObject())) {
- response.render(OnDomReadyHeaderItem.forScript(
- String.format("$('#emptyPlaceholder').append(\"%s\"); $('#attributes').hide();",
- getString("attribute.empty.list"))));
- }
+ public boolean isPanelVisible() {
+ return !attrs.getObject().isEmpty() || !membershipTOs.getObject().isEmpty();
}
protected abstract void setAttrs();
@@ -204,27 +199,26 @@ public abstract class AbstractAttrs<S extends SchemaTO> extends WizardStep imple
protected abstract List<Attr> getAttrsFromTO(MembershipTO membershipTO);
- protected static List<String> getMembershipAuxClasses(final MembershipTO membershipTO) {
+ protected static List<String> getMembershipAuxClasses(final MembershipTO membershipTO, final String anyType) {
try {
return SyncopeRestClient.searchUserTypeExtensions(membershipTO.getGroupName());
} catch (Exception e) {
- return List.of();
+ return Collections.emptyList();
}
}
@Override
+ protected void onInitialize() {
+ evaluate();
+ super.onInitialize();
+ }
+
public boolean evaluate() {
this.attrs.setObject(loadAttrs());
this.membershipTOs.setObject(loadMembershipAttrs());
return !attrs.getObject().isEmpty() || !membershipTOs.getObject().isEmpty();
}
- public PageReference getPageReference() {
- // SYNCOPE-1213
- // default implementation does not require to pass page reference, override this method of want otherwise
- return null;
- }
-
private class AttrComparator implements Comparator<Attr>, Serializable {
private static final long serialVersionUID = -5105030477767941060L;
@@ -253,6 +247,16 @@ public abstract class AbstractAttrs<S extends SchemaTO> extends WizardStep imple
}
}
+ @Override
+ public void onEvent(final IEvent<?> event) {
+ super.onEvent(event);
+ if (event.getPayload() instanceof AjaxPalettePanel.UpdateActionEvent) {
+ evaluate();
+ AjaxPalettePanel.UpdateActionEvent updateEvent = (AjaxPalettePanel.UpdateActionEvent) event.getPayload();
+ updateEvent.getTarget().add(this);
+ }
+ }
+
public static class Schemas extends Panel {
private static final long serialVersionUID = -2447602429647965090L;
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/DerAttrs.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/DerAttrs.java
similarity index 91%
rename from client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/DerAttrs.java
rename to client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/DerAttrs.java
index 952b834..fea60ce 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/DerAttrs.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/DerAttrs.java
@@ -16,27 +16,28 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.client.enduser.wizards.any;
+package org.apache.syncope.client.enduser.panels.any;
-import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.client.enduser.SyncopeEnduserSession;
import org.apache.syncope.client.enduser.layout.CustomizationOption;
-import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
+import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
+import org.apache.syncope.common.lib.Attr;
import org.apache.syncope.common.lib.EntityTOUtils;
import org.apache.syncope.common.lib.to.AnyTO;
-import org.apache.syncope.common.lib.Attr;
import org.apache.syncope.common.lib.to.DerSchemaTO;
import org.apache.syncope.common.lib.to.GroupableRelatableTO;
import org.apache.syncope.common.lib.to.MembershipTO;
import org.apache.syncope.common.lib.types.SchemaType;
import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
+import org.apache.wicket.extensions.markup.html.tabs.ITab;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.html.WebMarkupContainer;
@@ -47,30 +48,21 @@ import org.apache.wicket.model.Model;
import org.apache.wicket.model.ResourceModel;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.model.util.ListModel;
+import java.util.stream.Collectors;
public class DerAttrs extends AbstractAttrs<DerSchemaTO> {
private static final long serialVersionUID = -5387344116983102292L;
public <T extends AnyTO> DerAttrs(
+ final String id,
final AnyWrapper<T> modelObject,
final List<String> anyTypeClasses,
final Map<String, CustomizationOption> whichDerAttrs) {
- super(modelObject, anyTypeClasses, whichDerAttrs);
- setTitleModel(new ResourceModel("attributes.derived"));
-
- add(new Accordion("derSchemas", List.of(new AbstractTab(
- new ResourceModel("attributes.accordion", "Derived Attributes")) {
-
- private static final long serialVersionUID = 1037272333056449378L;
-
- @Override
- public WebMarkupContainer getPanel(final String panelId) {
- return new DerAttrs.DerSchemas(panelId, schemas, attrs);
- }
- }), Model.of(0)).setOutputMarkupId(true));
+ super(id, modelObject, anyTypeClasses, whichDerAttrs);
+ add(new DerAttrs.DerSchemas("derSchemas", schemas, attrs).setOutputMarkupId(true));
add(new ListView<MembershipTO>("membershipsDerSchemas", membershipTOs) {
private static final long serialVersionUID = 6741044372185745296L;
@@ -78,7 +70,7 @@ public class DerAttrs extends AbstractAttrs<DerSchemaTO> {
@Override
protected void populateItem(final ListItem<MembershipTO> item) {
final MembershipTO membershipTO = item.getModelObject();
- item.add(new Accordion("membershipDerSchemas", List.of(new AbstractTab(
+ item.add(new Accordion("membershipDerSchemas", Collections.<ITab>singletonList(new AbstractTab(
new StringResourceModel(
"attributes.membership.accordion",
DerAttrs.this,
@@ -159,7 +151,7 @@ public class DerAttrs extends AbstractAttrs<DerSchemaTO> {
membershipTO.getDerAttrs().addAll(derAttrs);
}
- public static class DerSchemas extends Schemas {
+ public class DerSchemas extends Schemas {
private static final long serialVersionUID = -4730563859116024676L;
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/Resources.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/Details.java
similarity index 56%
rename from client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/Resources.java
rename to client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/Details.java
index c4e7857..23c9bdf 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/Resources.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/Details.java
@@ -16,25 +16,30 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.client.enduser.wizards.any;
+package org.apache.syncope.client.enduser.panels.any;
-import org.apache.syncope.client.enduser.SyncopeEnduserSession;
import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
-import org.apache.syncope.client.ui.commons.wizards.any.AbstractResources;
import org.apache.syncope.common.lib.to.AnyTO;
-import org.apache.syncope.common.rest.api.service.SyncopeService;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-public class Resources extends AbstractResources {
+public class Details<T extends AnyTO> extends Panel {
- private static final long serialVersionUID = 702900610508752856L;
+ private static final long serialVersionUID = -8995647450549098844L;
- public <T extends AnyTO> Resources(final AnyWrapper<T> modelObject) {
- super(modelObject);
- }
+ protected static final Logger LOG = LoggerFactory.getLogger(Details.class);
+
+ protected final PageReference pageRef;
- @Override
- public boolean evaluate() {
- available.setObject(SyncopeEnduserSession.get().getService(SyncopeService.class).platform().getResources());
- return !available.getObject().isEmpty();
+ public Details(
+ final String id,
+ final AnyWrapper<T> wrapper,
+ final boolean templateMode,
+ final boolean includeStatusPanel,
+ final PageReference pageRef) {
+ super(id);
+ this.pageRef = pageRef;
}
}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/Groups.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/Groups.java
similarity index 71%
rename from client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/Groups.java
rename to client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/Groups.java
index 7626f73..5b3912a 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/Groups.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/Groups.java
@@ -16,35 +16,79 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.client.enduser.wizards.any;
+package org.apache.syncope.client.enduser.panels.any;
-import java.util.List;
-import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.client.enduser.rest.GroupRestClient;
-import org.apache.syncope.client.lib.SyncopeClient;
-import org.apache.syncope.client.ui.commons.Constants;
import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel;
-import org.apache.syncope.client.ui.commons.wizards.any.AbstractGroups;
import org.apache.syncope.client.ui.commons.wizards.any.AbstractGroupsModel;
import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
-import org.apache.syncope.common.lib.to.AnyTO;
-import org.apache.syncope.common.lib.to.GroupTO;
-import org.apache.syncope.common.lib.to.GroupableRelatableTO;
-import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.event.Broadcast;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.IChoiceRenderer;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.util.ListModel;
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.apache.commons.collections4.ListUtils;
+import org.apache.syncope.client.ui.commons.ajax.markup.html.LabelInfo;
+import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.to.GroupableRelatableTO;
+import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.panel.Panel;
-public class Groups extends AbstractGroups {
+public class Groups extends Panel {
private static final long serialVersionUID = 552437609667518888L;
+ protected static final int MAX_GROUP_LIST_CARDINALITY = 30;
+
private final EnduserGroupsModel groupsModel;
- public <T extends AnyTO> Groups(final AnyWrapper<T> modelObject) {
- super(modelObject);
+ protected final AnyTO anyTO;
+
+ protected WebMarkupContainer dyngroupsContainer;
+
+ protected WebMarkupContainer dynrealmsContainer;
+
+ protected WebMarkupContainer groupsContainer;
+
+ public <T extends AnyTO> Groups(final String id,
+ final AnyWrapper<T> modelObject,
+ final boolean templateMode) {
+
+ super(id);
+ this.anyTO = modelObject.getInnerObject();
+
+ setOutputMarkupId(true);
+
+ groupsContainer = new WebMarkupContainer("groupsContainer");
+ groupsContainer.setOutputMarkupId(true);
+ groupsContainer.setOutputMarkupPlaceholderTag(true);
+ add(groupsContainer);
+
+ // ------------------
+ // insert changed label if needed
+ // ------------------
+ if (modelObject instanceof UserWrapper
+ && UserWrapper.class.cast(modelObject).getPreviousUserTO() != null
+ && !ListUtils.isEqualList(
+ UserWrapper.class.cast(modelObject).getInnerObject().getMemberships(),
+ UserWrapper.class.cast(modelObject).getPreviousUserTO().getMemberships())) {
+ groupsContainer.add(new LabelInfo("changed", StringUtils.EMPTY));
+ } else {
+ groupsContainer.add(new Label("changed", StringUtils.EMPTY));
+ }
+ // ------------------
+
this.groupsModel = new EnduserGroupsModel();
setOutputMarkupId(true);
@@ -54,7 +98,14 @@ public class Groups extends AbstractGroups {
addDynamicRealmsContainer();
}
- @Override
+ private Function<AjaxRequestTarget, Boolean> getEventFunction() {
+ return (Function<AjaxRequestTarget, Boolean> & Serializable) (target) -> {
+ send(Groups.this.getPage(), Broadcast.BREADTH,
+ new AjaxPalettePanel.UpdateActionEvent((UserTO) anyTO, target));
+ return true;
+ };
+ }
+
protected void addGroupsPanel() {
if (anyTO instanceof GroupTO) {
groupsContainer.add(new Label("groups").setVisible(false));
@@ -82,7 +133,7 @@ public class Groups extends AbstractGroups {
return choices.getObject().stream().
filter(object -> id.equalsIgnoreCase(object.getGroupName())).findAny().orElse(null);
}
- });
+ }).event(getEventFunction());
groupsContainer.add(builder.setAllowOrder(true).withFilter().build("groups",
new ListModel<MembershipTO>() {
@@ -104,8 +155,7 @@ public class Groups extends AbstractGroups {
? groupsModel.getObject()
: GroupRestClient.searchAssignableGroups(
anyTO.getRealm(),
- SyncopeClient.getGroupSearchConditionBuilder().
- isAssignable().and().is(Constants.NAME_FIELD_NAME).equalTo(filter).query(),
+ filter,
1, MAX_GROUP_LIST_CARDINALITY)).stream()
.map(input -> new MembershipTO.Builder(input.getKey())
.groupName(input.getName()).build()).collect(Collectors.toList());
@@ -115,11 +165,9 @@ public class Groups extends AbstractGroups {
}
}
- @Override
protected void addDynamicRealmsContainer() {
}
- @Override
protected void addDynamicGroupsContainer() {
}
@@ -167,7 +215,7 @@ public class Groups extends AbstractGroups {
@Override
public List<String> getDynMemberships() {
- return List.of();
+ return Collections.emptyList();
}
/**
@@ -193,4 +241,7 @@ public class Groups extends AbstractGroups {
}
}
}
+
+ public interface SerializableFunction extends Function<AjaxRequestTarget, Boolean>, Serializable {
+ }
}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/PlainAttrs.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/PlainAttrs.java
similarity index 75%
rename from client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/PlainAttrs.java
rename to client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/PlainAttrs.java
index 281ca92..a25c76d 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/PlainAttrs.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/PlainAttrs.java
@@ -16,113 +16,86 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.client.enduser.wizards.any;
+package org.apache.syncope.client.enduser.panels.any;
import java.util.ArrayList;
-import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
-import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.apache.syncope.client.enduser.layout.CustomizationOption;
-import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDateFieldPanel;
import org.apache.syncope.client.enduser.markup.html.form.BinaryFieldPanel;
import org.apache.syncope.client.enduser.markup.html.form.MultiFieldPanel;
-import org.apache.syncope.client.ui.commons.markup.html.form.EncryptedFieldPanel;
import org.apache.syncope.client.ui.commons.SchemaUtils;
+import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
+import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
+import org.apache.syncope.common.lib.Attr;
+import org.apache.syncope.common.lib.Attributable;
+import org.apache.syncope.common.lib.EntityTOUtils;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.wicket.AttributeModifier;
+import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
+import org.apache.wicket.extensions.markup.html.tabs.ITab;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.IChoiceRenderer;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.model.util.ListModel;
+import java.util.stream.Collectors;
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
import org.apache.syncope.client.ui.commons.markup.html.form.AbstractFieldPanel;
import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDateFieldPanel;
import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDateTimeFieldPanel;
import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
import org.apache.syncope.client.ui.commons.markup.html.form.AjaxSpinnerFieldPanel;
import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.EncryptedFieldPanel;
import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
-import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
-import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
-import org.apache.syncope.common.lib.EntityTOUtils;
-import org.apache.syncope.common.lib.SyncopeConstants;
-import org.apache.syncope.common.lib.to.AnyObjectTO;
import org.apache.syncope.common.lib.to.AnyTO;
-import org.apache.syncope.common.lib.Attr;
-import org.apache.syncope.common.lib.Attributable;
-import org.apache.syncope.common.lib.to.GroupTO;
import org.apache.syncope.common.lib.to.GroupableRelatableTO;
import org.apache.syncope.common.lib.to.MembershipTO;
import org.apache.syncope.common.lib.to.PlainSchemaTO;
-import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.syncope.common.lib.types.AttrSchemaType;
-import org.apache.syncope.common.lib.types.SchemaType;
-import org.apache.wicket.PageReference;
-import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
-import org.apache.wicket.markup.html.WebMarkupContainer;
-import org.apache.wicket.markup.html.form.IChoiceRenderer;
-import org.apache.wicket.markup.html.list.ListItem;
-import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
-import org.apache.wicket.model.ResourceModel;
import org.apache.wicket.model.StringResourceModel;
-import org.apache.wicket.model.util.ListModel;
public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
private static final long serialVersionUID = 552437609667518888L;
- protected final AjaxWizard.Mode mode;
-
protected final AnyTO previousObject;
protected String fileKey = "";
- public <T extends AnyTO> PlainAttrs(
- final AnyWrapper<T> modelObject,
- final AjaxWizard.Mode mode,
+ public PlainAttrs(
+ final String id,
+ final UserWrapper modelObject,
final List<String> anyTypeClasses,
final Map<String, CustomizationOption> whichPlainAttrs) throws IllegalArgumentException {
- super(modelObject, anyTypeClasses, whichPlainAttrs);
- this.mode = mode;
+ super(id, modelObject, anyTypeClasses, whichPlainAttrs);
- if (modelObject.getInnerObject() instanceof UserTO) {
- fileKey = UserTO.class.cast(modelObject.getInnerObject()).getUsername();
- } else if (modelObject.getInnerObject() instanceof GroupTO) {
- fileKey = GroupTO.class.cast(modelObject.getInnerObject()).getName();
- } else if (modelObject.getInnerObject() instanceof AnyObjectTO) {
- fileKey = AnyObjectTO.class.cast(modelObject.getInnerObject()).getName();
- }
+ fileKey = modelObject.getInnerObject().getUsername();
- if (modelObject instanceof UserWrapper) {
- previousObject = UserWrapper.class.cast(modelObject).getPreviousUserTO();
- } else {
- previousObject = null;
- }
-
- setTitleModel(new ResourceModel("attributes.plain"));
-
- add(new Accordion("plainSchemas", List.of(new AbstractTab(
- new ResourceModel("attributes.accordion", "Plain Attributes")) {
-
- private static final long serialVersionUID = 1037272333056449378L;
-
- @Override
- public WebMarkupContainer getPanel(final String panelId) {
- return new PlainSchemasOwn(panelId, schemas, attrs);
- }
- }), Model.of(0)).setOutputMarkupId(true));
+ previousObject = modelObject.getPreviousUserTO();
+ add(new PlainSchemasOwn("plainSchemas", schemas, attrs).setOutputMarkupId(true));
add(new ListView<MembershipTO>("membershipsPlainSchemas", membershipTOs) {
- private static final long serialVersionUID = 1749643897846L;
+ private static final long serialVersionUID = 6741044372185745296L;
@Override
protected void populateItem(final ListItem<MembershipTO> item) {
final MembershipTO membershipTO = item.getModelObject();
- item.add(new Accordion("membershipPlainSchemas", List.of(new AbstractTab(
+ item.add(new Accordion("membershipPlainSchemas", Collections.<ITab>singletonList(new AbstractTab(
new StringResourceModel(
"attributes.membership.accordion",
PlainAttrs.this,
@@ -158,11 +131,6 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
}
@Override
- protected boolean filterSchemas() {
- return super.filterSchemas() && mode != AjaxWizard.Mode.TEMPLATE;
- }
-
- @Override
protected List<Attr> getAttrsFromTO() {
return anyTO.getPlainAttrs().stream().sorted(attrComparator).collect(Collectors.toList());
}
@@ -174,11 +142,11 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
@Override
protected void setAttrs() {
- List<Attr> attrs = new ArrayList<>();
+ List<Attr> plainAttrs = new ArrayList<>();
Map<String, Attr> attrMap = EntityTOUtils.buildAttrMap(anyTO.getPlainAttrs());
- attrs.addAll(schemas.values().stream().map(schema -> {
+ plainAttrs.addAll(schemas.values().stream().map(schema -> {
Attr attrTO = new Attr();
attrTO.setSchema(schema.getKey());
if (attrMap.get(schema.getKey()) == null || attrMap.get(schema.getKey()).getValues().isEmpty()) {
@@ -190,7 +158,7 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
}).collect(Collectors.toList()));
anyTO.getPlainAttrs().clear();
- anyTO.getPlainAttrs().addAll(attrs);
+ anyTO.getPlainAttrs().addAll(plainAttrs);
}
@Override
@@ -205,17 +173,16 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
attrMap = new HashMap<>();
}
- plainAttrs.addAll(membershipSchemas.get(membershipTO.getGroupKey()).values().stream().
- map(schema -> {
- Attr attrTO = new Attr();
- attrTO.setSchema(schema.getKey());
- if (attrMap.get(schema.getKey()) == null || attrMap.get(schema.getKey()).getValues().isEmpty()) {
- attrTO.getValues().add(StringUtils.EMPTY);
- } else {
- attrTO.getValues().addAll(attrMap.get(schema.getKey()).getValues());
- }
- return attrTO;
- }).collect(Collectors.toList()));
+ plainAttrs.addAll(membershipSchemas.get(membershipTO.getGroupKey()).values().stream().map(schema -> {
+ Attr attr = new Attr();
+ attr.setSchema(schema.getKey());
+ if (attrMap.get(schema.getKey()) == null || attrMap.get(schema.getKey()).getValues().isEmpty()) {
+ attr.getValues().add(StringUtils.EMPTY);
+ } else {
+ attr.getValues().addAll(attrMap.get(schema.getKey()).getValues());
+ }
+ return attr;
+ }).collect(Collectors.toList()));
membershipTO.getPlainAttrs().clear();
membershipTO.getPlainAttrs().addAll(plainAttrs);
@@ -238,7 +205,7 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
case Boolean:
panel = new AjaxCheckBoxPanel(
"panel",
- schemaTO.getLabel(getLocale()),
+ schemaTO.getLabel(SyncopeEnduserSession.get().getLocale()),
new Model<>(),
true);
panel.setRequired(required);
@@ -252,13 +219,13 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
if (datePattern.contains("H")) {
panel = new AjaxDateTimeFieldPanel(
"panel",
- schemaTO.getLabel(getLocale()),
+ schemaTO.getLabel(SyncopeEnduserSession.get().getLocale()),
new Model<>(),
FastDateFormat.getInstance(datePattern));
} else {
panel = new AjaxDateFieldPanel(
"panel",
- schemaTO.getLabel(getLocale()),
+ schemaTO.getLabel(SyncopeEnduserSession.get().getLocale()),
new Model<>(),
FastDateFormat.getInstance(datePattern));
}
@@ -271,7 +238,7 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
case Enum:
panel = new AjaxDropDownChoicePanel<>("panel",
- schemaTO.getLabel(getLocale()), new Model<>(), true);
+ schemaTO.getLabel(SyncopeEnduserSession.get().getLocale()), new Model<>(), true);
((AjaxDropDownChoicePanel<String>) panel).setChoices(SchemaUtils.getEnumeratedValues(schemaTO));
if (StringUtils.isNotBlank(schemaTO.getEnumerationKeys())) {
@@ -307,7 +274,7 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
case Long:
panel = new AjaxSpinnerFieldPanel.Builder<Long>().enableOnChange().build(
"panel",
- schemaTO.getLabel(getLocale()),
+ schemaTO.getLabel(SyncopeEnduserSession.get().getLocale()),
Long.class,
new Model<>());
@@ -319,7 +286,7 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
case Double:
panel = new AjaxSpinnerFieldPanel.Builder<Double>().enableOnChange().step(0.1).build(
"panel",
- schemaTO.getLabel(getLocale()),
+ schemaTO.getLabel(SyncopeEnduserSession.get().getLocale()),
Double.class,
new Model<>());
@@ -329,22 +296,12 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
break;
case Binary:
- final PageReference pageRef = getPageReference();
panel = new BinaryFieldPanel(
"panel",
- schemaTO.getLabel(getLocale()),
+ schemaTO.getLabel(SyncopeEnduserSession.get().getLocale()),
new Model<>(),
schemaTO.getMimeType(),
- fileKey) {
-
- private static final long serialVersionUID = -3268213909514986831L;
-
- @Override
- protected PageReference getPageReference() {
- return pageRef;
- }
-
- };
+ fileKey);
if (required) {
panel.addRequiredLabel();
}
@@ -352,7 +309,7 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
case Encrypted:
panel = new EncryptedFieldPanel("panel",
- schemaTO.getLabel(getLocale()), new Model<>(), true);
+ schemaTO.getLabel(SyncopeEnduserSession.get().getLocale()), new Model<>(), true);
if (required) {
panel.addRequiredLabel();
@@ -361,7 +318,7 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
default:
panel = new AjaxTextFieldPanel("panel",
- schemaTO.getLabel(getLocale()), new Model<>(), true);
+ schemaTO.getLabel(SyncopeEnduserSession.get().getLocale()), new Model<>(), true);
if (jexlHelp) {
AjaxTextFieldPanel.class.cast(panel).enableJexlHelp();
@@ -375,6 +332,10 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
panel.setReadOnly(readOnly);
panel.setMarkupId(StringUtils.isBlank(groupName) ? schemaTO.getKey() : groupName + '.' + schemaTO.getKey());
+ Label label = (Label) panel.get(AbstractFieldPanel.LABEL);
+ label.add(new AttributeModifier("for", FORM_SUFFIX
+ + (StringUtils.isBlank(groupName) ? schemaTO.getKey() : groupName + '.' + schemaTO.getKey())));
+
return panel;
}
@@ -400,25 +361,25 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
protected void populateItem(final ListItem<Attr> item) {
- Attr attr = item.getModelObject();
- PlainSchemaTO schema = schemas.get(attr.getSchema());
+ Attr attrTO = item.getModelObject();
+ PlainSchemaTO schema = schemas.get(attrTO.getSchema());
// set default values, if any
- if (attr.getValues().stream().noneMatch(StringUtils::isNotBlank)) {
- attr.getValues().clear();
- attr.getValues().addAll(getDefaultValues(attr.getSchema(), groupName));
+ if (attrTO.getValues().stream().noneMatch(StringUtils::isNotBlank)) {
+ attrTO.getValues().clear();
+ attrTO.getValues().addAll(getDefaultValues(attrTO.getSchema(), groupName));
}
- AbstractFieldPanel<?> panel = getFieldPanel(schemas.get(attr.getSchema()));
- if (schemas.get(attr.getSchema()).isMultivalue()) {
+ AbstractFieldPanel<?> panel = getFieldPanel(schemas.get(attrTO.getSchema()));
+ if (schemas.get(attrTO.getSchema()).isMultivalue()) {
panel = new MultiFieldPanel.Builder<>(
new PropertyModel<>(
- attributableTO.getObject().getPlainAttr(attr.getSchema()), "values"))
- .build("panel", attr.getSchema(), FieldPanel.class.cast(panel));
+ attributableTO.getObject().getPlainAttr(attrTO.getSchema()), "values"))
+ .build("panel", attrTO.getSchema(), FieldPanel.class.cast(panel));
// SYNCOPE-1215 the entire multifield panel must be readonly, not only its field
((MultiFieldPanel) panel).setReadOnly(schema == null ? false : schema.isReadonly());
} else {
- FieldPanel.class.cast(panel).setNewModel(attr.getValues()).
+ FieldPanel.class.cast(panel).setNewModel(attrTO.getValues()).
setReadOnly(schema == null ? false : schema.isReadonly());
}
@@ -446,26 +407,26 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
protected void populateItem(final ListItem<Attr> item) {
- Attr attr = item.getModelObject();
- PlainSchemaTO schema = schemas.get(attr.getSchema());
+ Attr attrTO = item.getModelObject();
+ PlainSchemaTO schema = schemas.get(attrTO.getSchema());
// set default values, if any
- if (attr.getValues().stream().noneMatch(StringUtils::isNotBlank)) {
- attr.getValues().clear();
- attr.getValues().addAll(getDefaultValues(attr.getSchema()));
+ if (attrTO.getValues().stream().noneMatch(StringUtils::isNotBlank)) {
+ attrTO.getValues().clear();
+ attrTO.getValues().addAll(getDefaultValues(attrTO.getSchema()));
}
- AbstractFieldPanel<?> panel = getFieldPanel(schemas.get(attr.getSchema()));
- if (schemas.get(attr.getSchema()).isMultivalue()) {
+ AbstractFieldPanel<?> panel = getFieldPanel(schemas.get(attrTO.getSchema()));
+ if (schemas.get(attrTO.getSchema()).isMultivalue()) {
panel = new MultiFieldPanel.Builder<>(
- new PropertyModel<>(attr, "values")).build(
+ new PropertyModel<>(attrTO, "values")).build(
"panel",
- attr.getSchema(),
+ attrTO.getSchema(),
FieldPanel.class.cast(panel));
// SYNCOPE-1215 the entire multifield panel must be readonly, not only its field
((MultiFieldPanel) panel).setReadOnly(schema == null ? false : schema.isReadonly());
} else {
- FieldPanel.class.cast(panel).setNewModel(attr.getValues()).
+ FieldPanel.class.cast(panel).setNewModel(attrTO.getValues()).
setReadOnly(schema == null ? false : schema.isReadonly());
}
item.add(panel);
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/Resources.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/Resources.java
new file mode 100644
index 0000000..93265e8
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/Resources.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.panels.any;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.client.ui.commons.ajax.markup.html.LabelInfo;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel;
+import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
+import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.rest.api.service.SyncopeService;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.util.ListModel;
+
+public class Resources extends Panel {
+
+ private static final long serialVersionUID = 702900610508752856L;
+
+ protected final ListModel<String> available;
+
+ public <T extends AnyTO> Resources(final String id, final AnyWrapper<T> modelObject) {
+ super(id);
+ final T entityTO = modelObject.getInnerObject();
+
+ if (modelObject instanceof UserWrapper
+ && UserWrapper.class.cast(modelObject).getPreviousUserTO() != null
+ && !modelObject.getInnerObject().getResources().equals(
+ UserWrapper.class.cast(modelObject).getPreviousUserTO().getResources())) {
+
+ add(new LabelInfo("changed", StringUtils.EMPTY));
+ } else {
+ add(new Label("changed", StringUtils.EMPTY));
+ }
+
+ this.setOutputMarkupId(true);
+ this.available = new ListModel<>(List.of());
+
+ add(new AjaxPalettePanel.Builder<String>().build("resources",
+ new PropertyModel<List<String>>(entityTO, "resources") {
+
+ private static final long serialVersionUID = 3799387950428254072L;
+
+ @Override
+ public List<String> getObject() {
+ return new ArrayList<>(entityTO.getResources());
+ }
+
+ @Override
+ public void setObject(final List<String> object) {
+ entityTO.getResources().clear();
+ entityTO.getResources().addAll(object);
+ }
+ }, available).hideLabel().setOutputMarkupId(true));
+ }
+
+ @Override
+ protected void onInitialize() {
+ super.onInitialize();
+ available.setObject(SyncopeEnduserSession.get().getService(SyncopeService.class).platform().getResources());
+ }
+
+ public boolean evaluate() {
+ available.setObject(SyncopeEnduserSession.get().getService(SyncopeService.class).platform().getResources());
+ return !available.getObject().isEmpty();
+ }
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/SelfUserDetails.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/SelfUserDetails.java
new file mode 100644
index 0000000..3918934
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/SelfUserDetails.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.panels.any;
+
+import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
+import org.apache.wicket.PageReference;
+
+public class SelfUserDetails extends UserDetails {
+
+ private static final long serialVersionUID = 6592027822510220469L;
+
+ public SelfUserDetails(
+ final String id,
+ final UserWrapper wrapper,
+ final boolean templateMode,
+ final boolean includeStatusPanel,
+ final boolean showPasswordManagement,
+ final PageReference pageRef) {
+
+ super(id, wrapper, templateMode, includeStatusPanel, pageRef);
+
+ // ------------------------
+ // Password
+ // ------------------------
+ EditUserPasswordPanel panel = new EditUserPasswordPanel("password", wrapper, templateMode);
+ panel.setVisible(showPasswordManagement);
+
+ add(panel);
+ }
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/UserDetails.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/UserDetails.java
new file mode 100644
index 0000000..8897db7
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/UserDetails.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.panels.any;
+
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.password.strength.PasswordStrengthBehavior;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.password.strength.PasswordStrengthConfig;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.enduser.rest.RealmRestClient;
+import org.apache.syncope.client.ui.commons.ajax.markup.html.LabelInfo;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
+import org.apache.syncope.client.ui.commons.wizards.any.PasswordPanel;
+import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
+import org.apache.syncope.common.lib.to.RealmTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.ResourceModel;
+
+public class UserDetails extends Details<UserTO> {
+
+ private static final long serialVersionUID = 6592027822510220463L;
+
+ private final FieldPanel<String> realm;
+
+ protected final AjaxTextFieldPanel username;
+
+ protected final UserTO userTO;
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public UserDetails(
+ final String id,
+ final UserWrapper wrapper,
+ final boolean templateMode,
+ final boolean includeStatusPanel,
+ final PageReference pageRef) {
+
+ super(id, wrapper, templateMode, includeStatusPanel, pageRef);
+
+ userTO = wrapper.getInnerObject();
+ // ------------------------
+ // Username
+ // ------------------------
+ username = new AjaxTextFieldPanel("username", "username", new PropertyModel<>(userTO, "username"), false);
+
+ if (wrapper.getPreviousUserTO() != null && StringUtils.
+ compare(wrapper.getPreviousUserTO().getUsername(), wrapper.getInnerObject().getUsername()) != 0) {
+ username.showExternAction(new LabelInfo("externalAction", wrapper.getPreviousUserTO().getUsername()));
+ }
+
+ if (templateMode) {
+ username.enableJexlHelp();
+ } else {
+ username.addRequiredLabel();
+ }
+ add(username);
+ // ------------------------
+
+ // ------------------------
+ // Realm
+ // ------------------------
+ realm = new AjaxDropDownChoicePanel<>(
+ "destinationRealm", "destinationRealm", new PropertyModel<>(userTO, "realm"), false);
+
+ ((AjaxDropDownChoicePanel<String>) realm).setChoices(
+ RealmRestClient.list().stream().map(RealmTO::getFullPath).collect(Collectors.toList()));
+ add(realm);
+ }
+
+ protected static class EditUserPasswordPanel extends Panel {
+
+ private static final long serialVersionUID = -8198836979773590078L;
+
+ protected EditUserPasswordPanel(
+ final String id,
+ final UserWrapper wrapper,
+ final boolean templateMode) {
+
+ super(id);
+ setOutputMarkupId(true);
+ add(new Label("warning", new ResourceModel("password.change.warning")));
+ add(new PasswordPanel(
+ "passwordPanel",
+ wrapper,
+ templateMode,
+ wrapper.getInnerObject().getKey() == null,
+ new PasswordStrengthBehavior(new PasswordStrengthConfig().
+ withDebug(false).
+ withShowVerdictsInsideProgressBar(true).
+ withShowProgressBar(true))));
+ }
+ }
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/VirAttrs.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/VirAttrs.java
similarity index 82%
rename from client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/VirAttrs.java
rename to client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/VirAttrs.java
index 86c55b2..65386d9 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/VirAttrs.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/VirAttrs.java
@@ -16,86 +16,77 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.client.enduser.wizards.any;
+package org.apache.syncope.client.enduser.panels.any;
-import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.client.enduser.SyncopeEnduserSession;
import org.apache.syncope.client.enduser.layout.CustomizationOption;
import org.apache.syncope.client.enduser.markup.html.form.MultiFieldPanel;
-import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
import org.apache.syncope.client.ui.commons.markup.html.form.AbstractFieldPanel;
import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
+import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
+import org.apache.syncope.common.lib.Attr;
import org.apache.syncope.common.lib.EntityTOUtils;
import org.apache.syncope.common.lib.to.AnyTO;
-import org.apache.syncope.common.lib.Attr;
import org.apache.syncope.common.lib.to.GroupableRelatableTO;
import org.apache.syncope.common.lib.to.MembershipTO;
import org.apache.syncope.common.lib.to.VirSchemaTO;
import org.apache.syncope.common.lib.types.SchemaType;
import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
+import org.apache.wicket.extensions.markup.html.tabs.ITab;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
-import org.apache.wicket.model.ResourceModel;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.model.util.ListModel;
+import java.util.stream.Collectors;
public class VirAttrs extends AbstractAttrs<VirSchemaTO> {
private static final long serialVersionUID = -7982691107029848579L;
public <T extends AnyTO> VirAttrs(
+ final String id,
final AnyWrapper<T> modelObject,
final List<String> anyTypeClasses,
final Map<String, CustomizationOption> whichVirAttrs) {
- super(modelObject, anyTypeClasses, whichVirAttrs);
-
- setTitleModel(new ResourceModel("attributes.virtual"));
-
- add(new Accordion("virSchemas", List.of(new AbstractTab(
- new ResourceModel("attributes.accordion", "Virtual Attributes")) {
-
- private static final long serialVersionUID = 1037272333056449378L;
-
- @Override
- public WebMarkupContainer getPanel(final String panelId) {
- return new VirAttrs.VirSchemas(panelId, schemas, attrs);
- }
- }), Model.of(0)).setOutputMarkupId(true));
+ super(id, modelObject, anyTypeClasses, whichVirAttrs);
+ add(new VirAttrs.VirSchemas("virSchemas", schemas, attrs).setOutputMarkupId(true));
add(new ListView<MembershipTO>("membershipsVirSchemas", membershipTOs) {
private static final long serialVersionUID = 9101744072914090143L;
@Override
protected void populateItem(final ListItem<MembershipTO> item) {
- MembershipTO membTO = item.getModelObject();
- item.add(new Accordion("membershipVirSchemas", List.of(new AbstractTab(
- new StringResourceModel("attributes.membership.accordion", VirAttrs.this, Model.of(membTO))) {
-
- private static final long serialVersionUID = 1037272333056449378L;
-
- @Override
- public WebMarkupContainer getPanel(final String panelId) {
- return new VirAttrs.VirSchemas(
- panelId,
- membTO.getGroupName(),
- membershipSchemas.get(membTO.getGroupKey()),
- new ListModel<>(getAttrsFromTO(membTO)));
- }
- }), Model.of(-1)).setOutputMarkupId(true));
+ final MembershipTO membershipTO = item.getModelObject();
+ item.add(new Accordion("membershipVirSchemas",
+ Collections.<ITab>singletonList(new AbstractTab(new StringResourceModel(
+ "attributes.membership.accordion", VirAttrs.this, Model.of(membershipTO))) {
+
+ private static final long serialVersionUID = 1037272333056449378L;
+
+ @Override
+ public WebMarkupContainer getPanel(final String panelId) {
+ return new VirAttrs.VirSchemas(
+ panelId,
+ membershipTO.getGroupName(),
+ membershipSchemas.get(membershipTO.getGroupKey()),
+ new ListModel<>(getAttrsFromTO(membershipTO)));
+ }
+ }), Model.of(-1)).setOutputMarkupId(true));
}
- });
+ }).setOutputMarkupId(true);
}
@Override
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/CaptchaPanel.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/captcha/CaptchaPanel.java
similarity index 83%
rename from client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/CaptchaPanel.java
rename to client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/captcha/CaptchaPanel.java
index 4b886bf..007e26b 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/CaptchaPanel.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/captcha/CaptchaPanel.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.client.enduser.wizards.any;
+package org.apache.syncope.client.enduser.panels.captcha;
import java.security.SecureRandom;
import org.apache.commons.lang3.StringUtils;
@@ -31,7 +31,7 @@ import org.apache.wicket.model.Model;
public class CaptchaPanel<T> extends Panel {
- private static final long serialVersionUID = 1169850573252481471L;
+ private static final long serialVersionUID = -450657681453274465L;
private static final SecureRandom RANDOM = new SecureRandom();
@@ -40,8 +40,6 @@ public class CaptchaPanel<T> extends Panel {
withinRange('a', 'z').
build();
- private String randomText;
-
private final Model<String> captchaText = new Model<>();
private final CaptchaImageResource captchaImageResource;
@@ -55,8 +53,7 @@ public class CaptchaPanel<T> extends Panel {
@Override
protected byte[] render() {
- randomText = RANDOM_LETTERS.generate(6);
- getChallengeIdModel().setObject(randomText);
+ getChallengeIdModel().setObject(RANDOM_LETTERS.generate(6));
return super.render();
}
};
@@ -82,13 +79,13 @@ public class CaptchaPanel<T> extends Panel {
setOutputMarkupPlaceholderTag(true));
}
- public void reload() {
- this.captchaImageResource.invalidate();
- }
-
- public boolean captchaCheck() {
- return StringUtils.isBlank(captchaText.getObject()) || StringUtils.isBlank(randomText)
+ public boolean check() {
+ boolean check = StringUtils.isBlank(captchaText.getObject())
+ || StringUtils.isBlank(captchaImageResource.getChallengeId())
? false
- : captchaText.getObject().equals(randomText);
+ : captchaText.getObject().equals(captchaImageResource.getChallengeId());
+
+ captchaImageResource.invalidate();
+ return check;
}
}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/resources/CaptchaResource.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/resources/CaptchaResource.java
deleted file mode 100644
index 7cbb5f5..0000000
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/resources/CaptchaResource.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.enduser.resources;
-
-import java.security.SecureRandom;
-import javax.servlet.http.HttpServletRequest;
-import org.apache.commons.text.RandomStringGenerator;
-import org.apache.syncope.client.enduser.SyncopeEnduserConstants;
-import org.apache.wicket.extensions.markup.html.captcha.CaptchaImageResource;
-import org.apache.wicket.request.cycle.RequestCycle;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class CaptchaResource extends CaptchaImageResource {
-
- private static final long serialVersionUID = 8293404296348102926L;
-
- private static final SecureRandom RANDOM = new SecureRandom();
-
- private static final Logger LOG = LoggerFactory.getLogger(CaptchaResource.class);
-
- private static final RandomStringGenerator RANDOM_LETTERS = new RandomStringGenerator.Builder().
- usingRandom(RANDOM::nextInt).
- withinRange('a', 'z').
- build();
-
- @Override
- protected byte[] render() {
- LOG.debug("Generate captcha");
-
- String captcha = RANDOM_LETTERS.generate(6);
- HttpServletRequest request = ((HttpServletRequest) RequestCycle.get().getRequest().getContainerRequest());
- // store the captcha in the current session
- request.getSession().setAttribute(SyncopeEnduserConstants.CAPTCHA_SESSION_KEY, captcha);
-
- getChallengeIdModel().setObject(captcha);
- return super.render();
- }
-
-}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/AnyTypeRestClient.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/AnyTypeRestClient.java
index 8f3c823..09c6ea9 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/AnyTypeRestClient.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/AnyTypeRestClient.java
@@ -19,6 +19,7 @@
package org.apache.syncope.client.enduser.rest;
import java.io.Serializable;
+import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.apache.commons.lang3.ObjectUtils;
@@ -47,7 +48,7 @@ public class AnyTypeRestClient extends BaseRestClient {
}
public static List<AnyTypeTO> listAnyTypes() {
- List<AnyTypeTO> types = List.of();
+ List<AnyTypeTO> types = Collections.emptyList();
try {
types = getService(AnyTypeService.class).list();
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/BaseRestClient.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/BaseRestClient.java
index 6f9e4ff..97196c0 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/BaseRestClient.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/BaseRestClient.java
@@ -18,10 +18,17 @@
*/
package org.apache.syncope.client.enduser.rest;
+import java.net.URI;
+import javax.ws.rs.core.HttpHeaders;
+import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.syncope.client.enduser.SyncopeEnduserSession;
import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.client.ui.commons.Constants;
import org.apache.syncope.client.ui.commons.rest.RestClient;
import org.apache.syncope.common.lib.search.OrderByClauseBuilder;
+import org.apache.syncope.common.lib.types.ExecStatus;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.JAXRSService;
import org.apache.syncope.common.rest.api.service.SyncopeService;
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
import org.slf4j.Logger;
@@ -49,7 +56,7 @@ public abstract class BaseRestClient implements RestClient {
SyncopeEnduserSession.get().resetClient(serviceClass);
}
- protected static String toOrderBy(final SortParam<String> sort) {
+ public static String toOrderBy(final SortParam<String> sort) {
OrderByClauseBuilder builder = SyncopeClient.getOrderByClauseBuilder();
String property = sort.getProperty();
@@ -65,4 +72,22 @@ public abstract class BaseRestClient implements RestClient {
return builder.build();
}
+
+ protected static <E extends JAXRSService, T> T getObject(
+ final E service, final URI location, final Class<T> resultClass) {
+
+ WebClient webClient = WebClient.fromClient(WebClient.client(service));
+ webClient.accept(SyncopeEnduserSession.get().getMediaType()).to(location.toASCIIString(), false);
+ return webClient.
+ header(RESTHeaders.DOMAIN, SyncopeEnduserSession.get().getDomain()).
+ header(HttpHeaders.AUTHORIZATION, "Bearer " + SyncopeEnduserSession.get().getJWT()).
+ get(resultClass);
+ }
+
+ protected static String getStatus(final int httpStatus) {
+ ExecStatus execStatus = ExecStatus.fromHttpStatus(httpStatus);
+ return execStatus == null
+ ? Constants.UNKNOWN
+ : execStatus.name();
+ }
}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/GroupRestClient.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/GroupRestClient.java
index f9e22c3..23c98fd 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/GroupRestClient.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/GroupRestClient.java
@@ -18,9 +18,8 @@
*/
package org.apache.syncope.client.enduser.rest;
-import java.util.List;
-import javax.ws.rs.core.GenericType;
-import javax.ws.rs.core.Response;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.request.GroupCR;
import org.apache.syncope.common.lib.request.GroupUR;
import org.apache.syncope.common.lib.to.GroupTO;
@@ -29,6 +28,9 @@ import org.apache.syncope.common.rest.api.beans.AnyQuery;
import org.apache.syncope.common.rest.api.service.AnyService;
import org.apache.syncope.common.rest.api.service.GroupService;
import org.apache.syncope.common.rest.api.service.SyncopeService;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.Response;
+import java.util.List;
/**
* Console client for invoking Rest Group's services.
@@ -42,16 +44,16 @@ public class GroupRestClient extends AbstractAnyRestClient<GroupTO> {
return GroupService.class;
}
- public static ProvisioningResult<GroupTO> create(final GroupCR groupCR) {
- Response response = getService(GroupService.class).create(groupCR);
+ public static ProvisioningResult<GroupTO> create(final GroupCR groupTO) {
+ Response response = getService(GroupService.class).create(groupTO);
return response.readEntity(new GenericType<ProvisioningResult<GroupTO>>() {
});
}
- public ProvisioningResult<GroupTO> update(final String etag, final GroupUR updateReq) {
+ public ProvisioningResult<GroupTO> update(final String etag, final GroupUR groupPatch) {
ProvisioningResult<GroupTO> result;
synchronized (this) {
- result = getService(etag, GroupService.class).update(updateReq).
+ result = getService(etag, GroupService.class).update(groupPatch).
readEntity(new GenericType<ProvisioningResult<GroupTO>>() {
});
resetClient(getAnyServiceClass());
@@ -65,7 +67,8 @@ public class GroupRestClient extends AbstractAnyRestClient<GroupTO> {
final int page,
final int size) {
- return getService(SyncopeService.class).searchAssignableGroups(realm, term, page, size).getResult();
+ return getService(SyncopeService.class).searchAssignableGroups(
+ StringUtils.isNotEmpty(realm) ? realm : SyncopeConstants.ROOT_REALM, term, page, size).getResult();
}
@Override
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/RoleRestClient.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/RoleRestClient.java
deleted file mode 100644
index 6b3aea3..0000000
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/RoleRestClient.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.enduser.rest;
-
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.List;
-import java.util.stream.Collectors;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.to.RoleTO;
-import org.apache.syncope.common.rest.api.service.RoleService;
-
-/**
- * Console client for invoking Rest Role's services.
- */
-public class RoleRestClient extends BaseRestClient {
-
- private static final long serialVersionUID = -3161863874876938094L;
-
- public static void delete(final String key) {
- getService(RoleService.class).delete(key);
- }
-
- public static RoleTO read(final String key) {
- return getService(RoleService.class).read(key);
- }
-
- public static void update(final RoleTO roleTO) {
- getService(RoleService.class).update(roleTO);
- }
-
- public static void create(final RoleTO roleTO) {
- getService(RoleService.class).create(roleTO);
- }
-
- public static List<RoleTO> list() {
- return getService(RoleService.class).list();
- }
-
- public static String readAnyLayout(final String roleKey) {
- try {
- return IOUtils.toString(InputStream.class.cast(
- getService(RoleService.class).getAnyLayout(roleKey).getEntity()),
- StandardCharsets.UTF_8);
- } catch (Exception e) {
- LOG.error("Error retrieving console layout info for role {}", roleKey, e);
- return StringUtils.EMPTY;
- }
- }
-
- public static void setAnyLayout(final String roleKey, final String content) {
- getService(RoleService.class).setAnyLayout(
- roleKey, IOUtils.toInputStream(content, StandardCharsets.UTF_8));
- }
-
- public static void removeAnyLayout(final String roleKey) {
- getService(RoleService.class).removeAnyLayout(roleKey);
- }
-
- public static List<String> getAllAvailableEntitlements() {
- return getSyncopeService().platform().getEntitlements().stream().sorted().collect(Collectors.toList());
- }
-}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SchemaRestClient.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SchemaRestClient.java
index c89e35b..666b273 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SchemaRestClient.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SchemaRestClient.java
@@ -19,6 +19,7 @@
package org.apache.syncope.client.enduser.rest;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
@@ -86,7 +87,7 @@ public class SchemaRestClient extends BaseRestClient {
}
public static List<String> getSchemaNames(final SchemaType schemaType) {
- List<String> schemaNames = List.of();
+ List<String> schemaNames = Collections.emptyList();
try {
schemaNames = getSchemas(schemaType, null, new String[0]).stream().
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SyncopeRestClient.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SyncopeRestClient.java
index 74429c8..233dd51 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SyncopeRestClient.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SyncopeRestClient.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.client.enduser.rest;
+import java.util.Collections;
import java.util.List;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.to.TypeExtensionTO;
@@ -28,7 +29,7 @@ public class SyncopeRestClient extends BaseRestClient {
private static final long serialVersionUID = -2211371717449597247L;
public static List<String> listAnyTypeClasses() {
- List<String> types = List.of();
+ List<String> types = Collections.emptyList();
try {
types = getService(SyncopeService.class).platform().getAnyTypeClasses();
@@ -39,13 +40,14 @@ public class SyncopeRestClient extends BaseRestClient {
}
public static List<String> searchUserTypeExtensions(final String groupName) {
- List<String> types = List.of();
+ List<String> types = Collections.emptyList();
try {
TypeExtensionTO typeExtensionTO = getService(SyncopeService.class).readUserTypeExtension(groupName);
types = typeExtensionTO == null ? types : typeExtensionTO.getAuxClasses();
- } catch (SyncopeClientException e) {
+ } catch (Exception e) {
LOG.error("While reading all any type classes for group [{}]", groupName, e);
}
return types;
}
+
}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/UserSelfRestClient.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/UserSelfRestClient.java
index 95a35ce..f7cf9cf 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/UserSelfRestClient.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/UserSelfRestClient.java
@@ -18,51 +18,41 @@
*/
package org.apache.syncope.client.enduser.rest;
-import javax.ws.rs.core.GenericType;
-import javax.ws.rs.core.Response;
-import org.apache.syncope.common.lib.request.BooleanReplacePatchItem;
import org.apache.syncope.common.lib.request.UserCR;
import org.apache.syncope.common.lib.request.UserUR;
import org.apache.syncope.common.lib.to.ProvisioningResult;
import org.apache.syncope.common.lib.to.UserTO;
import org.apache.syncope.common.rest.api.service.UserSelfService;
+import javax.ws.rs.core.GenericType;
-/**
- * Console client for invoking rest users services.
- */
public class UserSelfRestClient extends BaseRestClient {
private static final long serialVersionUID = -1575748964398293968L;
- public static ProvisioningResult<UserTO> create(final UserCR createReq) {
- Response response = getService(UserSelfService.class).create(createReq);
- return response.readEntity(new GenericType<ProvisioningResult<UserTO>>() {
+ public static void changePassword(final String password) {
+ getService(UserSelfService.class).mustChangePassword(password);
+ }
+
+ public static void requestPasswordReset(final String username, final String securityAnswer) {
+ getService(UserSelfService.class).requestPasswordReset(username, securityAnswer);
+ }
+
+ public ProvisioningResult<UserTO> create(final UserCR createReq, final boolean storePassword) {
+ ProvisioningResult<UserTO> result;
+ result = getService(UserSelfService.class).create(createReq).readEntity(
+ new GenericType<ProvisioningResult<UserTO>>() {
});
+ return result;
}
- public ProvisioningResult<UserTO> update(final String etag, final UserUR updateReq) {
+ public ProvisioningResult<UserTO> update(final String etag, final UserUR userPatch) {
ProvisioningResult<UserTO> result;
synchronized (this) {
- result = getService(etag, UserSelfService.class).update(updateReq).
+ result = getService(etag, UserSelfService.class).update(userPatch).
readEntity(new GenericType<ProvisioningResult<UserTO>>() {
});
resetClient(UserSelfService.class);
}
return result;
}
-
- public ProvisioningResult<UserTO> mustChangePassword(final String etag, final boolean value, final String key) {
- UserUR userUR = new UserUR();
- userUR.setKey(key);
- userUR.setMustChangePassword(new BooleanReplacePatchItem.Builder().value(value).build());
- return update(etag, userUR);
- }
-
- public static void changePassword(final String password) {
- getService(UserSelfService.class).mustChangePassword(password);
- }
-
- public static void requestPasswordReset(final String username, final String securityAnswer) {
- getService(UserSelfService.class).requestPasswordReset(username, securityAnswer);
- }
}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/Captcha.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wicket/markup/head/MetaHeaderItem.java
similarity index 50%
rename from client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/Captcha.java
rename to client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wicket/markup/head/MetaHeaderItem.java
index c46755c..90ccf60 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/Captcha.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wicket/markup/head/MetaHeaderItem.java
@@ -16,34 +16,34 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.client.enduser.wizards.any;
+package org.apache.syncope.client.enduser.wicket.markup.head;
-import org.apache.syncope.client.enduser.SyncopeWebApplication;
-import org.apache.wicket.extensions.wizard.WizardModel;
-import org.apache.wicket.extensions.wizard.WizardStep;
+import java.io.Serializable;
+import java.util.Arrays;
+import org.apache.wicket.markup.head.HeaderItem;
+import org.apache.wicket.request.Response;
-public class Captcha extends WizardStep implements WizardModel.ICondition {
+public class MetaHeaderItem extends HeaderItem implements Serializable {
- private static final long serialVersionUID = 702900610508752856L;
+ private static final long serialVersionUID = 7578609827530302053L;
- private final CaptchaPanel<Void> captchaPanel;
+ private final String key;
- public Captcha() {
- captchaPanel = new CaptchaPanel<>("captchaPanel");
- captchaPanel.setOutputMarkupId(true);
- add(captchaPanel);
- }
+ private final String value;
- public boolean captchaCheck() {
- return captchaPanel.captchaCheck();
+ public MetaHeaderItem(final String key, final String value) {
+ this.key = key;
+ this.value = value;
}
- public void reload() {
- captchaPanel.reload();
+ @Override
+ public Iterable<?> getRenderTokens() {
+ return Arrays.asList("meta-" + key + "-" + value);
}
@Override
- public boolean evaluate() {
- return SyncopeWebApplication.get().isCaptchaEnabled();
+ public void render(final Response response) {
+ response.write("<meta http-equiv=\"" + key + "\" content=\"" + value + "\"/>");
+ response.write("\n");
}
}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfUpdate.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/widgets/BaseWidget.java
similarity index 71%
rename from client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfUpdate.java
rename to client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/widgets/BaseWidget.java
index 7c86c2d..8426ae5 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfUpdate.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/widgets/BaseWidget.java
@@ -16,16 +16,16 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.client.enduser.pages;
+package org.apache.syncope.client.enduser.widgets;
-import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.markup.html.panel.Panel;
-public class SelfUpdate extends BaseEnduserWebPage {
+public abstract class BaseWidget extends Panel {
- private static final long serialVersionUID = 164651008547631054L;
+ private static final long serialVersionUID = -4186604985011430091L;
- public SelfUpdate(final PageParameters parameters) {
- super(parameters);
+ public BaseWidget(final String id) {
+ super(id);
}
}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/widgets/UserProfileWidget.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/widgets/UserProfileWidget.java
new file mode 100644
index 0000000..9f52674
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/widgets/UserProfileWidget.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.widgets;
+
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+
+public class UserProfileWidget extends BaseWidget {
+
+ private static final long serialVersionUID = 4437711189800676363L;
+
+ protected UserTO userTO;
+
+ public UserProfileWidget(final String id) {
+ super(id);
+
+ userTO = SyncopeEnduserSession.get().getSelfTO(true);
+
+ WebMarkupContainer userProfile = new WebMarkupContainer("userProfile");
+ userProfile.setOutputMarkupId(true);
+ add(userProfile);
+
+ Label welcome = new Label("welcome", userTO.getUsername());
+ welcome.setOutputMarkupId(true);
+ userProfile.add(welcome);
+
+ addBaseFields(userProfile);
+ addExtFields(userProfile);
+ }
+
+ protected void addBaseFields(final WebMarkupContainer userProfile) {
+ Label username = new Label("username", userTO.getUsername());
+ username.setOutputMarkupId(true);
+ userProfile.add(username);
+
+ Label changePwdDate = new Label("changePwdDate", userTO.getChangePwdDate());
+ changePwdDate.setOutputMarkupId(true);
+ userProfile.add(changePwdDate);
+
+ Label lastLoginDate = new Label("lastLoginDate", userTO.getLastLoginDate());
+ lastLoginDate.setOutputMarkupId(true);
+ userProfile.add(lastLoginDate);
+ }
+
+ protected void addExtFields(final WebMarkupContainer userProfile) {
+ }
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/AnyWizardBuilder.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/AnyWizardBuilder.java
deleted file mode 100644
index 922554e..0000000
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/AnyWizardBuilder.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.enduser.wizards.any;
-
-import java.io.Serializable;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Future;
-import org.apache.commons.lang3.tuple.Pair;
-import org.apache.syncope.client.enduser.SyncopeWebApplication;
-import org.apache.syncope.client.enduser.SyncopeEnduserSession;
-import org.apache.syncope.client.enduser.layout.UserFormLayoutInfo;
-import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
-import org.apache.syncope.client.ui.commons.wizards.AjaxWizardMgtButtonBar;
-import org.apache.syncope.client.ui.commons.wizards.any.AbstractAnyWizardBuilder;
-import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
-import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
-import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.wicket.Component;
-import org.apache.wicket.PageReference;
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.extensions.wizard.FinishButton;
-import org.apache.wicket.extensions.wizard.IWizard;
-import org.apache.wicket.extensions.wizard.IWizardModel;
-import org.apache.wicket.extensions.wizard.IWizardStep;
-import org.apache.wicket.extensions.wizard.WizardModel;
-
-public abstract class AnyWizardBuilder extends AbstractAnyWizardBuilder<UserTO> {
-
- private static final long serialVersionUID = -2480279868319546243L;
-
- protected final List<String> anyTypeClasses;
-
- protected UserFormLayoutInfo formLayoutInfo;
-
- protected Captcha captcha;
-
- /**
- * Construct.
- *
- * @param anyTO any
- * @param anyTypeClasses any type classes
- * @param formLayoutInfo form layout info
- * @param pageRef caller page reference.
- */
- public AnyWizardBuilder(
- final UserTO anyTO,
- final List<String> anyTypeClasses,
- final UserFormLayoutInfo formLayoutInfo,
- final PageReference pageRef) {
-
- super(new AnyWrapper<>(anyTO), pageRef);
- this.anyTypeClasses = anyTypeClasses;
- this.formLayoutInfo = formLayoutInfo;
- }
-
- /**
- * Construct.
- *
- * @param wrapper any wrapper
- * @param anyTypeClasses any type classes
- * @param formLayoutInfo form layout info
- * @param pageRef caller page reference.
- */
- public AnyWizardBuilder(
- final UserWrapper wrapper,
- final List<String> anyTypeClasses,
- final UserFormLayoutInfo formLayoutInfo,
- final PageReference pageRef) {
-
- super(wrapper, pageRef);
- this.anyTypeClasses = anyTypeClasses;
- this.formLayoutInfo = formLayoutInfo;
- }
-
- @Override
- protected WizardModel buildModelSteps(final AnyWrapper<UserTO> modelObject, final WizardModel wizardModel) {
- wizardModel.add(new UserDetails(
- UserWrapper.class.cast(modelObject),
- mode == AjaxWizard.Mode.TEMPLATE,
- UserFormLayoutInfo.class.cast(formLayoutInfo).isPasswordManagement()));
-
- if (formLayoutInfo.isAuxClasses()) {
- wizardModel.add(new EnduserAuxClasses(modelObject, anyTypeClasses));
- }
-
- if (formLayoutInfo.isGroups()) {
- wizardModel.add(new Groups(modelObject));
- }
-
- // attributes panel steps
- if (formLayoutInfo.isPlainAttrs()) {
- wizardModel.add(new PlainAttrs(
- modelObject,
- mode,
- anyTypeClasses,
- formLayoutInfo.getWhichPlainAttrs()) {
-
- private static final long serialVersionUID = 8167894751609598306L;
-
- @Override
- public PageReference getPageReference() {
- return pageRef;
- }
-
- });
- }
- if (formLayoutInfo.isDerAttrs()) {
- wizardModel.add(new DerAttrs(modelObject, anyTypeClasses, formLayoutInfo.getWhichDerAttrs()));
- }
- if (formLayoutInfo.isVirAttrs()) {
- wizardModel.add(new VirAttrs(modelObject, anyTypeClasses, formLayoutInfo.getWhichVirAttrs()));
- }
- if (formLayoutInfo.isResources()) {
- wizardModel.add(new Resources(modelObject));
- }
- if (SyncopeWebApplication.get().isCaptchaEnabled()) {
- // add captcha
- captcha = new Captcha();
- captcha.setOutputMarkupId(true);
- wizardModel.add(captcha);
- }
-
- return wizardModel;
- }
-
- @Override
- protected long getMaxWaitTimeInSeconds() {
- return SyncopeWebApplication.get().getMaxWaitTimeInSeconds();
- }
-
- @Override
- protected void sendError(final Exception exception) {
- SyncopeEnduserSession.get().onException(exception);
- }
-
- @Override
- protected void sendWarning(final String message) {
- SyncopeEnduserSession.get().warn(message);
- }
-
- @Override
- protected Future<Pair<Serializable, Serializable>> execute(
- final Callable<Pair<Serializable, Serializable>> future) {
- return SyncopeEnduserSession.get().execute(future);
- }
-
- @Override
- public AjaxWizard<AnyWrapper<UserTO>> build(final String id, final AjaxWizard.Mode mode) {
- this.mode = mode;
-
- // get the specified item if available
- final AnyWrapper<UserTO> modelObject = newModelObject();
-
- return new AjaxWizard<AnyWrapper<UserTO>>(
- id, modelObject, buildModelSteps(modelObject, new WizardModel()), mode, this.pageRef) {
-
- private static final long serialVersionUID = 7770507663760640735L;
-
- @Override
- protected void onCancelInternal() {
- AnyWizardBuilder.this.onCancelInternal(modelObject);
- }
-
- @Override
- protected Pair<Serializable, Serializable> onApplyInternal(final AjaxRequestTarget target) {
- Serializable res = AnyWizardBuilder.this.onApplyInternal(modelObject);
-
- Serializable payload;
- switch (mode) {
- case CREATE:
- payload = getCreateCustomPayloadEvent(res, target);
- break;
- case EDIT:
- case TEMPLATE:
- payload = getEditCustomPayloadEvent(res, target);
- break;
- default:
- payload = null;
- }
-
- return Pair.of(payload, res);
- }
-
- @Override
- protected long getMaxWaitTimeInSeconds() {
- return AnyWizardBuilder.this.getMaxWaitTimeInSeconds();
- }
-
- @Override
- protected void sendError(final Exception exception) {
- SyncopeEnduserSession.get().onException(exception);
- }
-
- @Override
- protected void sendWarning(final String message) {
- AnyWizardBuilder.this.sendWarning(message);
- }
-
- @Override
- protected Future<Pair<Serializable, Serializable>> execute(
- final Callable<Pair<Serializable, Serializable>> future) {
- return AnyWizardBuilder.this.execute(future);
- }
-
- @Override
- protected Component newButtonBar(final String id) {
- return new AjaxWizardMgtButtonBar<>(id, this, mode) {
-
- private static final long serialVersionUID = -3041152400413815333L;
-
- @Override
- protected FinishButton newFinishButton(final String id, final IWizard wizard) {
- return new FinishButton(id, wizard) {
-
- private static final long serialVersionUID = 864248301720764819L;
-
- @Override
- public boolean isEnabled() {
- switch (mode) {
- case EDIT:
- case TEMPLATE:
- return true;
- case READONLY:
- return false;
- default:
- if (!completed) {
- final IWizardStep activeStep = getWizardModel().getActiveStep();
- completed = (activeStep != null)
- && getWizardModel().isLastStep(activeStep)
- && super.isEnabled();
- }
- return completed;
- }
- }
-
- @Override
- public boolean isVisible() {
- switch (mode) {
- case READONLY:
- return false;
- default:
- return true;
- }
- }
-
- @Override
- public void onClick() {
- IWizardModel wizardModel = getWizardModel();
- IWizardStep activeStep = wizardModel.getActiveStep();
-
- // let the step apply any state
- activeStep.applyState();
-
- // if the step completed after applying the state, notify the wizard
- if (activeStep.isComplete()
- && SyncopeWebApplication.get().isCaptchaEnabled()
- && !getWizardModel().isLastStep(activeStep)) {
- // go to last step
- getWizardModel().last();
- } else if (activeStep.isComplete()) {
- getWizardModel().finish();
- } else {
- error(getLocalizer().getString(
- "org.apache.wicket.extensions.wizard.FinishButton.step.did.not.complete",
- this));
- }
- }
- };
- }
-
- };
- }
-
- }.setEventSink(eventSink).addOuterObject(outerObjects);
- }
-}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/EnduserAuxClasses.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/EnduserAuxClasses.java
deleted file mode 100644
index bdd6f44..0000000
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/EnduserAuxClasses.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.enduser.wizards.any;
-
-import org.apache.syncope.client.ui.commons.wizards.any.AbstractAuxClasses;
-import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
-import java.util.List;
-import java.util.stream.Collectors;
-import org.apache.syncope.client.enduser.rest.SyncopeRestClient;
-import org.apache.syncope.common.lib.to.AnyTO;
-import org.apache.syncope.common.lib.to.AnyTypeClassTO;
-
-public class EnduserAuxClasses extends AbstractAuxClasses {
-
- private static final long serialVersionUID = 552437609667518888L;
-
- public <T extends AnyTO> EnduserAuxClasses(final AnyWrapper<T> modelObject, final List<String> anyTypeClasses) {
- super(modelObject, anyTypeClasses);
- }
-
- @Override
- protected final List<AnyTypeClassTO> listAnyTypecClasses() {
- return SyncopeRestClient.listAnyTypeClasses().stream().map(name -> {
- AnyTypeClassTO anyTypeClassTO = new AnyTypeClassTO();
- anyTypeClassTO.setKey(name);
- return anyTypeClassTO;
- }).collect(Collectors.toList());
- }
-}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/UserDetails.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/UserDetails.java
deleted file mode 100644
index 8f07432..0000000
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/UserDetails.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.enduser.wizards.any;
-
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.password.strength.PasswordStrengthBehavior;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.password.strength.PasswordStrengthConfig;
-import java.util.List;
-import java.util.stream.Collectors;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.client.enduser.rest.RealmRestClient;
-import org.apache.syncope.client.enduser.rest.SecurityQuestionRestClient;
-import org.apache.syncope.client.ui.commons.Constants;
-import org.apache.syncope.client.ui.commons.ajax.markup.html.LabelInfo;
-import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
-import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
-import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
-import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
-import org.apache.syncope.client.ui.commons.wizards.any.PasswordPanel;
-import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
-import org.apache.syncope.common.lib.to.RealmTO;
-import org.apache.syncope.common.lib.to.SecurityQuestionTO;
-import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.wicket.Component;
-import org.apache.wicket.ajax.AjaxEventBehavior;
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.ajax.markup.html.AjaxLink;
-import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
-import org.apache.wicket.extensions.markup.html.tabs.ITab;
-import org.apache.wicket.extensions.wizard.WizardStep;
-import org.apache.wicket.markup.ComponentTag;
-import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.markup.html.form.IChoiceRenderer;
-import org.apache.wicket.markup.html.panel.Panel;
-import org.apache.wicket.model.IModel;
-import org.apache.wicket.model.Model;
-import org.apache.wicket.model.PropertyModel;
-import org.apache.wicket.model.ResourceModel;
-
-public class UserDetails extends WizardStep {
-
- private static final long serialVersionUID = 6592027822510220463L;
-
- private static final String PASSWORD_CONTENT_PATH = "body:content";
-
- private final FieldPanel<String> realm;
-
- protected final AjaxTextFieldPanel username;
-
- private final FieldPanel<String> securityQuestion;
-
- private final FieldPanel<String> securityAnswer;
-
- protected final UserTO userTO;
-
- @SuppressWarnings({ "unchecked", "rawtypes" })
- public UserDetails(
- final UserWrapper wrapper,
- final boolean templateMode,
- final boolean showPasswordManagement) {
-
- super();
-
- userTO = wrapper.getInnerObject();
- // ------------------------
- // Username
- // ------------------------
- username = new AjaxTextFieldPanel(Constants.USERNAME_FIELD_NAME, Constants.USERNAME_FIELD_NAME,
- new PropertyModel<>(userTO, Constants.USERNAME_FIELD_NAME), false);
-
- if (wrapper.getPreviousUserTO() != null && StringUtils.
- compare(wrapper.getPreviousUserTO().getUsername(), wrapper.getInnerObject().getUsername()) != 0) {
- username.showExternAction(new LabelInfo("externalAction", wrapper.getPreviousUserTO().getUsername()));
- }
-
- if (templateMode) {
- username.enableJexlHelp();
- } else {
- username.addRequiredLabel();
- }
- add(username);
- // ------------------------
-
- // ------------------------
- // Realm
- // ------------------------
- realm = new AjaxDropDownChoicePanel<>(
- "destinationRealm", "destinationRealm", new PropertyModel<>(userTO, "realm"), false);
-
- ((AjaxDropDownChoicePanel<String>) realm).setChoices(
- RealmRestClient.list().stream().map(RealmTO::getFullPath).collect(Collectors.toList()));
- add(realm);
-
- // ------------------------
- // Password
- // ------------------------
- Model<Integer> model = Model.of(-1);
-
- Accordion accordion = new Accordion("accordionPanel", List.of(
- new AbstractTab(new ResourceModel("password.change", "Change password")) {
-
- private static final long serialVersionUID = 1037272333056449378L;
-
- @Override
- public Panel getPanel(final String panelId) {
- EditUserPasswordPanel panel = new EditUserPasswordPanel(panelId, wrapper, templateMode);
- panel.setEnabled(model.getObject() >= 0);
- return panel;
- }
- }), model) {
-
- private static final long serialVersionUID = -2898628183677758699L;
-
- @Override
- protected Component newTitle(final String markupId, final ITab tab, final Accordion.State state) {
- return new AjaxLink<Integer>(markupId) {
-
- private static final long serialVersionUID = 7021195294339489084L;
-
- @Override
- protected void onComponentTag(final ComponentTag tag) {
- super.onComponentTag(tag);
- tag.put("style", "color: #337ab7");
- }
-
- @Override
- public void onClick(final AjaxRequestTarget target) {
- model.setObject(model.getObject() == 0 ? -1 : 0);
- Component passwordPanel = getParent().get(PASSWORD_CONTENT_PATH);
- passwordPanel.setEnabled(model.getObject() >= 0);
- target.add(passwordPanel);
- }
- }.setBody(new ResourceModel("password.change", "Change password ..."));
- }
- };
-
- accordion.setOutputMarkupId(true);
- accordion.setVisible(showPasswordManagement);
- add(accordion);
- // ------------------------
-
- // ------------------------
- // Security Question
- // ------------------------
- securityQuestion = new AjaxDropDownChoicePanel("securityQuestion", "securityQuestion", new PropertyModel<>(
- userTO, "securityQuestion"));
- ((AjaxDropDownChoicePanel) securityQuestion).setNullValid(true);
-
- final List<SecurityQuestionTO> securityQuestions = SecurityQuestionRestClient.list();
- ((AjaxDropDownChoicePanel<String>) securityQuestion).setChoices(securityQuestions.stream().map(
- SecurityQuestionTO::getKey).collect(Collectors.toList()));
- ((AjaxDropDownChoicePanel<String>) securityQuestion).setChoiceRenderer(
- new IChoiceRenderer<String>() {
-
- private static final long serialVersionUID = -4421146737845000747L;
-
- @Override
- public Object getDisplayValue(final String value) {
- return securityQuestions.stream().filter(sq -> value.equals(sq.getKey()))
- .map(SecurityQuestionTO::getContent).findFirst().orElse(null);
- }
-
- @Override
- public String getIdValue(final String value, final int index) {
- return value;
- }
-
- @Override
- public String getObject(
- final String id,
- final IModel<? extends List<? extends String>> choices) {
- return id;
- }
- });
-
- securityQuestion.add(new AjaxEventBehavior(Constants.ON_CHANGE) {
-
- private static final long serialVersionUID = 192359260308762078L;
-
- @Override
- protected void onEvent(final AjaxRequestTarget target) {
- securityAnswer.setEnabled(StringUtils.isNotBlank(securityQuestion.getModelObject()));
- target.add(securityAnswer);
- }
- });
-
- add(securityQuestion);
- // ------------------------
-
- // ------------------------
- // Security Answer
- // ------------------------
- securityAnswer =
- new AjaxTextFieldPanel("securityAnswer", "securityAnswer",
- new PropertyModel<>(userTO, "securityAnswer"), false);
- add(securityAnswer.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true).setEnabled(StringUtils.
- isNotBlank(securityQuestion.getModelObject())));
- // ------------------------
- }
-
- public static class EditUserPasswordPanel extends Panel {
-
- private static final long serialVersionUID = -8198836979773590078L;
-
- public EditUserPasswordPanel(
- final String id,
- final UserWrapper wrapper,
- final boolean templateMode) {
- super(id);
- setOutputMarkupId(true);
- add(new Label("warning", new ResourceModel("password.change.warning")));
- add(new PasswordPanel("passwordPanel", wrapper, templateMode, new PasswordStrengthBehavior(
- new PasswordStrengthConfig()
- .withDebug(true)
- .withShowVerdictsInsideProgressBar(true)
- .withShowProgressBar(true))));
- }
-
- }
-}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/UserWizardBuilder.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/UserWizardBuilder.java
deleted file mode 100644
index 714cacb..0000000
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/UserWizardBuilder.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.enduser.wizards.any;
-
-import java.io.Serializable;
-import java.util.List;
-import java.util.Optional;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.client.ui.commons.layout.UserForm;
-import org.apache.syncope.client.enduser.layout.UserFormLayoutInfo;
-import org.apache.syncope.client.enduser.rest.UserSelfRestClient;
-import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
-import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
-import org.apache.syncope.client.ui.commons.wizards.exception.CaptchaNotMatchingException;
-import org.apache.syncope.common.lib.AnyOperations;
-import org.apache.syncope.common.lib.EntityTOUtils;
-import org.apache.syncope.common.lib.request.PasswordPatch;
-import org.apache.syncope.common.lib.request.UserCR;
-import org.apache.syncope.common.lib.request.UserUR;
-import org.apache.syncope.common.lib.to.ProvisioningResult;
-import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.wicket.PageReference;
-
-public class UserWizardBuilder extends AnyWizardBuilder implements UserForm {
-
- private static final long serialVersionUID = 6716803168859873877L;
-
- protected final UserSelfRestClient userSelfRestClient = new UserSelfRestClient();
-
- /**
- * Constructor to be used for templating only.
- *
- * @param anyTypeClasses any type classes.
- * @param formLayoutInfo form layout.
- * @param pageRef reference page.
- */
- public UserWizardBuilder(
- final List<String> anyTypeClasses,
- final UserFormLayoutInfo formLayoutInfo,
- final PageReference pageRef) {
-
- super(new UserWrapper(null), anyTypeClasses, formLayoutInfo, pageRef);
- }
-
- /**
- * Constructor to be used for Approval and Remediation details only.
- *
- * @param previousUserTO previous user status.
- * @param userTO new user status to be approved.
- * @param anyTypeClasses any type classes.
- * @param formLayoutInfo from layout.
- * @param pageRef reference page.
- */
- public UserWizardBuilder(
- final UserTO previousUserTO,
- final UserTO userTO,
- final List<String> anyTypeClasses,
- final UserFormLayoutInfo formLayoutInfo,
- final PageReference pageRef) {
-
- super(new UserWrapper(previousUserTO, userTO), anyTypeClasses, formLayoutInfo, pageRef);
- }
-
- @Override
- protected Serializable onApplyInternal(final AnyWrapper<UserTO> modelObject) {
- // captcha check
- if (captcha != null && captcha.evaluate() && !captcha.captchaCheck()) {
- throw new CaptchaNotMatchingException();
- }
- UserTO inner = modelObject.getInnerObject();
-
- ProvisioningResult<UserTO> result;
- if (inner.getKey() == null) {
- UserCR req = new UserCR();
- EntityTOUtils.toAnyCR(inner, req);
- req.setStorePassword(modelObject instanceof UserWrapper
- ? UserWrapper.class.cast(modelObject).isStorePasswordInSyncope()
- : StringUtils.isNotBlank(inner.getPassword()));
-
- result = UserSelfRestClient.create(req);
- } else {
- fixPlainAndVirAttrs(inner, getOriginalItem().getInnerObject());
- UserUR userUR = AnyOperations.diff(inner, getOriginalItem().getInnerObject(), false);
-
- if (StringUtils.isNotBlank(inner.getPassword())) {
- PasswordPatch passwordPatch = new PasswordPatch.Builder().
- value(inner.getPassword()).onSyncope(true).resources(inner.getResources()).build();
- userUR.setPassword(passwordPatch);
- }
-
- // update just if it is changed
- if (userUR.isEmpty()) {
- result = new ProvisioningResult<>();
- result.setEntity(inner);
- } else {
- result = userSelfRestClient.update(getOriginalItem().getInnerObject().getETagValue(), userUR);
- }
- }
-
- return result;
- }
-
- /**
- * Overrides default setItem() in order to clean statusModel as well.
- *
- * @param item item to be set.
- * @return the current wizard.
- */
- @Override
- public UserWizardBuilder setItem(final AnyWrapper<UserTO> item) {
- super.setItem(Optional.ofNullable(item)
- .map(userTOAnyWrapper -> new UserWrapper(userTOAnyWrapper.getInnerObject())).orElse(null));
- return this;
- }
-
-}
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/AdminLTE_plugins/dataTables.bootstrap4.min.css b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/AdminLTE_plugins/dataTables.bootstrap4.min.css
new file mode 100644
index 0000000..f1930be
--- /dev/null
+++ b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/AdminLTE_plugins/dataTables.bootstrap4.min.css
@@ -0,0 +1 @@
+table.dataTable{clear:both;margin-top:6px !important;margin-bottom:6px !important;max-width:none !important;border-collapse:separate !important;border-spacing:0}table.dataTable td,table.dataTable th{-webkit-box-sizing:content-box;box-sizing:content-box}table.dataTable td.dataTables_empty,table.dataTable th.dataTables_empty{text-align:center}table.dataTable.nowrap th,table.dataTable.nowrap td{white-space:nowrap}div.dataTables_wrapper div.dataTables_length label{font-weight:normal;text-ali [...]
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/BaseExtPage.java b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/accessibility.css
similarity index 67%
rename from client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/BaseExtPage.java
rename to client/idrepo/enduser/src/main/resources/META-INF/resources/css/accessibility.css
index ace1d2f..21b9201 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/BaseExtPage.java
+++ b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/accessibility.css
@@ -16,19 +16,29 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.client.enduser.pages;
-import org.apache.wicket.request.mapper.parameter.PageParameters;
+.btn-accessibility {
+ position: absolute;
+ right: 0;
+ font-size: 12px;
+ padding: 15px 15px 0 0;
+}
-public abstract class BaseExtPage extends BaseEnduserWebPage {
+.btn-accessibility i {
+ font-size: 30px;
+}
- private static final long serialVersionUID = 4627828052717627159L;
+.btn-accessibility:hover {
+ cursor:pointer;
+}
- public BaseExtPage() {
- super();
- }
+.control-sidebar-menu a:focus {
+ outline: 1px #949494 solid;
+}
- public BaseExtPage(final PageParameters parameters) {
- super(parameters);
- }
+#change_contrast {
+ top: 0;
+}
+#change_fontSize {
+ top: 40px;
}
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/accessibility/accessibilityFont.css b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/accessibility/accessibilityFont.css
new file mode 100644
index 0000000..da26456
--- /dev/null
+++ b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/accessibility/accessibilityFont.css
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+body,
+p {
+ font-size: 200%;
+}
+
+.menu a,
+.treeview-menu a,
+.control-sidebar-subheading {
+ font-size: 80% !important;
+}
+
+.box-header > .fa,
+.box-header > .glyphicon,
+.box-header > .ion, .box-header .box-title,
+.dropdown-toggle .filter-option,
+.dropdown-menu a,
+.menu-info span,
+.user-header,
+.header,
+.modal-title,
+.small-box p,
+.alert-widget,
+.alert-widget > a > .label,
+.alert h4,
+#mappings .fa,
+.popover-content,
+.box .dropdown-toggle .glyphicon,
+.box-header button,
+.toggle .toggle-group .btn,
+.input-group .form-control label {
+ font-size: 120%;
+}
+
+input,
+select,
+.footer a,
+.modal-footer button,
+.dropdown-menu:not([role='menu']),
+.dropdown-menu > li.header,
+div#tablehandling ul.menu i,
+.content-header > .breadcrumb,
+.btn-primary:not(.btn-circle),
+.modal-content .box .dialog pre {
+ font-size: 100% !important;
+}
+
+#topology .window {
+ height: 90px;
+}
+
+.dataTables_length select {
+ font-size: 85% !important;
+}
+
+button.close {
+ font-size: 2em;
+}
+
+.details-footer .information {
+ font-size: 12px;
+}
+
+.btn-file i,
+.btn-file span,
+.input-group-btn button.btn-primary {
+ font-size: 20px !important;
+}
+
+.checkbox input[type=checkbox],
+.checkbox-inline input[type=checkbox],
+.radio input[type=radio],
+.radio-inline input[type=radio],
+input[type=checkbox],
+input[type=radio] {
+ width: 20px;
+ height: 20px;
+}
+
+.k-timepicker,
+.k-datepicker {
+ width: 200px !important;
+}
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/accessibility/accessibilityHC.css b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/accessibility/accessibilityHC.css
new file mode 100644
index 0000000..aa4d5b9
--- /dev/null
+++ b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/accessibility/accessibilityHC.css
@@ -0,0 +1,296 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* General
+============================================================================= */
+.content-wrapper,
+.content-wrapper .box,
+.modal-content,
+.modal-header,
+.background-footer,
+input:not(:disabled):not([type="file"]),
+select,
+select option,
+button:not(.close),
+.table .sorting,
+.dataTables_paginate a,
+.modal-footer,
+.box-header,
+.dropdown-menu,
+.main-footer,
+.circular-actions a,
+.k-select,
+.file-caption.kv-fileinput-caption {
+ color: #f7f7f7 !important;
+ background-color: #0f1417 !important;
+}
+
+
+.dataTable thead th:after,
+.close {
+ color: #f7f7f7 !important;
+ opacity: 0.8 !important;
+}
+
+
+.input-group-addon a,
+.input-group-addon i,
+.dropdown-menu > li:not(.disabled) > a:hover,
+.dropdown-menu > li:not(.disabled) > a:hover span,
+.dropdown-menu > li:not(.disabled) > a:focus,
+.dropdown-menu > li:not(.disabled) > a:focus span,
+#startAtContainer .input-group-addon span,
+#startAtContainer .k-widget span,
+#startAtContainer .k-widget i,
+#startAtContainer .k-widget input,
+.modal-footer i,
+.navbar-nav > .user-menu > .dropdown-menu > .user-footer .btn-default,
+.content-wrapper .nav-tabs a,
+.wrapper .content-wrapper .nav.nav-tabs li.active a span,
+.wrapper .content-wrapper .nav.nav-tabs li.active a,
+.wrapper .content-wrapper .nav.nav-tabs li a:hover span,
+.wrapper .content-wrapper .nav.nav-tabs li a:hover,
+.wrapper .content-wrapper .nav.nav-tabs li a:active span,
+.wrapper .content-wrapper .nav.nav-tabs li a:active,
+.wrapper .content-wrapper .nav.nav-tabs li a:focus span,
+.wrapper .content-wrapper .nav.nav-tabs li a:focus,
+.wrapper .content-wrapper .tab-content .btn-primary .fa-download {
+ color: #000000 !important;
+}
+
+
+.content-wrapper a:not(.btn-primary),
+.content-wrapper span:not(.label-info):not([role="presentation"]):not([class^='cm-']),
+.content-wrapper p,
+.content-wrapper .box-title a,
+.realm-choice .dropdown-menu > li:not(.disabled) > a:hover,
+.realm-choice .dropdown-menu > li:not(.disabled) > a:hover span,
+.realm-choice .dropdown-menu > li:not(.disabled) > a:focus,
+.realm-choice .dropdown-menu > li:not(.disabled) > a:focus span,
+.box-header,
+.breadcrumb .active,
+table tbody tr:hover button span,
+.dropdown-menu > li > a,
+.modal-header button,
+.circular-actions a i,
+.wizard-form .input-group-addon i,
+#startAtContainer .input-group-addon i,
+.attribute .input-group-addon i {
+ color: #f7f7f7 !important;
+}
+
+
+.box.box-primary {
+ border-top-color: #f7f7f7;
+}
+
+
+img,
+.content-wrapper div.btn.btn-file span.hidden-xs {
+ background-color: #f7f7f7;
+ color: #000000 !important;
+}
+.btn-primary,
+.callout.callout-info,
+.alert-info,
+.label-info,
+.modal-info .modal-body {
+ background-color: #f7f7f7 !important;
+ color: #000000 !important;
+}
+
+
+.logo img,
+.modal-content .input-group .input-group-addon {
+ background-color: transparent;
+}
+
+
+.modal-dialog {
+ border: 2px solid white;
+}
+
+
+#veil:not(:required):after {
+ -webkit-box-shadow: #f7f7f7 1.5em 0 0 0,
+ #f7f7f7 1.1em 1.1em 0 0,
+ #f7f7f7 0 1.5em 0 0,
+ #f7f7f7 -1.1em 1.1em 0 0,
+ rgba(0, 0, 0, 0.5) -1.5em 0 0 0,
+ rgba(0, 0, 0, 0.5) -1.1em -1.1em 0 0,
+ #f7f7f7 0 -1.5em 0 0,
+ #f7f7f7 1.1em -1.1em 0 0;
+ box-shadow: #f7f7f7 1.5em 0 0 0,
+ #f7f7f7 1.1em 1.1em 0 0,
+ #f7f7f7 0 1.5em 0 0,
+ #f7f7f7 -1.1em 1.1em 0 0,
+ #f7f7f7 -1.5em 0 0 0,
+ #f7f7f7 -1.1em -1.1em 0 0,
+ #f7f7f7 0 -1.5em 0 0,
+ #f7f7f7 1.1em -1.1em 0 0;
+}
+
+
+.input-group input:disabled,
+.input-group input[disabled],
+.control-sidebar-dark .control-sidebar-menu > li > a:hover,
+.table-hover > tbody > tr:hover,
+.skin-blue .main-header .navbar .sidebar-toggle:hover,
+.main-header .navbar .sidebar-toggle:hover,
+.skin-blue .main-header .logo:hover,
+.main-header .logo:hover,
+div.toggle-menu ul li:hover,
+.skin-blue .main-header .navbar .nav > li > a:hover,
+.skin-blue .main-header .navbar .nav > li > a:active,
+.skin-blue .main-header .navbar .nav > li > a:focus,
+.skin-blue .main-header .navbar .nav .open > a:hover,
+.skin-blue .main-header .navbar .nav .open > a:focus,
+.skin-blue .sidebar-menu > li:hover > a,
+.skin-blue .sidebar-menu > li.active > a,
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus,
+.dropdown-menu > li > a:hover,
+.k-block,
+.k-draghandle,
+.k-grid-header,
+.k-grouping-header,
+.k-header,
+.k-pager-wrap,
+.k-toolbar,
+.k-treemap-tile,
+.k-picker-wrap,
+.k-state-disabled,
+.km-pane-wrapper .k-header {
+ background-color: rgba(108, 115, 117, 0.55);
+}
+
+
+.sidebar-mini .main-header .navbar {
+ background-color: #222d32; /* color from 'skin-blue' */
+ border-bottom: 1px white solid;
+ box-sizing: border-box;
+}
+.sidebar-mini .main-header .logo,
+div.toggle-menu,
+.skin-blue .main-header li.user-header {
+ background-color: #222d32;
+}
+
+
+div.toggle-menu {
+ border: 1px solid #f7f7f7;
+}
+
+
+a,
+.pagination > .active > a,
+.pagination > .active > a:focus,
+.pagination > .active > a:hover,
+.pagination > .active > span,
+.pagination > .active > span:focus,
+.pagination > .active > span:hover {
+ border-color: #76abd9;
+}
+a {
+ color: #76abd9;
+}
+
+
+.logs button.btn-primary {
+ border-color: #f7f7f7;
+}
+.logs button.btn-primary:hover {
+ border-color: #adadad;
+}
+div.infolabel,
+.input-group input:disabled,
+.input-group input[disabled] {
+ color: #d2d2d2;
+}
+
+
+.bg-red,
+.callout.callout-danger,
+.alert-danger,
+.alert-error,
+.label-danger,
+.modal-danger .modal-body,
+.btn-danger.active,
+.btn-danger:active,
+.open>.dropdown-toggle.btn-danger {
+ background-color: #942819 !important;
+}
+.bg-yellow {
+ background-color: #6F4706 !important;
+}
+.bg-green,
+.copy-clipboard-feedback,
+.btn-success,
+.callout.callout-success,
+.alert-success,
+.label-success,
+.modal-success .modal-body {
+ background-color: #005C32 !important;
+}
+.bg-aqua {
+ background-color: #004E61 !important;
+}
+.bg-yellow,
+.callout.callout-warning,
+.alert-warning,
+.label-warning,
+.modal-warning .modal-body {
+ background-color: #6F4706 !important;
+}
+.callout.callout-warning {
+ border-color: #6F4706;
+}
+
+
+.bootstrap-select .btn.btn-default {
+ background-color: rgba(101, 101, 101, 0.7) !important;
+}
+
+
+/* Login page
+============================================================================= */
+.login-body {
+ background-image: linear-gradient(rgb(31, 109, 142), #004626);
+}
+
+.login-logo {
+ background: transparent;
+}
+
+.btn-accessibility {
+ color: #f7f7f7 !important;
+}
+
+.form-signin .btn-primary,
+.form-signin .btn-primary {
+ border: 2px solid white;
+}
+.form-signin .btn-primary.focus,
+.form-signin .btn-primary:focus {
+ border-color: #8c8c8c;
+}
+
+.login-card {
+ background-color: #0f1417;
+}
\ No newline at end of file
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts.css b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts.css
new file mode 100644
index 0000000..da7bdf8
--- /dev/null
+++ b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts.css
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* source-sans-pro-300 - latin-ext_cyrillic_latin_cyrillic-ext */
+@font-face {
+ font-family: 'Source Sans Pro';
+ font-style: normal;
+ font-weight: 300;
+ src: local('Source Sans Pro Light'), local('SourceSansPro-Light'),
+ url('./fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
+ url('./fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+}
+
+/* source-sans-pro-300italic - latin-ext_cyrillic_latin_cyrillic-ext */
+@font-face {
+ font-family: 'Source Sans Pro';
+ font-style: italic;
+ font-weight: 300;
+ src: local('Source Sans Pro Light Italic'), local('SourceSansPro-LightItalic'),
+ url('./fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
+ url('./fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+}
+
+/* source-sans-pro-regular - latin-ext_cyrillic_latin_cyrillic-ext */
+@font-face {
+ font-family: 'Source Sans Pro';
+ font-style: normal;
+ font-weight: 400;
+ src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'),
+ url('./fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
+ url('./fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+}
+
+/* source-sans-pro-italic - latin-ext_cyrillic_latin_cyrillic-ext */
+@font-face {
+ font-family: 'Source Sans Pro';
+ font-style: italic;
+ font-weight: 400;
+ src: local('Source Sans Pro Italic'), local('SourceSansPro-Italic'),
+ url('./fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
+ url('./fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+}
+
+/* source-sans-pro-600 - latin-ext_cyrillic_latin_cyrillic-ext */
+@font-face {
+ font-family: 'Source Sans Pro';
+ font-style: normal;
+ font-weight: 600;
+ src: local('Source Sans Pro SemiBold'), local('SourceSansPro-SemiBold'),
+ url('./fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
+ url('./fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+}
+
+/* source-sans-pro-600italic - latin-ext_cyrillic_latin_cyrillic-ext */
+@font-face {
+ font-family: 'Source Sans Pro';
+ font-style: italic;
+ font-weight: 600;
+ src: local('Source Sans Pro SemiBold Italic'), local('SourceSansPro-SemiBoldItalic'),
+ url('./fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
+ url('./fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+}
+
+/* source-sans-pro-700 - latin-ext_cyrillic_latin_cyrillic-ext */
+@font-face {
+ font-family: 'Source Sans Pro';
+ font-style: normal;
+ font-weight: 700;
+ src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'),
+ url('./fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
+ url('./fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+}
+
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300.woff b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300.woff
new file mode 100644
index 0000000..2132b5e
Binary files /dev/null and b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300.woff differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300.woff2 b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300.woff2
new file mode 100644
index 0000000..943f826
Binary files /dev/null and b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300.woff2 differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300italic.woff b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300italic.woff
new file mode 100644
index 0000000..aa25cd3
Binary files /dev/null and b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300italic.woff differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300italic.woff2 b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300italic.woff2
new file mode 100644
index 0000000..441997f
Binary files /dev/null and b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300italic.woff2 differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600.woff b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600.woff
new file mode 100644
index 0000000..24d2824
Binary files /dev/null and b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600.woff differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600.woff2 b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600.woff2
new file mode 100644
index 0000000..9ec7d25
Binary files /dev/null and b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600.woff2 differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600italic.woff b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600italic.woff
new file mode 100644
index 0000000..ce5a1cc
Binary files /dev/null and b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600italic.woff differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600italic.woff2 b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600italic.woff2
new file mode 100644
index 0000000..7ed2f82
Binary files /dev/null and b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600italic.woff2 differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-700.woff b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-700.woff
new file mode 100644
index 0000000..9fbfe68
Binary files /dev/null and b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-700.woff differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-700.woff2 b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-700.woff2
new file mode 100644
index 0000000..096dcb1
Binary files /dev/null and b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-700.woff2 differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-italic.woff b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-italic.woff
new file mode 100644
index 0000000..c1cf1ea
Binary files /dev/null and b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-italic.woff differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-italic.woff2 b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-italic.woff2
new file mode 100644
index 0000000..ff006be
Binary files /dev/null and b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-italic.woff2 differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-regular.woff b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-regular.woff
new file mode 100644
index 0000000..e8a1ac7
Binary files /dev/null and b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-regular.woff differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-regular.woff2 b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-regular.woff2
new file mode 100644
index 0000000..1b0bc46
Binary files /dev/null and b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-regular.woff2 differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/login.css b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/login.css
new file mode 100644
index 0000000..931cb47
--- /dev/null
+++ b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/login.css
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+body, html {
+ height: 100% !important;
+ background-repeat: no-repeat;
+ background-image: linear-gradient(rgb(70, 30, 30), #dd4b39);
+}
+
+.card-container.card {
+ width: 350px;
+ padding: 40px 40px;
+}
+
+.btn {
+ font-weight: 700;
+ height: 36px;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+ cursor: default;
+}
+
+/*
+ * Card component
+ */
+.card {
+ background-color: #F7F7F7;
+ /* just in case there no content*/
+ padding: 20px 25px 30px;
+ margin: 0 auto 25px;
+ margin-top: 50px;
+ /* shadows and rounded borders */
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ border-radius: 2px;
+ -moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
+ -webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
+ box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
+}
+
+.login-logo {
+ width: 200px;
+ margin: 0 auto 10px;
+ display: block;
+}
+
+/*
+ * Form styles
+ */
+.profile-name-card {
+ font-size: 16px;
+ font-weight: bold;
+ text-align: center;
+ margin: 10px 0 0;
+ min-height: 1em;
+}
+
+.form-signin #inputPassword {
+ direction: ltr;
+ height: 44px;
+ font-size: 16px;
+}
+
+.form-signin input[type=password],
+.form-signin input[type=text],
+.form-signin button {
+ width: 100%;
+ display: block;
+ margin-bottom: 10px;
+ z-index: 1;
+ position: relative;
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.form-signin .form-control:focus {
+ border-color: rgb(104, 145, 162);
+ outline: 0;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgb(104, 145, 162);
+ box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgb(104, 145, 162);
+}
+
+.btn.btn-signin {
+ background-color: rgb(104, 145, 162);
+ padding: 0px;
+ font-weight: 700;
+ font-size: 14px;
+ height: 36px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ border: none;
+ -o-transition: all 0.218s;
+ -moz-transition: all 0.218s;
+ -webkit-transition: all 0.218s;
+ transition: all 0.218s;
+}
+
+.btn.btn-signin:hover,
+.btn.btn-signin:active,
+.btn.btn-signin:focus {
+ background-color: #00a65a;
+}
+
+.btn.btn-sso {
+ padding: 0px;
+ font-weight: 700;
+ font-size: 14px;
+ height: 36px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ border: none;
+ -o-transition: all 0.218s;
+ -moz-transition: all 0.218s;
+ -webkit-transition: all 0.218s;
+ transition: all 0.218s;
+}
+
+.btn.btn-sso:hover,
+.btn.btn-sso:active,
+.btn.btn-sso:focus {
+ background-color: #00a65a;
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserConstants.java b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/search.css
similarity index 50%
copy from client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserConstants.java
copy to client/idrepo/enduser/src/main/resources/META-INF/resources/css/search.css
index 0a683a9..ece4f4a 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserConstants.java
+++ b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/search.css
@@ -16,20 +16,65 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.client.enduser;
-public final class SyncopeEnduserConstants {
+.searchBox .col-xs-12{
+ padding-left: 0px !important;
+ padding-right: 0px !important;
+}
+
+.clause{
+ display: block;
+ line-height: 34px;
+ width: 100%;
+}
+
+.clause .operator{
+ width: 65px !important;
+}
+
+.clause .operator .checkbox{
+ margin: 0px !important;
+}
+
+.clause .field {
+ line-height: 34px;
+ float: left;
+ padding: 0 3px 0px 0px;
+ display: inline-block !important;
+}
+.clause .type{
+ width: 120px !important;
+}
- public static final String CAPTCHA_SESSION_KEY = "captcha";
+.clause .property{
+ width: 190px;
+}
- public static final String XSRF_COOKIE = "XSRF-TOKEN";
+.clause .comparator{
+ width: 100px;
+}
- public static final String XSRF_HEADER_NAME = "X-XSRF-TOKEN";
+.clause .comparator button{
+ width: 100px !important;
+}
- public static final String MEMBERSHIP_ATTR_SEPARATOR = "#";
+.clause .value{
+ width: 220px;
+}
- private SyncopeEnduserConstants() {
- // private constructor for utility class
- }
+.clause .textvalue{
+ width: 45px;
+}
+
+.clause .action{
+ float: left;
+ padding: 0px 7px 0px;
+}
+.searchBox .input-group-addon:last-child{
+ border: 1px solid #ccc !important;
}
+
+.searchBox .input-group{
+ margin-top: 1px;
+}
\ No newline at end of file
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/syncopeEnduser.css b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/syncopeEnduser.css
new file mode 100644
index 0000000..0b487d3
--- /dev/null
+++ b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/syncopeEnduser.css
@@ -0,0 +1,1377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+pre {
+ white-space: -moz-pre-wrap; /* Mozilla, supported since 1999 */
+ white-space: -pre-wrap; /* Opera */
+ white-space: -o-pre-wrap; /* Opera */
+ white-space: pre-wrap; /* CSS3 - Text module (Candidate Recommendation) http://www.w3.org/TR/css3-text/#white-space */
+ word-wrap: break-word; /* IE 5.5+ */
+}
+
+/* Absolute Center Spinner */
+#veil {
+ display:none;
+ position: fixed;
+ z-index:99999;
+ height: 2em;
+ width: 2em;
+ overflow: show;
+ margin: auto;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+}
+
+/* Transparent Overlay */
+#veil:before {
+ content: '';
+ display: block;
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0,0,0,0.3);
+}
+
+/* :not(:required) hides these rules from IE9 and below */
+#veil:not(:required) {
+ /* hide "loading..." text */
+ font: 0/0 a;
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0;
+}
+
+#veil:not(:required):after {
+ content: '';
+ display: block;
+ font-size: 10px;
+ width: 1em;
+ height: 1em;
+ margin-top: -0.5em;
+ -webkit-animation: spinner 2000ms infinite linear;
+ -moz-animation: spinner 2000ms infinite linear;
+ -ms-animation: spinner 2000ms infinite linear;
+ -o-animation: spinner 2000ms infinite linear;
+ animation: spinner 2000ms infinite linear;
+ border-radius: 0.5em;
+ -webkit-box-shadow: rgba(0, 0, 0, 0.75) 1.5em 0 0 0, rgba(0, 0, 0, 0.75) 1.1em 1.1em 0 0, rgba(0, 0, 0, 0.75) 0 1.5em 0 0, rgba(0, 0, 0, 0.75) -1.1em 1.1em 0 0, rgba(0, 0, 0, 0.5) -1.5em 0 0 0, rgba(0, 0, 0, 0.5) -1.1em -1.1em 0 0, rgba(0, 0, 0, 0.75) 0 -1.5em 0 0, rgba(0, 0, 0, 0.75) 1.1em -1.1em 0 0;
+ box-shadow: rgba(0, 0, 0, 0.75) 1.5em 0 0 0, rgba(0, 0, 0, 0.75) 1.1em 1.1em 0 0, rgba(0, 0, 0, 0.75) 0 1.5em 0 0, rgba(0, 0, 0, 0.75) -1.1em 1.1em 0 0, rgba(0, 0, 0, 0.75) -1.5em 0 0 0, rgba(0, 0, 0, 0.75) -1.1em -1.1em 0 0, rgba(0, 0, 0, 0.75) 0 -1.5em 0 0, rgba(0, 0, 0, 0.75) 1.1em -1.1em 0 0;
+}
+
+/* Animation */
+
+@-webkit-keyframes spinner {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ -moz-transform: rotate(0deg);
+ -ms-transform: rotate(0deg);
+ -o-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ -moz-transform: rotate(360deg);
+ -ms-transform: rotate(360deg);
+ -o-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+@-moz-keyframes spinner {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ -moz-transform: rotate(0deg);
+ -ms-transform: rotate(0deg);
+ -o-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ -moz-transform: rotate(360deg);
+ -ms-transform: rotate(360deg);
+ -o-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+@-o-keyframes spinner {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ -moz-transform: rotate(0deg);
+ -ms-transform: rotate(0deg);
+ -o-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ -moz-transform: rotate(360deg);
+ -ms-transform: rotate(360deg);
+ -o-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+@keyframes spinner {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ -moz-transform: rotate(0deg);
+ -ms-transform: rotate(0deg);
+ -o-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ -moz-transform: rotate(360deg);
+ -ms-transform: rotate(360deg);
+ -o-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+
+.block-sidebar {
+ max-height: 100% !important;
+ overflow: auto !important;
+ padding-top: 90px !important;
+ padding-bottom: 50px !important;
+ position: fixed !important;
+ width: 245px !important;
+}
+
+.inner-control-sidebar {
+ position: fixed;
+ height: auto;
+}
+
+.content-margin-layout {
+ margin: 0px 230px 0px 0px !important;
+ padding: 20px !important;
+}
+
+.admin-content-page {
+ padding: 20px;
+ background: #ecf0f5
+}
+
+.realms {
+ min-height: 554px
+}
+
+.realm-header {
+ clear: both;
+ display:block;
+ display: inline-table;
+ margin: 0 0 10px;
+ line-height: 25px;
+}
+
+.realm-label {
+ float: left;
+ font-size: 16px;
+}
+
+.realm-label label {
+ font-weight: 600 !important;
+}
+
+.realm-choice {
+ right: 0px;
+ position: absolute;
+}
+
+.realm-header .dropdown-menu li a {
+ text-align: left !important;
+ white-space: pre !important;
+ line-height: 7px;
+}
+
+.block-header {
+ position: fixed !important;
+ width: 100% !important;
+ top: 0 !important;
+}
+
+.block-footer {
+ position: fixed !important;
+ width: 100% !important;
+ bottom: 0px !important;
+}
+
+.logo-pos {
+ padding-top: 4px !important;
+ overflow: visible !important;
+}
+
+.angle {
+ border: medium none !important;
+ cursor: pointer;
+ display: inline-table !important;
+ float: right;
+ height: 30px;
+ overflow: hidden;
+ position: relative !important;
+ right: 0;
+ top: -30px;
+ width: 25%;
+ z-index: 3;
+}
+
+.main-header .logo {
+ height: 55px !important;
+}
+
+.w_caption h3 {
+ font-size: 16px;
+}
+
+div.wicket-modal div.w_content_3 {
+ border: 1px solid #eee;
+ border-radius: 20px;
+ padding: 5px;
+}
+
+.tab-content {
+ margin-bottom: 10px;
+ margin-top: 5px;
+ position: relative;
+ overflow-x: hidden;
+ overflow-y: auto;
+ padding: 20px 20px 5px 20px;
+}
+
+.modal-body .tab-content .information {
+ position: relative !important;
+}
+
+.scrollable-tab-content {
+ overflow-y: auto;
+ max-height: 480px;
+}
+
+.inner-scrollable-tab-content {
+ height: 430px;
+ margin-top: 20px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+a.help {
+ position: relative;
+ display: inline;
+ text-decoration: none;
+}
+
+a.help span {
+ position: absolute;
+ width:19em;
+ color: #000000;
+ background: whitesmoke;
+ visibility: hidden;
+ border-radius: 0px;
+ padding: 3px;
+}
+
+a.help span:after {
+ position: absolute;
+ top: 50%;
+ left: 100%;
+ margin-top: -8px;
+ width: 0;
+ height: 0;
+}
+
+a.help span {
+ visibility: visible;
+ opacity: none;
+ right: 100%;
+ top: 50%;
+ margin-right: 4px;
+ margin-top: -11px;
+ border: 1px solid black;
+ z-index: 1000000;
+}
+
+a.help span a {
+ color: #463;
+ text-decoration: none;
+}
+
+.modal-open .modal {
+ overflow: hidden;
+}
+
+.modal-body {
+ max-height: 550px;
+ overflow-y: auto;
+ overflow-x: hidden;
+}
+
+.modal {
+ background: rgba(0, 0, 0, 0) none repeat scroll 0 0;
+ z-index: 7000 !important;
+}
+
+.wizard-step-title {
+ font-size: 18px !important;
+ font-weight: normal !important;
+ margin-bottom: 10px !important;
+}
+
+div.wizard-view div.wizard-view{
+ max-height: 380px;
+ height: 380px;
+ overflow-y: auto;
+ overflow-x: hidden;
+ padding: 50px 20px 50px 20px;
+}
+
+.wizard-view .wizard-form{
+ max-height: 450px;
+ height: 450px;
+}
+
+.modal-body .tab-content .wizard-view {
+ max-height: 330px !important;
+ height: 330px !important;
+}
+
+.modal-body .tab-content .wizard-form {
+ max-height: 400px !important;
+ height: 400px !important;
+}
+
+.wizard-view > div {
+ display: block;
+ height: 95%;
+ position: relative;
+}
+
+.wizard-view {
+ padding: 0px 5px;
+}
+
+.wizard-form {
+ height:480px;
+ position: relative;
+}
+
+.wizard-form > div {
+ max-height: 440px;
+ height: 440px;
+ overflow-y: auto;
+ overflow-x: hidden;
+ padding: 0px;
+}
+
+.box-body .wizard-form {
+ height:400px !important;
+}
+
+.box-body .wizard-form > div {
+ height: 360px !important;
+}
+
+div.modal-body div.box-body div.wizard-buttons {
+ bottom: 30px;
+}
+
+.wizard-buttons {
+ padding: 10px 0px 5px 0px;
+ position: absolute;
+ bottom: 4px;
+ width: 100%;
+}
+
+.wizard-buttons div.float-left {
+ position: absolute;
+ left: 15px;
+}
+
+.wizard-buttons div.float-right {
+ position: absolute;
+ right: 15px;
+}
+
+.wizard-step-title {
+ font-weight: bold;
+ font-size:medium;
+}
+
+div.realms div.summarize {
+ margin: 50px 100px;
+}
+
+.navbar a {
+ height: 55px
+}
+
+.navbar .footer a {
+ height: 34px
+}
+
+.navbar .user-footer a {
+ height: 34px
+}
+
+span.overridable div.checkbox {
+ float: right;
+ margin: 0px;
+ padding: 0px;
+}
+
+span.overridable div.checkbox label div div.toggle-group label {
+ padding-left: 7px;
+}
+
+div#outer.modal-lg, div#utilityModal.modal-lg, section#notifications .modal-lg {
+ max-width: 1200px;
+ width: 97%;
+}
+
+.details {
+ max-height: 440px;
+ overflow-x: hidden;
+ overflow-y: auto;
+ display: block
+}
+
+th.checkGroupColumn {
+ width: 20px;
+ text-align: center;
+ padding-right: 8px !important;
+}
+
+td.checkGroupColumn {
+ text-align: center;
+}
+
+/**
+ BEGIN - Style for Information panel
+*/
+div.information{
+ margin: 30px 0px 0px 0px;
+ border: 1px solid #EEE;
+ font-size: 10px;
+ color: #888;
+ display: inline-table;
+ width: 100%;
+ clear: both;
+ float:none;
+ position: absolute;
+ bottom: -90px;
+ left: 0px;
+ padding: 2px;
+}
+
+div.infolabel{
+ margin-left: 5px;
+ float:left;
+ width: 150px;
+ font-weight: bold;
+ color: #888;
+}
+
+div.infoleft{
+ float:left;
+ display: inline-table;
+ width: 50%
+}
+
+div.inforight{
+ display: inline-table;
+ width: 50%
+}
+
+div.inforow{
+ display: inline-table;
+ width: 100%
+}
+
+div.wrap{
+ word-wrap: break-word;
+ width: 550px;
+ margin-left: 155px;
+}
+/**
+END - Style for Information panel
+*/
+
+#ownership div.toggle {
+ width: 110px !important;
+}
+
+.table > tbody > tr > td.list_view_panel_labels {
+ vertical-align: middle;
+}
+
+div.searchResult{
+ padding-top: 30px;
+ display: block;
+ clear: both;
+}
+
+.logs .input-group-addon .input-group-btn {
+ width: 130px !important;
+}
+
+.logs .col_width {
+ width: 90% !important;
+}
+
+.logs .box-header {
+ display: none !important;
+}
+
+.logs .box{
+ border-top: 0px !important;
+}
+
+*::after, *::before {
+ box-sizing: border-box;
+}
+
+.wicket-aa-container {
+ border-color: #eee;
+ box-shadow: none;
+ background-clip: padding-box;
+ background-color: #fff;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ border-radius: 4px;
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.176);
+ float: left;
+ font-size: 14px;
+ min-width: 160px;
+ max-height: 250px;
+ z-index: 7001 !important;
+}
+
+div.wicket-aa ul {
+ list-style: none;
+ padding-left: 15px;
+}
+/**
+START - startAt
+*/
+div#startAt {
+ background-color: rgba(98, 98, 98, 0.98) !important;
+ color: #CCC;
+ right: 5px !important;
+ top: 100px !important;
+ min-width: 450px;
+ min-height: 130px !important;
+ z-index: 6000 !important;
+}
+
+div#itemTransformersTogglePanel {
+ min-width: 1000px;
+}
+
+div#startAtContainer {
+ padding: 15px;
+}
+
+div#startAtContainer input {
+ background-color: rgba(200, 200, 200, 0.60) !important;
+}
+/**
+END - startAt
+*/
+
+/**
+START - Notifications
+*/
+/*Temporany fix diagonal stacking*/
+.k-popup.k-notification {
+ box-shadow: none;
+}
+
+.k-notification-error.k-group {
+ background: rgba(100% , 0% , 0% , .7);
+ color: red;
+}
+
+.errorNotification {
+ width: 300px;
+ height: 100px;
+ vertical-align: middle;
+ display: table-cell;
+}
+
+.errorNotification #level {
+ float: left;
+ padding-left: 10px;
+ font-size: 2em;
+ width: 2%;
+}
+
+.errorNotification #message {
+ float: right;
+ padding-left: 0px;
+ width:85%
+}
+
+.k-notification-success.k-group {
+ background: rgba(0% , 60% , 0% , .7);
+ color: #fff;
+}
+
+.successNotification {
+ width: 300px;
+ height: 100px;
+ vertical-align: middle;
+ display: table-cell;
+}
+
+.successNotification #level {
+ float: left;
+ padding-left: 10px;
+ font-size: 2em;
+ width: 2%;
+}
+
+.successNotification #message {
+ float: right;
+ padding-left: 0px;
+ width:85%
+}
+/**
+EN - Notifications
+*/
+
+/**
+START - Actions
+*/
+.actions > li > a {
+ padding: 5px 0px 5px 0px !important;
+}
+
+div.listview-actions a {
+ float:left;
+ padding: 5px 0px 5px 0px !important;
+}
+
+.action a.btn {
+ padding: 0px;
+}
+
+.btn-circle, .circular-actions a {
+ border-radius: 15px !important;
+ font-size: 12px;
+ height: 30px;
+ line-height: 1.42857;
+ padding: 6px 0;
+ text-align: center;
+ width: 30px;
+}
+
+.circular-actions a.btn {
+ background-color: #3c8dbc;
+ border-color: #367fa9;
+ color: #fff;
+}
+
+.btn-circle i, .circular-actions a i {
+ margin: 0px;
+}
+
+.multipanel-btn-minus {
+ padding: 0px 0px 0px 6px;
+ border: 0 none !important;
+}
+
+.multipanel-btn-plus {
+ padding: 15px 0px 8px 6px;
+ border: 0 none !important;
+}
+
+.multipanel-box {
+ padding: 5px;
+ display: inline-table;
+ margin: 0px;
+}
+/**
+END - Actions
+*/
+
+/**
+START - DataTable
+*/
+.dataTable {
+ clear: both;
+}
+/**
+END - DataTable
+*/
+
+/**
+START - Result page
+*/
+.attribute {
+ padding: 0px 4px 0px 4px;
+}
+
+span.highlight .attribute label {
+ color : red;
+}
+/**
+END - Result page
+*/
+
+.navbar-nav > .user-menu > .dropdown-menu > li.user-header {
+ height: auto !important;
+ padding: 10px;
+ text-align: center;
+}
+
+.nav-tabs-custom > .nav-tabs > li.active {
+ border-top-color: #d2d6de !important;
+}
+
+.code-deletion {
+ background-color: #ffdddd;
+ border-color: #f1c0c0;
+}
+
+.code-addition {
+ background-color: #dbffdb;
+ border-color: #c1e9c1;
+}
+
+/**
+START - AjaxDateTimePicker
+*/
+.input-auto-width {
+ width: auto !important;
+}
+
+.icon-top-position {
+ top: 5px !important;
+}
+
+/**
+END - AjaxDateTimePicker
+*/
+
+/**
+START - Search - AjaxDateTimePicker
+*/
+.searchBox .col-xs-12{
+ padding-left: 0px !important;
+ padding-right: 0px !important;
+}
+
+.clause{
+ display: block;
+ line-height: 34px;
+ width: 100%;
+}
+
+.clause .operator{
+ width: 65px !important;
+}
+
+.clause .operator .checkbox{
+ margin: 0px !important;
+}
+
+.clause .field {
+ line-height: 34px;
+ float: left;
+ padding: 0 3px 0px 0px;
+ display: inline-block !important;
+}
+
+.clause .type{
+ width: 170px !important;
+}
+
+.clause .type button{
+ width: 170px !important;
+}
+
+.clause .property{
+ width: 300px;
+}
+
+.clause .property button{
+ width: 300px;
+}
+
+.clause .comparator{
+ width: 100px;
+}
+
+.clause .comparator button{
+ width: 100px !important;
+}
+
+.clause .value{
+ width: 220px;
+}
+
+.clause .date{
+ width: 160px;
+}
+
+.clause .hours{
+ width: 45px;
+}
+
+.clause .separator{
+ width: 20px;
+ padding-left: 12px;
+}
+
+.clause .action{
+ float: left;
+ padding: 0px 7px 0px;
+}
+
+.searchBox .input-group-addon:last-child{
+ border: 1px solid #ccc !important;
+}
+
+.searchBox .input-group{
+ margin-top: 1px;
+}
+
+.custom-autocomplete-box li.selected {
+ background-color: #eee;
+}
+
+/**
+END - Search - AjaxDateTimePicker
+*/
+/**
+START - Parameters Details
+*/
+div#parametersForm{
+ min-height: 220px;
+}
+/**
... 7763 lines suppressed ...