Java tutorial
/* * Copyright (c) Interactive Information R & D (I2RD) LLC. * All Rights Reserved. * * This software is confidential and proprietary information of * I2RD LLC ("Confidential Information"). You shall not disclose * such Confidential Information and shall use it only in * accordance with the terms of the license agreement you entered * into with I2RD. */ package com.example.app.profile.ui.user; import com.example.app.profile.model.Profile; import com.example.app.profile.model.ProfileDAO; import com.example.app.profile.model.membership.Membership; import com.example.app.profile.model.membership.MembershipTypeProvider; import com.example.app.profile.model.user.ContactMethod; import com.example.app.profile.model.user.User; import com.example.app.profile.model.user.UserDAO; import com.example.app.profile.service.MembershipOperationProvider; import com.example.app.profile.service.SelectedCompanyTermProvider; import com.example.app.support.service.AppUtil; import com.example.app.support.service.ContactUtil; import com.example.app.support.ui.UIPreferences; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Configurable; import javax.annotation.Nonnull; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.TimeZone; import java.util.prefs.Preferences; import com.i2rd.hr.miwt.AddressCellRenderer; import com.i2rd.hr.miwt.NameCellRenderer; import net.proteusframework.cms.support.ImageFileUtil; import net.proteusframework.core.StringFactory; import net.proteusframework.core.hibernate.dao.EntityRetriever; import net.proteusframework.core.html.HTMLElement; import net.proteusframework.core.locale.LocalizedObjectKey; import net.proteusframework.core.locale.TextSources; import net.proteusframework.core.locale.annotation.I18N; import net.proteusframework.core.locale.annotation.I18NFile; import net.proteusframework.core.locale.annotation.L10N; import net.proteusframework.core.metric.Dimension; import net.proteusframework.core.metric.PixelMetric; import net.proteusframework.core.notification.Notifiable; import net.proteusframework.core.notification.Notification; import net.proteusframework.core.notification.NotificationImpl; import net.proteusframework.internet.http.Link; import net.proteusframework.ui.miwt.Image; import net.proteusframework.ui.miwt.component.Component; import net.proteusframework.ui.miwt.component.Container; import net.proteusframework.ui.miwt.component.Dialog; import net.proteusframework.ui.miwt.component.Field; import net.proteusframework.ui.miwt.component.ImageComponent; import net.proteusframework.ui.miwt.component.PushButton; import net.proteusframework.ui.miwt.component.Window; import net.proteusframework.ui.miwt.component.composite.CustomCellRenderer; import net.proteusframework.ui.miwt.component.composite.MessageContainer; import net.proteusframework.ui.miwt.component.composite.editor.CheckboxValueEditor; import net.proteusframework.ui.miwt.component.composite.editor.ComboBoxValueEditor; import net.proteusframework.ui.miwt.component.composite.editor.TextEditor; import net.proteusframework.ui.miwt.event.ActionListener; import net.proteusframework.ui.miwt.util.CommonActions; import net.proteusframework.ui.miwt.util.CommonButtonText; import net.proteusframework.ui.miwt.validation.CompositeValidator; import net.proteusframework.ui.miwt.validation.Validator; import net.proteusframework.users.model.Address; import net.proteusframework.users.model.ContactDataCategory; import net.proteusframework.users.model.EmailAddress; import net.proteusframework.users.model.PhoneNumber; import net.proteusframework.users.model.dao.NonUniqueCredentialsException; import net.proteusframework.users.model.dao.PrincipalDAO; import static com.example.app.profile.ui.user.UserValueViewerLOK.*; import static com.example.app.support.service.AppUtil.nullFirst; import static net.proteusframework.core.notification.NotificationImpl.error; import static net.proteusframework.core.notification.NotificationType.INFO; import static net.proteusframework.core.validation.CommonValidationText.ARG0_IS_REQUIRED; import static net.proteusframework.ui.miwt.validation.RequiredValueValidator.createRequiredValueValidator; /** * Value Viewer for {@link User} * * @author Alan Holt (aholt@venturetech.net) * @since 12/7/15 3:49 PM */ @I18NFile(symbolPrefix = "com.example.app.profile.ui.user.UserValueViewer", i18n = { @I18N(symbol = "Label Name", l10n = @L10N("Name")), @I18N(symbol = "Label Address", l10n = @L10N("Address")), @I18N(symbol = "Label Email", l10n = @L10N("Email/Username")), @I18N(symbol = "Label Phone", l10n = @L10N("Phone Number")), @I18N(symbol = "Label SMS Phone", l10n = @L10N("SMS Phone Number")), @I18N(symbol = "Label Update Password", l10n = @L10N("Update Password")), @I18N(symbol = "Label New Password", l10n = @L10N("New Password")), @I18N(symbol = "Label Preferred Time Zone", l10n = @L10N("Preferred Time Zone")), @I18N(symbol = "Label Confirm Password", l10n = @L10N("Confirm New Password")), @I18N(symbol = "Error Message Passwords Not Match", l10n = @L10N("Passwords do not match.")), @I18N(symbol = "Error Message Error Occurred Updating Password", l10n = @L10N("An error occurred updating password, please contact support.")), @I18N(symbol = "Message Password Updated", l10n = @L10N("Password successfully updated.")), @I18N(symbol = "Label Contact Preferences", l10n = @L10N("Notification Preferences")), @I18N(symbol = "Label Send Notification to PhoneSms", l10n = @L10N("Send notifications to your SMS Phone Number.")), @I18N(symbol = "Label Login Landing Page", l10n = @L10N("Login Landing Page")) }) @Configurable public class UserValueViewer extends Container { /** Logger. */ private static final Logger _logger = LogManager.getLogger(UserValueViewer.class); private static final int MAX_HEIGHT_PROFILE_PIC_VIEW = 200; private static class ConfirmPasswordValidator implements Validator { private final TextEditor _newPassword; private final TextEditor _confirmPassword; public ConfirmPasswordValidator(TextEditor newPassword, TextEditor confirmPassword) { _newPassword = newPassword; _confirmPassword = confirmPassword; } @Override public boolean isComponentValidationSupported(Component component) { return true; } @Override public boolean validate(Component component, Notifiable notifiable) { if (!Objects.equals(_newPassword.getUIValue(), _confirmPassword.getUIValue())) { NotificationImpl error = error(ERROR_MESSAGE_PASSWORDS_NOT_MATCH()); error.setSource(_confirmPassword); notifiable.sendNotification(error); return false; } return true; } } @Autowired private AppUtil _appUtil; @Autowired private EntityRetriever _er; @Autowired private UserDAO _userDAO; @Autowired private ProfileDAO _profileDAO; @Autowired private MembershipOperationProvider _mop; @Autowired private PrincipalDAO _principalDAO; @Autowired private SelectedCompanyTermProvider _terms; @Autowired private MembershipTypeProvider _mtp; @Autowired private UIPreferences _uiPreferences; private boolean _adminMode = true; private User _user; private Notifiable _notifiable; /** * Instantiate a new instance of UserValueViewer * <br> * NOTE: Must set User with {@link #setUser(User)} before initialization. */ public UserValueViewer() { super(); } /** * Displays a dialog for the current user to update the password for the user being viewed */ public void beginUpdatePassword() { final Dialog dlg = new Dialog(getApplication(), LABEL_UPDATE_PASSWORD()); dlg.addClassName("update-password-dialog"); final MessageContainer dlgMessages = new MessageContainer(35_000L); final TextEditor newPassword = new TextEditor(LABEL_NEW_PASSWORD(), null); newPassword.setInputType(Field.InputType.password); newPassword.setRequiredValueValidator(); final TextEditor confirmPassword = new TextEditor(LABEL_CONFIRM_PASSWORD(), null); confirmPassword.setInputType(Field.InputType.password); confirmPassword.setValueValidator(new CompositeValidator( createRequiredValueValidator(ARG0_IS_REQUIRED, confirmPassword.getLabel().getText()) .withNotificationSourceSetter( (validator, component, notification) -> notification.setSource(confirmPassword)), new ConfirmPasswordValidator(newPassword, confirmPassword))); final PushButton save = CommonActions.SAVE.push(); final PushButton cancel = CommonActions.CANCEL.push(); ActionListener cancelAction = new Window.Closer(); cancel.addActionListener(cancelAction); save.addActionListener(ev -> { dlgMessages.clearNotifications(); boolean valid = newPassword.validateUIValue(dlgMessages); valid = confirmPassword.validateUIValue(dlgMessages) && valid; if (valid) { List<Notification> notifications = new ArrayList<>(); try { boolean success = _principalDAO.setNewPassword(getUser().getPrincipal(), getUser().getPrincipal().getUsername(), notifications, newPassword.commitValue()); if (!success) { notifications.forEach(notification -> { notification.setSource(newPassword); dlgMessages.sendNotification(notification); }); } else { getNotifiable().sendNotification(new NotificationImpl(INFO, MESSAGE_PASSWORD_UPDATED())); cancelAction.actionPerformed(ev); } } catch (NonUniqueCredentialsException e) { _logger.error("Unable to update Password", e); dlgMessages.sendNotification(error(ERROR_MESSAGE_ERROR_OCCURRED_UPDATING_PASSWORD())); } } }); dlg.add(of(HTMLElement.section, "prop-editor prop-wrapper", dlgMessages, of("prop-body", newPassword, confirmPassword), of("prop-footer", of("prop-footer-actions", of("persistence-actions actions bottom", save, cancel))))); getWindowManager().add(dlg); dlg.setVisible(true); } /** * Get the User to view * * @return the User to view */ @Nonnull public User getUser() { return Optional.ofNullable(_er.reattachIfNecessary(_user)) .orElseThrow(() -> new IllegalStateException("User has not been set, so it cannot be retrieved.")); } /** * Get the Notifiable for this UserValueViewer * If one has not been set, a warning is logged. * * @return the Notifiable */ @Nonnull public Notifiable getNotifiable() { if (_notifiable == null) { _logger.warn( "Notifiable has not been set yet, any messages sent to this returned value will not show up on the UI."); return new MessageContainer(35_000L); } return _notifiable; } /** * Set the User to view * * @param user the User to view */ public void setUser(@Nonnull User user) { _user = user; if (isInited()) { setupUI(); } } /** * Set the Notifiable for this UserValueViewer * * @param notifiable the Notifiable */ public void setNotifiable(@Nonnull Notifiable notifiable) { _notifiable = notifiable; } /** * Get boolean flag. If true, the current user has the right permissions/roles to update the viewed User's password * * @return boolean flag */ public boolean canUpdatePassword() { User currentUser = _userDAO.getAssertedCurrentUser(); final TimeZone timeZone = getSession().getTimeZone(); Profile profile = _uiPreferences.getSelectedCompany(); return _profileDAO.canOperate(currentUser, profile, timeZone, _mop.changeUserPassword()) || Objects.equals(currentUser.getId(), getUser().getId()); } @Override public void init() { super.init(); setupUI(); } /** * Get boolean flag. If true, this viewer is being displayed in admin mode * <br> * defaults to true. * * @return boolean flag */ public boolean isAdminMode() { return _adminMode; } /** * Set boolean flag. If true, this viewer is being displayed in admin mode * * @param adminMode boolean flag */ public void setAdminMode(boolean adminMode) { _adminMode = adminMode; } private void setupUI() { removeAllComponents(); User currentUser = _userDAO.getAssertedCurrentUser(); Profile profile = _uiPreferences.getSelectedCompany(); final ImageComponent userImage = new ImageComponent(); if (getUser().getImage() != null) { try { final Dimension size = ImageFileUtil.getDimension(getUser().getImage(), true); // We are showing a max height of 200px if (size != null && size.getHeightMetric().intValue() < MAX_HEIGHT_PROFILE_PIC_VIEW) userImage.setSize(size); else userImage.setHeight(new PixelMetric(MAX_HEIGHT_PROFILE_PIC_VIEW)); } catch (IOException e) { _logger.debug("Unable to get file dimension.", e); } userImage.setImage(new Image(getUser().getImage())); } else userImage.setImage(new Image(_appUtil.getDefaultUserImage())); userImage.addClassName("image"); final Container userImageField = of("prop picture", userImage); final TextEditor nameField = new TextEditor(LABEL_NAME(), NameCellRenderer.getFormattedString(getUser().getPrincipal().getContact().getName())); nameField.setEditable(false); setFieldVisibility(nameField); nameField.addClassName("name"); Address address = ContactUtil .getAddress(getUser().getPrincipal().getContact(), ContactDataCategory.values()) .orElse(new Address()); final Container addressContainer = Container.of("prop address", LABEL_ADDRESS(), new AddressCellRenderer(address)); final TextEditor emailField = new TextEditor(LABEL_EMAIL(), ContactUtil.getEmailAddress(getUser().getPrincipal().getContact(), ContactDataCategory.values()) .map(EmailAddress::getEmail).orElse("")); emailField.setEditable(false); setFieldVisibility(emailField); emailField.addClassName("email"); final TextEditor phoneField = new TextEditor(LABEL_PHONE(), ContactUtil.getPhoneNumber(getUser().getPrincipal().getContact(), ContactDataCategory.values()) .map(PhoneNumber::toExternalForm).orElse("")); phoneField.setEditable(false); setFieldVisibility(phoneField); phoneField.addClassName("phone"); final TextEditor smsPhoneField = new TextEditor(LABEL_SMS_PHONE(), Optional.ofNullable(getUser().getSmsPhone()).map(PhoneNumber::toExternalForm).orElse("")); smsPhoneField.setEditable(false); setFieldVisibility(smsPhoneField); smsPhoneField.addClassName("sms-phone"); final TextEditor timeZoneField = new TextEditor(LABEL_PREFERRED_TIME_ZONE(), Optional.ofNullable(getUser().getPrincipal().getContact().getPreferredTimeZone()) // .map(tz -> tz.getDisplayName(false, TimeZone.LONG, getLocaleContext().getLocale())) .map(TimeZone::getID).orElse("")); timeZoneField.setEditable(false); setFieldVisibility(timeZoneField); timeZoneField.addClassName("time-zone"); final ComboBoxValueEditor<Profile> coachingField = new ComboBoxValueEditor<>(_terms.company(), Collections.singletonList(profile), profile); coachingField.setEditable(false); coachingField.addClassName("coaching"); final CheckboxValueEditor<LocalizedObjectKey> contactPrefsField = new CheckboxValueEditor<>( LABEL_CONTACT_PREFERENCES(), Collections.singleton(LABEL_SEND_NOTIFICATION_TO_PHONESMS()), null); contactPrefsField.getLabel().withHTMLElement(HTMLElement.h3); contactPrefsField.withHTMLElement(HTMLElement.section); contactPrefsField.setEditable(false); contactPrefsField.setVisible(Objects.equals(currentUser.getId(), getUser().getId())); if (getUser().getPreferredContactMethod() == ContactMethod.PhoneSms) contactPrefsField.setValue(Collections.singleton(LABEL_SEND_NOTIFICATION_TO_PHONESMS())); List<Link> links = LoginLandingLinks.getAvailableLinks(getUser(), getLocaleContext()); links = nullFirst(links); final ComboBoxValueEditor<Link> loginLangingPage = new ComboBoxValueEditor<>(LABEL_LOGIN_LANDING_PAGE(), links, null); //This field is available when the current user is the user being viewed // AND only to the user who has a particular membership under the company. loginLangingPage.setVisible(Objects.equals(currentUser.getId(), getUser().getId()) && _profileDAO.getMembershipsForUser(getUser(), null, getSession().getTimeZone()).stream() .map(Membership::getMembershipType).filter(membershipType1 -> membershipType1 != null) .anyMatch(membershipType -> membershipType.equals(_mtp.companyAdmin()))); loginLangingPage.setCellRenderer(new CustomCellRenderer(CommonButtonText.PLEASE_SELECT, input -> { Link link = (Link) input; return TextSources.createText(link.getAdditionalAttributes().get("label")); })); final Preferences userPref = Preferences.userRoot().node(User.LOGIN_PREF_NODE); final String uri = userPref != null ? userPref.get(User.LOGIN_PREF_NODE_LANDING_PAGE, null) : null; for (Link l : links) { if (l != null && l.getURIAsString().equals(uri)) { loginLangingPage.setValue(l); break; } } loginLangingPage.getLabel().withHTMLElement(HTMLElement.h3); loginLangingPage.withHTMLElement(HTMLElement.section); loginLangingPage.setEditable(false); add(userImageField); add(nameField); add(addressContainer); add(emailField); add(phoneField); if (getUser().getSmsPhone() != null) add(smsPhoneField); add(timeZoneField); if (isAdminMode()) { add(coachingField); } add(contactPrefsField); add(loginLangingPage); } private static void setFieldVisibility(TextEditor field) { field.setVisible(!StringFactory.isEmptyString(field.getValue())); } }