From f5022840148915c27fe9c797827c66245c6f7058 Mon Sep 17 00:00:00 2001 From: jensp Date: Sun, 14 May 2017 17:46:06 +0000 Subject: [PATCH] CCM NG/ccm-core: More work on the Vaadin prototype git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4735 8810af33-2d31-482b-a856-94f89814c4df --- .../admin/ui/ConfirmDiscardDialog.java | 2 + .../admin/ui/usersgroupsroles/UserEditor.java | 244 +++++++++++++++++- .../ui/usersgroupsroles/UsersGroupsRoles.java | 36 ++- .../ui/admin/AdminResources.properties | 5 +- .../ui/admin/AdminResources_de.properties | 3 + .../ui/admin/AdminResources_en.properties | 5 +- .../ui/admin/AdminResources_fr.properties | 5 +- 7 files changed, 271 insertions(+), 29 deletions(-) diff --git a/ccm-core/src/main/java/org/libreccm/admin/ui/ConfirmDiscardDialog.java b/ccm-core/src/main/java/org/libreccm/admin/ui/ConfirmDiscardDialog.java index 8183d7926..b6f145156 100644 --- a/ccm-core/src/main/java/org/libreccm/admin/ui/ConfirmDiscardDialog.java +++ b/ccm-core/src/main/java/org/libreccm/admin/ui/ConfirmDiscardDialog.java @@ -45,6 +45,8 @@ public class ConfirmDiscardDialog extends Window { + "used with itself."); } + setCaption(message); + final Label label = new Label(message); final ResourceBundle bundle = ResourceBundle diff --git a/ccm-core/src/main/java/org/libreccm/admin/ui/usersgroupsroles/UserEditor.java b/ccm-core/src/main/java/org/libreccm/admin/ui/usersgroupsroles/UserEditor.java index 07a4bf09a..bb57c9cf1 100644 --- a/ccm-core/src/main/java/org/libreccm/admin/ui/usersgroupsroles/UserEditor.java +++ b/ccm-core/src/main/java/org/libreccm/admin/ui/usersgroupsroles/UserEditor.java @@ -20,13 +20,14 @@ package org.libreccm.admin.ui.usersgroupsroles; import com.arsdigita.ui.admin.AdminUiConstants; +import com.vaadin.data.HasValue; import com.vaadin.data.provider.AbstractDataProvider; import com.vaadin.data.provider.Query; +import com.vaadin.server.UserError; import com.vaadin.ui.Button; import com.vaadin.ui.CheckBox; import com.vaadin.ui.FormLayout; import com.vaadin.ui.HorizontalLayout; -import com.vaadin.ui.ItemCaptionGenerator; import com.vaadin.ui.Panel; import com.vaadin.ui.PasswordField; import com.vaadin.ui.RadioButtonGroup; @@ -34,16 +35,24 @@ import com.vaadin.ui.TextField; import com.vaadin.ui.UI; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.Window; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.libreccm.admin.ui.ConfirmDiscardDialog; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.core.EmailAddress; import org.libreccm.core.UnexpectedErrorException; +import org.libreccm.security.ChallengeManager; import org.libreccm.security.User; import org.libreccm.security.UserManager; import org.libreccm.security.UserRepository; import java.util.Arrays; import java.util.ResourceBundle; +import java.util.regex.Pattern; import java.util.stream.Stream; +import javax.mail.MessagingException; + /** * * @author Jens Pelzetter @@ -52,17 +61,23 @@ public class UserEditor extends Window { private static final long serialVersionUID = 7024424532574023431L; + private static final Logger LOGGER = LogManager.getLogger(UserEditor.class); + private enum PasswordOptions { + DO_NOTHING, GENERATE_AND_SEND, - SET + SET, } + private final UsersGroupsRoles usersGroupsRoles; private final User user; private final UserRepository userRepo; private final UserManager userManager; + private boolean dataHasChanged = false; + private TextField userName; private TextField familyName; private TextField givenName; @@ -73,11 +88,13 @@ public class UserEditor extends Window { private CheckBox passwordResetRequired; private CheckBox banned; - public UserEditor(final UserRepository userRepo, + public UserEditor(final UsersGroupsRoles usersGroupsRoles, + final UserRepository userRepo, final UserManager userManager) { - + super("Create new user"); - + + this.usersGroupsRoles = usersGroupsRoles; user = null; this.userRepo = userRepo; this.userManager = userManager; @@ -86,12 +103,14 @@ public class UserEditor extends Window { } public UserEditor(final User user, + final UsersGroupsRoles usersGroupsRoles, final UserRepository userRepo, final UserManager userManager) { super(String.format("Edit user %s", user.getName())); - + this.user = user; + this.usersGroupsRoles = usersGroupsRoles; this.userRepo = userRepo; this.userManager = userManager; @@ -103,20 +122,27 @@ public class UserEditor extends Window { final ResourceBundle bundle = ResourceBundle .getBundle(AdminUiConstants.ADMIN_BUNDLE, UI.getCurrent().getLocale()); - + + final DataHasChangedListener dataHasChangedListener + = new DataHasChangedListener(); + userName = new TextField(bundle .getString("ui.admin.user_edit.username.label")); userName.setRequiredIndicatorVisible(true); + userName.addValueChangeListener(dataHasChangedListener); familyName = new TextField(bundle .getString("ui.admin.user_edit.familyname.label")); + familyName.addValueChangeListener(dataHasChangedListener); givenName = new TextField(bundle .getString("ui.admin.user_edit.givenname.label")); + givenName.addValueChangeListener(dataHasChangedListener); emailAddress = new TextField(bundle .getString("ui.admin.user_edit.emailAddress.label")); emailAddress.setRequiredIndicatorVisible(true); + givenName.addValueChangeListener(dataHasChangedListener); passwordOptions = new RadioButtonGroup( bundle.getString("ui.admin.user_edit.password_options.label"), @@ -131,13 +157,23 @@ public class UserEditor extends Window { @Override public int size(final Query query) { - return PasswordOptions.values().length; + if (user == null) { + return PasswordOptions.values().length - 1; + } else { + return PasswordOptions.values().length; + } } @Override public Stream fetch( final Query query) { - return Arrays.stream(PasswordOptions.values()); + if (user == null) { + return Arrays + .stream(PasswordOptions.values()) + .filter(option -> option != PasswordOptions.DO_NOTHING); + } else { + return Arrays.stream(PasswordOptions.values()); + } } }); @@ -150,6 +186,9 @@ public class UserEditor extends Window { case SET: return bundle.getString( "ui.admin.user_edit.password_options.set"); + case DO_NOTHING: + return bundle.getString( + "ui.admin.user_edit.password_options.do_nothing"); default: throw new UnexpectedErrorException(String.format( "Unexpected value '%s' for password options.", @@ -160,6 +199,7 @@ public class UserEditor extends Window { password = new PasswordField(bundle .getString("ui.admin.user_edit.password.label")); password.setRequiredIndicatorVisible(true); + password.addValueChangeListener(dataHasChangedListener); passwordConfirmation = new PasswordField(bundle .getString("ui.admin.user_set_password_confirm.label")); @@ -179,6 +219,12 @@ public class UserEditor extends Window { passwordConfirmation.setEnabled(true); passwordConfirmation.setVisible(true); break; + case DO_NOTHING: + password.setEnabled(false); + password.setVisible(false); + passwordConfirmation.setEnabled(false); + passwordConfirmation.setVisible(false); + break; default: throw new UnexpectedErrorException(String.format( "Unexpected value '%s' for password options.", @@ -188,11 +234,16 @@ public class UserEditor extends Window { passwordOptions.setValue(PasswordOptions.GENERATE_AND_SEND); + final CheckBoxHasChangedListener checkBoxHasChangedListener + = new CheckBoxHasChangedListener(); + passwordResetRequired = new CheckBox(bundle .getString("ui.admin.user_edit.password_reset_required.label")); + passwordResetRequired.addValueChangeListener(checkBoxHasChangedListener); banned = new CheckBox(bundle .getString("ui.admin.user_edit.banned.label")); + banned.addValueChangeListener(checkBoxHasChangedListener); if (user == null) { banned.setVisible(false); @@ -206,6 +257,7 @@ public class UserEditor extends Window { } else { submit.setCaption(bundle.getString("ui.admin.save")); } + submit.addClickListener(event -> saveUser()); final Button cancel = new Button(bundle.getString("ui.admin.cancel")); cancel.addClickListener(event -> close()); @@ -234,16 +286,182 @@ public class UserEditor extends Window { } setContent(panel); + + if (user != null) { + userName.setValue(user.getName()); + givenName.setValue(user.getGivenName()); + familyName.setValue(user.getFamilyName()); + emailAddress.setValue(user.getPrimaryEmailAddress().getAddress()); + passwordResetRequired.setValue(user.isPasswordResetRequired()); + banned.setValue(user.isBanned()); + passwordOptions.setValue(PasswordOptions.DO_NOTHING); + } } - + @Override public void close() { - - final ConfirmDiscardDialog dialog = new ConfirmDiscardDialog( + + if (dataHasChanged) { + final ConfirmDiscardDialog dialog = new ConfirmDiscardDialog( this, "Are you sure to discard the changes made this user?"); dialog.setModal(true); UI.getCurrent().addWindow(dialog); - + } else { + super.close(); + } + } + + protected void saveUser() { + + final ResourceBundle bundle = ResourceBundle + .getBundle(AdminUiConstants.ADMIN_BUNDLE, + UI.getCurrent().getLocale()); + + boolean valid = true; + + if (userName.getValue() == null + || userName.getValue().trim().isEmpty()) { + userName.setComponentError(new UserError(bundle + .getString("ui.admin.user_edit.username.error.not_empty"))); + valid = false; + } + if (emailAddress.getValue() == null + || emailAddress.getValue().trim().isEmpty()) { + emailAddress.setComponentError(new UserError(bundle + .getString("ui.admin.user.email_form.address.not_empty"))); + valid = false; + } + + if ((user == null || !user.getName().equals(userName.getValue())) + && userRepo.isNameInUse(userName.getValue())) { + + userName.setComponentError(new UserError(bundle + .getString("ui.admin.new_user_form.error" + + ".username_already_in_use"))); + valid = false; + } else { + userName.setComponentError(null); + } + + if ((user == null + || !user.getPrimaryEmailAddress().getAddress().equals( + emailAddress.getValue())) + && userRepo.isEmailAddressInUse(emailAddress.getValue())) { + + emailAddress.setComponentError(new UserError(bundle + .getString("ui.admin.new_user_form.error.email_already_in_use"))); + valid = false; + } else { + if (!Pattern.matches( + "^[^@<>\"\\t ]+@[^@<>\".\\t]+([.][^@<>\".\\n ]+)+$", + emailAddress.getValue().trim())) { + emailAddress.setComponentError(new UserError(bundle + .getString("ui.admin.user_form.email_malformed"))); + valid = false; + } else { + emailAddress.setComponentError(null); + } + } + + if (passwordOptions.getValue() == PasswordOptions.SET) { + if (password.getValue() == null + || password.getValue().trim().isEmpty()) { + password.setComponentError(new UserError(bundle + .getString( + "ui.admin.set_password.new_password.error.not_empty"))); + } + + if (!password.getValue().equals(passwordConfirmation.getValue())) { + passwordConfirmation.setComponentError(new UserError(bundle + .getString("ui.admin.user_set_password.error.do_not_match"))); + } + } + + if (!valid) { + return; + } + + final User currentUser; + if (user == null) { + + final User newUser = userManager.createUser(givenName.getValue(), + familyName.getValue(), + userName.getValue(), + emailAddress.getValue(), + passwordConfirmation + .getValue()); + + newUser.setPasswordResetRequired(passwordResetRequired.getValue()); + newUser.setBanned(banned.getValue()); + currentUser = newUser; + + userRepo.save(newUser); + } else { + user.setName(userName.getValue().trim()); + user.setGivenName(givenName.getValue().trim()); + user.setFamilyName(familyName.getValue().trim()); + final EmailAddress email = user.getPrimaryEmailAddress(); + if (!email.getAddress().equals(emailAddress.getValue())) { + email.setAddress(emailAddress.getValue()); + } + user.setPasswordResetRequired(passwordResetRequired.getValue()); + user.setBanned(banned.getValue()); + + userRepo.save(user); + currentUser = user; + } + + switch (passwordOptions.getValue()) { + case GENERATE_AND_SEND: { + userManager.updatePassword(currentUser, null); + final ChallengeManager challengeManager = CdiUtil + .createCdiUtil() + .findBean(ChallengeManager.class); + try { + challengeManager.sendPasswordRecover(currentUser); + } catch (MessagingException ex) { + setComponentError(new UserError(bundle + .getString("ui.admin.user_form" + + ".failed_to_send_password_challenge"))); + LOGGER.error( + "Failed to send password challenge.", + ex); + } + break; + } + case SET: + userManager.updatePassword(currentUser, + passwordConfirmation.getValue()); + break; + } + + dataHasChanged = false; + usersGroupsRoles.refreshUsers(); + close(); + } + + private class DataHasChangedListener implements + HasValue.ValueChangeListener { + + private static final long serialVersionUID = -4698658552890778877L; + + @Override + public void valueChange(final HasValue.ValueChangeEvent event) { + dataHasChanged = true; + } + + } + + private class CheckBoxHasChangedListener implements + HasValue.ValueChangeListener { + + private static final long serialVersionUID = 1986372149566327203L; + + @Override + public void valueChange(final HasValue.ValueChangeEvent event) { + dataHasChanged = true; + } + } } diff --git a/ccm-core/src/main/java/org/libreccm/admin/ui/usersgroupsroles/UsersGroupsRoles.java b/ccm-core/src/main/java/org/libreccm/admin/ui/usersgroupsroles/UsersGroupsRoles.java index 10d985e85..76fe06860 100644 --- a/ccm-core/src/main/java/org/libreccm/admin/ui/usersgroupsroles/UsersGroupsRoles.java +++ b/ccm-core/src/main/java/org/libreccm/admin/ui/usersgroupsroles/UsersGroupsRoles.java @@ -20,6 +20,7 @@ package org.libreccm.admin.ui.usersgroupsroles; import com.arsdigita.ui.admin.AdminUiConstants; +import com.vaadin.icons.VaadinIcons; import com.vaadin.ui.Button; import com.vaadin.ui.CustomComponent; import com.vaadin.ui.Grid; @@ -27,12 +28,14 @@ import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.TabSheet; import com.vaadin.ui.TextField; import com.vaadin.ui.UI; +import com.vaadin.ui.Window; import com.vaadin.ui.components.grid.HeaderCell; import com.vaadin.ui.components.grid.HeaderRow; import com.vaadin.ui.renderers.ButtonRenderer; import com.vaadin.ui.themes.ValoTheme; import org.libreccm.admin.ui.AdminView; import org.libreccm.security.User; +import org.libreccm.security.UserRepository; import java.util.ResourceBundle; @@ -61,6 +64,7 @@ public class UsersGroupsRoles extends CustomComponent { private final Grid usersTable; private final TextField userNameFilter; private final Button clearFiltersButton; + private final Button createUserButton; private UsersTableDataProvider usersTableDataProvider; @@ -118,23 +122,13 @@ public class UsersGroupsRoles extends CustomComponent { new ButtonRenderer<>(event -> { final UserEditor editor = new UserEditor( event.getItem(), + this, view.getUserRepository(), view.getUserManager()); editor.center(); UI.getCurrent().addWindow(editor); })) .setId(COL_EDIT); - usersTable - .addColumn(user -> bundle.getString("ui.admin.users.table.delete"), - new ButtonRenderer<>(event -> { - final UserEditor editor = new UserEditor( - event.getItem(), - view.getUserRepository(), - view.getUserManager()); - editor.center(); - UI.getCurrent().addWindow(editor); - })) - .setId(COL_DELETE); final HeaderRow filterRow = usersTable.appendHeaderRow(); final HeaderCell userNameFilterCell = filterRow.getCell(COL_USER_NAME); @@ -161,8 +155,20 @@ public class UsersGroupsRoles extends CustomComponent { // usersTableDataProvider.setUserNameFilter(null); userNameFilter.setValue(""); }); + createUserButton = new Button("New User"); + createUserButton.addStyleName(ValoTheme.BUTTON_TINY); + createUserButton.setIcon(VaadinIcons.PLUS); + createUserButton.addClickListener(event -> { + final UserEditor userEditor = new UserEditor( + this, + view.getUserRepository(), + view.getUserManager()); + userEditor.center(); + UI.getCurrent().addWindow(userEditor); + }); final HorizontalLayout actionsLayout = new HorizontalLayout( - clearFiltersButton); + clearFiltersButton, + createUserButton); actionsCell.setComponent(actionsLayout); tabSheet.addTab(usersTable, "Users"); @@ -214,5 +220,9 @@ public class UsersGroupsRoles extends CustomComponent { this.usersTableDataProvider = dataProvider; usersTable.setDataProvider(dataProvider); } - + + protected void refreshUsers() { + usersTableDataProvider.refreshAll(); + } + } diff --git a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources.properties b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources.properties index 0378c3791..3933e2b3c 100644 --- a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources.properties +++ b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources.properties @@ -225,7 +225,7 @@ ui.admin.new_user_form.password_options.set_password.password.label=Password ui.admin.new_user_form.password_options.set_password.password_confirmation.label=Confirm password ui.admin.new_user_form.password_options.send_password.label=Send password ui.admin.new_user_form.error.username_already_in_use=The provided user name is already in use. -ui.admin.new_user_form.error.email_already_in_use=The provided email address is already assigned with an user account. +ui.admin.new_user_form.error.email_already_in_use=The provided email address is already associated with an user account. ui.admin.new_user_form.error.password_do_not_match=Passwords do not match. ui.admin.new_user_form.error.failed_to_send_password=Failed to send password to new user. ui.admin.new_user_form.password_options.set_password=Set password @@ -574,3 +574,6 @@ ui.admin.user_edit.password_options.generate_and_send=Generate and send to user ui.admin.user_edit.password_options.set=Set password ui.admin.yes=Yes ui.admin.no=No +ui.admin.user_form.email_malformed=This email address is malformed. +ui.admin.user_edit.password_options.do_nothing=Do not change password +ui.admin.user_form.failed_to_send_password_challenge=Failed to send password challenge to user. diff --git a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_de.properties b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_de.properties index 03969e056..f7ac25f28 100644 --- a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_de.properties +++ b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_de.properties @@ -578,3 +578,6 @@ ui.admin.user_edit.password_options.generate_and_send=Generieren und an Benutzer ui.admin.user_edit.password_options.set=Passwort setzen ui.admin.yes=Ja ui.admin.no=Nein +ui.admin.user_form.email_malformed=E-Mail Adresse ist fehlerhaft. +ui.admin.user_edit.password_options.do_nothing=Password nicht ver\u00e4ndern +ui.admin.user_form.failed_to_send_password_challenge=Beim Senden des Passwortes ist ein Fehler aufgetreten. diff --git a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_en.properties b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_en.properties index 2ced8d20a..d1b045161 100755 --- a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_en.properties +++ b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_en.properties @@ -198,7 +198,7 @@ ui.admin.new_user_form.password_options.set_password.password.label=Password ui.admin.new_user_form.password_options.set_password.password_confirmation.label=Confirm password ui.admin.new_user_form.password_options.send_password.label=Send password ui.admin.new_user_form.error.username_already_in_use=The provided user name is already in use. -ui.admin.new_user_form.error.email_already_in_use=The provided email address is already assigned with an user account. +ui.admin.new_user_form.error.email_already_in_use=The provided email address is already associated with an user account. ui.admin.new_user_form.error.password_do_not_match=Passwords do not match. ui.admin.new_user_form.error.failed_to_send_password=Failed to send password to new user. ui.admin.new_user_form.password_options.set_password=Set password @@ -571,3 +571,6 @@ ui.admin.user_edit.password_options.generate_and_send=Generate and send to user ui.admin.user_edit.password_options.set=Set password ui.admin.yes=Yes ui.admin.no=No +ui.admin.user_form.email_malformed=This email address is malformed. +ui.admin.user_edit.password_options.do_nothing=Do not change password +ui.admin.user_form.failed_to_send_password_challenge=Failed to send password challenge to user. diff --git a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_fr.properties b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_fr.properties index 18deb5867..3539f8352 100755 --- a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_fr.properties +++ b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_fr.properties @@ -189,7 +189,7 @@ ui.admin.new_user_form.password_options.set_password.password.label=Password ui.admin.new_user_form.password_options.set_password.password_confirmation.label=Confirm password ui.admin.new_user_form.password_options.send_password.label=Send password ui.admin.new_user_form.error.username_already_in_use=The provided user name is already in use. -ui.admin.new_user_form.error.email_already_in_use=The provided email address is already assigned with an user account. +ui.admin.new_user_form.error.email_already_in_use=The provided email address is already associated with an user account. ui.admin.new_user_form.error.password_do_not_match=Passwords do not match. ui.admin.new_user_form.error.failed_to_send_password=Failed to send password to new user. ui.admin.new_user_form.password_options.set_password=Set password @@ -562,3 +562,6 @@ ui.admin.user_edit.password_options.generate_and_send=Generate and send to user ui.admin.user_edit.password_options.set=Set password ui.admin.yes=Yes ui.admin.no=No +ui.admin.user_form.email_malformed=This email address is malformed. +ui.admin.user_edit.password_options.do_nothing=Do not change password +ui.admin.user_form.failed_to_send_password_challenge=Failed to send password challenge to user.