controllers.admin.UserManager.java Source code

Java tutorial

Introduction

Here is the source code for controllers.admin.UserManager.java

Source

/*! LICENSE
 *
 * Copyright (c) 2015, The Agile Factory SA and/or its affiliates. All rights
 * reserved.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package controllers.admin;

import java.io.ByteArrayInputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.inject.Inject;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.RandomStringUtils;

import be.objectify.deadbolt.core.models.Role;
import be.objectify.deadbolt.java.actions.Group;
import be.objectify.deadbolt.java.actions.Restrict;
import constants.IMafConstants;
import controllers.ControllersUtils;
import dao.pmo.ActorDao;
import framework.commons.IFrameworkConstants;
import framework.services.account.AccountManagementException;
import framework.services.account.IAccountManagerPlugin;
import framework.services.account.IPreferenceManagerPlugin;
import framework.services.account.IUserAccount;
import framework.services.account.IUserAccount.AccountType;
import framework.services.configuration.II18nMessagesPlugin;
import framework.services.email.IEmailService;
import framework.services.notification.INotificationManagerPlugin;
import framework.services.session.IUserSessionManagerPlugin;
import framework.services.storage.IPersonalStoragePlugin;
import framework.services.system.ISysAdminUtils;
import framework.utils.IColumnFormatter;
import framework.utils.ISelectableValueHolder;
import framework.utils.Msg;
import framework.utils.Table;
import framework.utils.Table.ColumnDef.SorterType;
import framework.utils.TableExcelRenderer;
import framework.utils.Utilities;
import models.framework_models.account.NotificationCategory;
import models.framework_models.account.NotificationCategory.Code;
import models.framework_models.account.SystemLevelRoleType;
import models.framework_models.parent.IModelConstants;
import models.pmo.Actor;
import play.Configuration;
import play.Logger;
import play.data.DynamicForm;
import play.data.Form;
import play.data.validation.Constraints.MaxLength;
import play.data.validation.Constraints.MinLength;
import play.data.validation.Constraints.Pattern;
import play.data.validation.Constraints.Required;
import play.mvc.Controller;
import play.mvc.Result;
import scala.concurrent.duration.Duration;
import services.licensesmanagement.ILicensesManagementService;

/**
 * The administration interface which is to be used to create, update, delete
 * (or unactivate) a user.
 * 
 * @author Pierre-Yves Cloux
 * 
 */
@Restrict({ @Group(IMafConstants.ADMIN_USER_ADMINISTRATION_PERMISSION) })
public class UserManager extends Controller {
    @Inject
    private IAccountManagerPlugin accountManagerPlugin;
    @Inject
    private ILicensesManagementService licensesManagementService;
    @Inject
    private INotificationManagerPlugin notificationManagerPlugin;
    @Inject
    private IPersonalStoragePlugin personalStoragePlugin;
    @Inject
    private IUserSessionManagerPlugin userSessionManagerPlugin;
    @Inject
    private ISysAdminUtils sysAdminUtils;
    @Inject
    private IPreferenceManagerPlugin preferenceManagerPlugin;
    @Inject
    private II18nMessagesPlugin i18nMessagesPlugin;
    @Inject
    private Configuration configuration;
    @Inject
    private IEmailService emailService;

    private static Logger.ALogger log = Logger.of(UserManager.class);
    private static Form<UserSeachFormData> userSearchForm = Form.form(UserSeachFormData.class);
    private static Form<UserAccountFormData> basicDataUpdateForm = Form.form(UserAccountFormData.class,
            UserAccountFormData.BasicDataChangeGroup.class);
    private static Form<UserAccountFormData> rolesUpdateForm = Form.form(UserAccountFormData.class,
            UserAccountFormData.RolesChangeGroup.class);
    private static Form<UserAccountFormData> mailUpdateForm = Form.form(UserAccountFormData.class,
            UserAccountFormData.EmailChangeGroup.class);
    private static Form<UserAccountFormData> creationForm = Form.form(UserAccountFormData.class,
            UserAccountFormData.UserCreationGroup.class);
    private static Form<UserAccountFormData> changeAccountTypeForm = Form.form(UserAccountFormData.class,
            UserAccountFormData.AccountTypeChangeGroup.class);
    private static Form<SelectActorForUserFormData> selectActorForUserFormData = Form
            .form(SelectActorForUserFormData.class);

    // Options to be used with the search form (radio buttons)
    public static final String UID_SEARCH_OPTION = "uid";
    public static final String MAIL_SEARCH_OPTION = "mail";
    public static final String FULLNAME_SEARCH_OPTION = "fullName";

    // Table to display search results
    private static Table<IUserAccount> tableTemplate = new Table<IUserAccount>() {
        {
            this.addColumn("firstName", "firstName", "object.user_account.first_name.label", SorterType.NONE);
            this.addColumn("lastName", "lastName", "object.user_account.last_name.label", SorterType.NONE);
            this.addColumn("mail", "mail", "object.user_account.email.label", SorterType.NONE);
            this.addColumn("accountType", "accountType", "object.user_account.type.label", SorterType.NONE);
            this.setJavaColumnFormatter("accountType", new IColumnFormatter<IUserAccount>() {
                @Override
                public String apply(IUserAccount userAccount, Object value) {
                    return userAccount.getAccountType().getLabel();
                }
            });

            this.setLineAction(new IColumnFormatter<IUserAccount>() {
                @Override
                public String apply(IUserAccount userAccount, Object value) {
                    return routes.UserManager.displayUser(userAccount.getUid()).url();
                }
            });

            this.setIdFieldName("uid");
        }
    };

    /**
     * Display the form to be used to search a user to be later modified.
     */
    public Result displayUserSearchForm() {
        Form<UserSeachFormData> userSearchFormLoaded = userSearchForm.fill(new UserSeachFormData());
        return ok(views.html.admin.usermanager.usermanager_search.render(userSearchFormLoaded));
    }

    /**
     * Find a user using either a UID or a mail address.<br/>
     * The option is provided as a parameter of the form.
     */
    public Result findUser() {
        Form<UserSeachFormData> boundForm = userSearchForm.bindFromRequest();
        if (boundForm.hasErrors()) {
            return badRequest(views.html.admin.usermanager.usermanager_search.render(boundForm));
        }

        UserSeachFormData searchFormData = boundForm.get();

        // Reject if search by UID but no uid
        if (searchFormData.searchType.equals(UID_SEARCH_OPTION) && (searchFormData.uid == null
                || searchFormData.uid.trim().equals("") || searchFormData.uid.indexOf('*') != -1)) {
            boundForm.reject("uid", Msg.get("error.required"));
            return badRequest(views.html.admin.usermanager.usermanager_search.render(boundForm));
        }
        // Reject if search by UID but no uid
        if (searchFormData.searchType.equals(MAIL_SEARCH_OPTION) && (searchFormData.mail == null
                || searchFormData.mail.trim().equals("") || searchFormData.mail.indexOf('*') != -1)) {
            boundForm.reject("mail", Msg.get("error.required"));
            return badRequest(views.html.admin.usermanager.usermanager_search.render(boundForm));
        }
        // Reject if search by full name but no full name
        if (searchFormData.searchType.equals(FULLNAME_SEARCH_OPTION)
                && (searchFormData.fullName == null || searchFormData.fullName.trim().equals(""))) {
            boundForm.reject("fullName", Msg.get("error.required"));
            return badRequest(views.html.admin.usermanager.usermanager_search.render(boundForm));
        }

        IUserAccount foundUser = null;
        if (searchFormData.searchType.equals(UID_SEARCH_OPTION)) {
            // Search by uid
            try {
                if (log.isDebugEnabled()) {
                    log.debug("Search user by uid with " + searchFormData.uid);
                }
                IUserAccount userAccount = getAccountManagerPlugin().getUserAccountFromUid(searchFormData.uid);
                if (userAccount == null || userAccount.isMarkedForDeletion() || !userAccount.isDisplayed()) {
                    boundForm.reject("uid", Msg.get("admin.user_manager.search.not_found", searchFormData.uid));
                    return badRequest(views.html.admin.usermanager.usermanager_search.render(boundForm));
                }
                foundUser = userAccount;
            } catch (AccountManagementException e) {
                return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                        getI18nMessagesPlugin());
            }
        }
        if (searchFormData.searchType.equals(MAIL_SEARCH_OPTION)) {
            // Search by e-mail
            try {
                if (log.isDebugEnabled()) {
                    log.debug("Search user by mail with " + searchFormData.mail);
                }
                IUserAccount userAccount = getAccountManagerPlugin().getUserAccountFromEmail(searchFormData.mail);
                if (userAccount == null || userAccount.isMarkedForDeletion() || !userAccount.isDisplayed()) {
                    boundForm.reject("mail", Msg.get("admin.user_manager.search.not_found", searchFormData.mail));
                    return badRequest(views.html.admin.usermanager.usermanager_search.render(boundForm));
                }
                foundUser = userAccount;
            } catch (AccountManagementException e) {
                return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                        getI18nMessagesPlugin());
            }
        }
        if (searchFormData.searchType.equals(FULLNAME_SEARCH_OPTION)) {
            // Search by last name
            try {
                if (log.isDebugEnabled()) {
                    log.debug("Search user by name with " + searchFormData.fullName);
                }
                List<IUserAccount> userAccounts = getAccountManagerPlugin()
                        .getUserAccountsFromName(searchFormData.fullName);
                if (userAccounts == null || userAccounts.size() == 0) {
                    boundForm.reject("fullName",
                            Msg.get("admin.user_manager.search.not_found", searchFormData.fullName));
                    return badRequest(views.html.admin.usermanager.usermanager_search.render(boundForm));
                }
                if (userAccounts.size() > 1) {
                    // Filter the accounts marked for deletion
                    CollectionUtils.filter(userAccounts, new Predicate() {
                        @Override
                        public boolean evaluate(Object userAccountAsObject) {
                            IUserAccount userAccount = (IUserAccount) userAccountAsObject;
                            try {
                                return !userAccount.isMarkedForDeletion() && userAccount.isDisplayed();
                            } catch (AccountManagementException e) {
                                return false;
                            }
                        }
                    });
                    Table<IUserAccount> foundUserAccountsTable = tableTemplate.fill(userAccounts);
                    // Multiple user found, display the selection list
                    return ok(
                            views.html.admin.usermanager.usermanager_search_results.render(foundUserAccountsTable));
                }
                foundUser = userAccounts.get(0); // Only one user
            } catch (AccountManagementException e) {
                return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                        getI18nMessagesPlugin());
            }
        }
        return ok(views.html.admin.usermanager.usermanager_display
                .render(getAccountManagerPlugin().isAuthenticationRepositoryMasterMode(), foundUser));
    }

    /**
     * Display the user associated with the specified user id.
     * 
     * @param uid
     *            a unique user id
     */
    public Result displayUser(String uid) {

        IUserAccount userAccount = null;
        try {
            userAccount = getAccountManagerPlugin().getUserAccountFromUid(uid);
            // Do not display the users marked for deletion
            if (userAccount == null || userAccount.isMarkedForDeletion() || !userAccount.isDisplayed()) {
                Utilities.sendErrorFlashMessage(Msg.get("admin.user_manager.unknown_user"));
                return displayUserSearchForm();
            }
        } catch (AccountManagementException e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                    getI18nMessagesPlugin());
        }
        return ok(views.html.admin.usermanager.usermanager_display
                .render(getAccountManagerPlugin().isAuthenticationRepositoryMasterMode(), userAccount));
    }

    /**
     * Display the form to edit the user basic data.
     * 
     * @param uid
     *            a unique user id
     */
    public Result editBasicData(String uid) {
        try {
            IUserAccount account = getAccountManagerPlugin().getUserAccountFromUid(uid);
            if (account == null || account.isMarkedForDeletion() || !account.isDisplayed()) {
                Utilities.sendErrorFlashMessage(Msg.get("admin.user_manager.unknown_user"));
                return displayUserSearchForm();
            }
            Form<UserAccountFormData> userAccountFormLoaded = basicDataUpdateForm
                    .fill(new UserAccountFormData(account));
            return ok(views.html.admin.usermanager.usermanager_editbasicdata.render(userAccountFormLoaded));
        } catch (Exception e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                    getI18nMessagesPlugin());
        }
    }

    /**
     * Display the form to edit the user account type.
     * 
     * @param uid
     *            a unique user id
     */
    public Result editUserAccountType(String uid) {
        try {
            IUserAccount account = getAccountManagerPlugin().getUserAccountFromUid(uid);
            if (account == null || account.isMarkedForDeletion() || !account.isDisplayed()) {
                Utilities.sendErrorFlashMessage(Msg.get("admin.user_manager.unknown_user"));
                return displayUserSearchForm();
            }
            Form<UserAccountFormData> userAccountFormLoaded = changeAccountTypeForm
                    .fill(new UserAccountFormData(account));
            return ok(views.html.admin.usermanager.usermanager_editaccounttype
                    .render(IUserAccount.AccountType.getValueHolder(), userAccountFormLoaded));
        } catch (Exception e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                    getI18nMessagesPlugin());
        }
    }

    /**
     * Save the modified user account type.
     */
    public Result saveUserAccountType() {
        try {
            Form<UserAccountFormData> boundForm = changeAccountTypeForm.bindFromRequest();
            if (boundForm.hasErrors()) {
                return badRequest(views.html.admin.usermanager.usermanager_editaccounttype
                        .render(IUserAccount.AccountType.getValueHolder(), boundForm));
            }
            UserAccountFormData accountDataForm = boundForm.get();
            getAccountManagerPlugin().updateUserAccountType(accountDataForm.uid,
                    IUserAccount.AccountType.valueOf(accountDataForm.accountType));

            getLicensesManagementService().updateConsumedUsers();

            Utilities.sendSuccessFlashMessage(Msg.get("admin.user_manager.update.success"));
            return redirect(controllers.admin.routes.UserManager.displayUser(accountDataForm.uid));
        } catch (Exception e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                    getI18nMessagesPlugin());
        }
    }

    /**
     * Save the modified basic data.
     */
    public Result saveBasicData() {
        try {
            Form<UserAccountFormData> boundForm = basicDataUpdateForm.bindFromRequest();
            if (boundForm.hasErrors()) {
                return badRequest(views.html.admin.usermanager.usermanager_editbasicdata.render(boundForm));
            }
            UserAccountFormData accountDataForm = boundForm.get();
            getAccountManagerPlugin().updateBasicUserData(accountDataForm.uid, accountDataForm.firstName,
                    accountDataForm.lastName);
            getAccountManagerPlugin().updatePreferredLanguage(accountDataForm.uid,
                    accountDataForm.preferredLanguage);
            Utilities.sendSuccessFlashMessage(Msg.get("admin.user_manager.update.success"));
            return redirect(controllers.admin.routes.UserManager.displayUser(accountDataForm.uid));
        } catch (Exception e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                    getI18nMessagesPlugin());
        }
    }

    /**
     * Display the e-mail address edition form.
     * 
     * @param uid
     *            a unique user id
     */
    public Result editMail(String uid) {
        try {
            IUserAccount account = getAccountManagerPlugin().getUserAccountFromUid(uid);
            if (account == null || account.isMarkedForDeletion() || !account.isDisplayed()) {
                Utilities.sendErrorFlashMessage(Msg.get("admin.user_manager.unknown_user"));
                return displayUserSearchForm();
            }
            Form<UserAccountFormData> userAccountFormLoaded = mailUpdateForm.fill(new UserAccountFormData(account));
            return ok(views.html.admin.usermanager.usermanager_editmail.render(userAccountFormLoaded));
        } catch (Exception e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                    getI18nMessagesPlugin());
        }
    }

    /**
     * Save the new e-mail address.
     */
    public Result saveMail() {
        try {
            Form<UserAccountFormData> boundForm = mailUpdateForm.bindFromRequest();
            if (boundForm.hasErrors()) {
                return badRequest(views.html.admin.usermanager.usermanager_editmail.render(boundForm));
            }

            UserAccountFormData accountDataForm = boundForm.get();

            if (getAccountManagerPlugin().isMailExistsInAuthenticationBackEnd(accountDataForm.mail)) {
                IUserAccount userAccount = getAccountManagerPlugin().getUserAccountFromEmail(accountDataForm.mail);
                if (userAccount == null || !userAccount.getUid().equals(accountDataForm.uid)) {
                    boundForm.reject("mail", Msg.get("object.user_account.email.already_exists"));
                    return badRequest(views.html.admin.usermanager.usermanager_editmail.render(boundForm));
                }
            }

            getAccountManagerPlugin().updateMail(accountDataForm.uid, accountDataForm.mail);
            Utilities.sendSuccessFlashMessage(Msg.get("admin.user_manager.update.success"));
            return redirect(controllers.admin.routes.UserManager.displayUser(accountDataForm.uid));
        } catch (Exception e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                    getI18nMessagesPlugin());
        }
    }

    /**
     * Reset the password for the specified user.
     * 
     * @param uid
     *            the unique id for a user
     */
    public Result resetPassword(String uid) {
        try {
            resetUserPasswordFromUid(uid, true);
            return redirect(controllers.admin.routes.UserManager.displayUser(uid));
        } catch (Exception e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                    getI18nMessagesPlugin());
        }
    }

    /**
     * The method to trigger a password reset for the specified uid.<br/>
     * This sends an e-mail to the user with a validation key.
     * 
     * @param uid
     *            a unique user id
     * @param eraseCurrentPassword
     *            true if the current user password is cleared before sending
     *            the password reset message
     * @throws Exception
     * @throws AccountManagementException
     */
    public void resetUserPasswordFromUid(String uid, boolean eraseCurrentPassword)
            throws Exception, AccountManagementException {
        if (!getAccountManagerPlugin().isAuthenticationRepositoryMasterMode()) {
            return;
        }
        IUserAccount userAccount = getAccountManagerPlugin().getUserAccountFromUid(uid);
        if (userAccount == null || userAccount.isMarkedForDeletion() || !userAccount.isDisplayed()) {
            throw new Exception("user not found");
        }
        resetUserPassword(userAccount, eraseCurrentPassword);
        Utilities.sendSuccessFlashMessage(Msg.get("admin.user_manager.reset_password.success"));
    }

    /**
     * The method to trigger a password reset for the specified mail.<br/>
     * This sends an e-mail to the user with a validation key.
     * 
     * @param mail
     *            a user e-mail
     * @param eraseCurrentPassword
     *            true if the current user password is cleared before sending
     *            the password reset message
     * @throws Exception
     * @throws AccountManagementException
     * @return a boolean (false if the e-mail does not exists)
     */
    public boolean resetUserPasswordFromEmail(String mail, boolean eraseCurrentPassword) {
        try {
            IUserAccount userAccount = getAccountManagerPlugin().getUserAccountFromEmail(mail);
            if (userAccount == null || userAccount.isMarkedForDeletion() || !userAccount.isDisplayed()) {
                return false;
            }
            resetUserPassword(userAccount, eraseCurrentPassword);
            return true;
        } catch (Exception e) {
            log.error("Exception following a password reset attempt with mail " + mail, e);
            return false;
        }
    }

    /**
     * Reset the user password of the user associated with the specified
     * account.
     * 
     * @param userAccount
     *            the user account
     * @param eraseCurrentPassword
     *            true if the current user password is cleared before sending
     *            the password reset message
     */
    private void resetUserPassword(IUserAccount userAccount, boolean eraseCurrentPassword)
            throws AccountManagementException {
        // Generate a random password
        String validationData = RandomStringUtils.randomAlphanumeric(12);
        String validationKey = accountManagerPlugin.getValidationKey(userAccount.getUid(), validationData);
        if (eraseCurrentPassword) {
            getAccountManagerPlugin().updatePassword(userAccount.getUid(), validationData);
        }

        // Send an e-mail for reseting password
        String resetPasswordUrl = getPreferenceManagerPlugin()
                .getPreferenceElseConfigurationValue(IFrameworkConstants.PUBLIC_URL_PREFERENCE, "maf.public.url")
                + controllers.admin.routes.PasswordReset
                        .displayPasswordResetForm(userAccount.getUid(), validationKey).url();
        emailService.sendEmail(Msg.get("admin.user_manager.reset_password.mail.subject"),
                play.Configuration.root().getString("maf.email.from"),
                this.getI18nMessagesPlugin()
                        .renderViewI18n("views.html.mail.account_password_reset_html",
                                play.Configuration.root().getString("maf.platformName"),
                                userAccount.getFirstName() + " " + userAccount.getLastName(), resetPasswordUrl)
                        .body(),
                userAccount.getMail());
    }

    /**
     * Display the form which is to be used to edit the roles of the specified
     * user.
     * 
     * @param uid
     *            the unique ID of the user to edit
     */
    public Result editRoles(String uid) {
        try {

            IUserAccount account = getAccountManagerPlugin().getUserAccountFromUid(uid);
            Form<UserAccountFormData> userAccountFormLoaded = rolesUpdateForm
                    .fill(new UserAccountFormData(account));
            return ok(views.html.admin.usermanager.usermanager_editroles
                    .render(SystemLevelRoleType.getAllActiveRolesAsValueHolderCollection(), userAccountFormLoaded));
        } catch (Exception e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                    getI18nMessagesPlugin());
        }
    }

    /**
     * Update the user account with the roles specified by the administrator.
     */
    public Result saveRoles() {
        try {

            Form<UserAccountFormData> boundForm = rolesUpdateForm.bindFromRequest();
            if (boundForm.hasErrors()) {
                return badRequest(views.html.admin.usermanager.usermanager_editmail.render(boundForm));
            }
            UserAccountFormData accountDataForm = boundForm.get();

            // Remove the null values which may be present in the list (see the
            // view to understand)
            accountDataForm.systemLevelRoleTypes.removeAll(Collections.singleton(null));
            getAccountManagerPlugin().overwriteSystemLevelRoleTypes(accountDataForm.uid,
                    accountDataForm.systemLevelRoleTypes);

            Utilities.sendSuccessFlashMessage(Msg.get("admin.user_manager.update.success"));
            return redirect(controllers.admin.routes.UserManager.displayUser(accountDataForm.uid));
        } catch (Exception e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                    getI18nMessagesPlugin());
        }
    }

    /**
     * Lock or unlock the specified account.
     * 
     * @param uid
     *            a user unique id
     * @param activationStatus
     *            the status (true to unlock, false to lock)
     */
    public Result changeActivationStatus(String uid, boolean activationStatus) {

        IUserAccount userAccount = null;
        try {
            userAccount = getAccountManagerPlugin().getUserAccountFromUid(uid);
            if (userAccount == null || userAccount.isMarkedForDeletion() || !userAccount.isDisplayed()) {
                Utilities.sendErrorFlashMessage(Msg.get("admin.user_manager.unknown_user"));
                return displayUserSearchForm();
            }
        } catch (AccountManagementException e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                    getI18nMessagesPlugin());
        }

        try {
            getAccountManagerPlugin().updateActivationStatus(uid, activationStatus);
            Utilities.sendSuccessFlashMessage(Msg.get("admin.user_manager.change_status.success", uid));
        } catch (AccountManagementException e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                    getI18nMessagesPlugin());
        }

        // Notify the user with the lock/unlock action
        try {
            emailService.sendEmail(Msg.get("admin.user_manager.change_status.mail.subject"),
                    play.Configuration.root().getString("maf.email.from"),
                    this.getI18nMessagesPlugin()
                            .renderViewI18n("views.html.mail.account_activation_status_changed_html",
                                    play.Configuration.root().getString("maf.platformName"),
                                    userAccount.getFirstName() + " " + userAccount.getLastName(),
                                    userAccount.isActive())
                            .body(),
                    userAccount.getMail());
        } catch (Exception e) {
            log.error("Unexpected error while sending an e-mail notification", e);
        }

        return redirect(routes.UserManager.displayUserSearchForm());
    }

    /**
     * Display the creation form for a new user.
     */
    public Result displayUserCreationForm() {

        if (!getLicensesManagementService().canCreateUser()) {
            Utilities.sendErrorFlashMessage(Msg.get("licenses_management.cannot_create_user"));
        }

        Form<UserAccountFormData> userAccountFormLoaded = creationForm.fill(new UserAccountFormData());
        return ok(views.html.admin.usermanager.usermanager_create.render(
                SystemLevelRoleType.getAllActiveRolesAsValueHolderCollection(),
                IUserAccount.AccountType.getValueHolder(), userAccountFormLoaded,
                getAccountManagerPlugin().isAuthenticationRepositoryMasterMode()));
    }

    /**
     * Create the new user from the data provided in the creation form.
     */
    public Result saveNewUser() {

        try {

            Form<UserAccountFormData> boundForm = creationForm.bindFromRequest();

            AccountType accountType = AccountType.valueOf(boundForm.data().get("accountType"));

            if (!accountType.equals(AccountType.VIEWER) && !getLicensesManagementService().canCreateUser()) {
                Utilities.sendErrorFlashMessage(Msg.get("licenses_management.cannot_create_user"));
                return redirect(controllers.admin.routes.UserManager.displayUserCreationForm());
            }

            if (boundForm.hasErrors()) {
                return badRequest(views.html.admin.usermanager.usermanager_create.render(
                        SystemLevelRoleType.getAllActiveRolesAsValueHolderCollection(),
                        IUserAccount.AccountType.getValueHolder(), boundForm,
                        getAccountManagerPlugin().isAuthenticationRepositoryMasterMode()));
            }
            UserAccountFormData accountDataForm = boundForm.get();

            // Check if the user already exists
            IUserAccount account = getAccountManagerPlugin().getUserAccountFromUid(accountDataForm.uid);
            if (account != null) {
                boundForm.reject("uid", Msg.get("object.user_account.uid.already_exists"));
                return badRequest(views.html.admin.usermanager.usermanager_create.render(
                        SystemLevelRoleType.getAllActiveRolesAsValueHolderCollection(),
                        IUserAccount.AccountType.getValueHolder(), boundForm,
                        getAccountManagerPlugin().isAuthenticationRepositoryMasterMode()));
            }

            // Check if the mail already exists in the backend
            if (getAccountManagerPlugin().isMailExistsInAuthenticationBackEnd(accountDataForm.mail)) {
                boundForm.reject("mail", Msg.get("object.user_account.email.already_exists"));
                return badRequest(views.html.admin.usermanager.usermanager_create.render(
                        SystemLevelRoleType.getAllActiveRolesAsValueHolderCollection(),
                        IUserAccount.AccountType.getValueHolder(), boundForm,
                        getAccountManagerPlugin().isAuthenticationRepositoryMasterMode()));
            }

            // If password mode is "manual" (not user selected), then check the
            // provided password
            if (accountDataForm.administratorDefinedPassword
                    && getAccountManagerPlugin().isAuthenticationRepositoryMasterMode()) {
                if (!accountDataForm.password.equals(accountDataForm.passwordCheck)) {
                    boundForm.reject("password", Msg.get("form.input.confirmationpassword.invalid"));
                    return badRequest(views.html.admin.usermanager.usermanager_create.render(
                            SystemLevelRoleType.getAllActiveRolesAsValueHolderCollection(),
                            IUserAccount.AccountType.getValueHolder(), boundForm,
                            getAccountManagerPlugin().isAuthenticationRepositoryMasterMode()));
                }
                if (Utilities.getPasswordStrength(accountDataForm.password) < 1) {
                    boundForm.reject("password", Msg.get("form.input.password.error.insufficient_strength"));
                    return badRequest(views.html.admin.usermanager.usermanager_create.render(
                            SystemLevelRoleType.getAllActiveRolesAsValueHolderCollection(),
                            IUserAccount.AccountType.getValueHolder(), boundForm,
                            getAccountManagerPlugin().isAuthenticationRepositoryMasterMode()));
                }
            }

            // Remove the null values which may be present in the list (see the
            // view to understand)
            accountDataForm.systemLevelRoleTypes.removeAll(Collections.singleton(null));

            // Create a new user
            getAccountManagerPlugin().createNewUserAccount(accountDataForm.uid,
                    IUserAccount.AccountType.valueOf(accountDataForm.accountType), accountDataForm.firstName,
                    accountDataForm.lastName, accountDataForm.mail, accountDataForm.systemLevelRoleTypes);

            getLicensesManagementService().updateConsumedUsers();

            // update the preferred language
            getAccountManagerPlugin().updatePreferredLanguage(accountDataForm.uid,
                    accountDataForm.preferredLanguage);

            Utilities.sendSuccessFlashMessage(Msg.get("admin.user_manager.create.success"));

            // If in master mode, the password is managed by the system
            if (getAccountManagerPlugin().isAuthenticationRepositoryMasterMode()) {
                if (!accountDataForm.administratorDefinedPassword) {
                    // Generate a random password
                    String password = RandomStringUtils.randomAlphanumeric(12);
                    String validationKey = getAccountManagerPlugin().getValidationKey(accountDataForm.uid,
                            password);
                    getAccountManagerPlugin().updatePassword(accountDataForm.uid, password);

                    // Notify account creation & password setup
                    String resetPasswordUrl = getPreferenceManagerPlugin().getPreferenceElseConfigurationValue(
                            IFrameworkConstants.PUBLIC_URL_PREFERENCE, "maf.public.url")
                            + controllers.admin.routes.PasswordReset
                                    .displayPasswordResetForm(accountDataForm.uid, validationKey).url();
                    emailService.sendEmail(Msg.get("admin.user_manager.create.mail.subject"),
                            play.Configuration.root().getString("maf.email.from"),
                            this.getI18nMessagesPlugin()
                                    .renderViewI18n("views.html.mail.account_creation_html",
                                            getAccountManagerPlugin().isAuthenticationRepositoryMasterMode(),
                                            play.Configuration.root().getString("maf.platformName"),
                                            accountDataForm.firstName + " " + accountDataForm.lastName,
                                            resetPasswordUrl, accountDataForm.uid)
                                    .body(),
                            accountDataForm.mail);
                } else {
                    // User password is defined by the administrator
                    getAccountManagerPlugin().updatePassword(accountDataForm.uid, accountDataForm.password);
                    getAccountManagerPlugin().resetValidationKey(accountDataForm.uid);
                }
            }

            // check if there is an existing actor for the uid
            Actor actor = ActorDao.getActorByUid(accountDataForm.uid);
            if (actor == null) {
                return ok(views.html.admin.usermanager.usermanager_create_actor.render(accountDataForm.uid,
                        selectActorForUserFormData));
            } else {
                return redirect(controllers.admin.routes.UserManager.displayUser(accountDataForm.uid));
            }
        } catch (Exception e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                    getI18nMessagesPlugin());
        }
    }

    /**
     * Create a actor from the user data.
     */
    public Result createActorFromUser() {

        // get the uid
        DynamicForm requestData = Form.form().bindFromRequest();
        String uid = requestData.get("uid");

        Actor actor = ActorDao.getActorByUid(uid);
        if (actor != null) {
            return redirect(controllers.admin.routes.UserManager.displayUser(uid));
        }

        try {

            IUserAccount account = getAccountManagerPlugin().getUserAccountFromUid(uid);
            if (account == null || account.isMarkedForDeletion() || !account.isDisplayed()) {
                Utilities.sendErrorFlashMessage(Msg.get("admin.user_manager.unknown_user"));
                return displayUserSearchForm();
            }

            Actor newActor = new Actor();
            newActor.firstName = account.getFirstName();
            newActor.isActive = true;
            newActor.lastName = account.getLastName();
            newActor.mail = account.getMail();
            newActor.uid = uid;
            newActor.save();

            Utilities.sendSuccessFlashMessage(Msg.get("admin.user_manager.create.actor.create.successful"));
            return redirect(controllers.admin.routes.UserManager.displayUser(uid));

        } catch (Exception e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                    getI18nMessagesPlugin());
        }

    }

    /**
     * Associate an existing actor to a user.
     */
    public Result selectActorForUser() {

        // bind the form
        Form<SelectActorForUserFormData> boundForm = selectActorForUserFormData.bindFromRequest();

        // get the uid
        String uid = boundForm.data().get("uid");

        if (boundForm.hasErrors()) {
            return ok(views.html.admin.usermanager.usermanager_create_actor.render(uid, boundForm));
        }

        SelectActorForUserFormData formData = boundForm.get();

        Actor actor = ActorDao.getActorById(formData.actor);
        if (actor != null && (actor.uid == null || actor.uid.equals(""))) {
            actor.uid = uid;
            actor.save();
            Utilities.sendSuccessFlashMessage(Msg.get("admin.user_manager.create.actor.select.successful"));
        }

        return redirect(controllers.admin.routes.UserManager.displayUser(uid));
    }

    /**
     * Export as excel the list of MAF users.
     */
    public Result exportAsExcel() {
        final String uid = getUserSessionManagerPlugin().getUserSessionId(ctx());

        final String fileName = String.format("users_%1$td_%1$tm_%1$ty_%1$tH-%1$tM-%1$tS.xlsx", new Date());

        final String successTitle = Msg.get("excel.export.success.title");
        final String successMessage = Msg.get("excel.export.success.message", fileName);
        final String failureTitle = Msg.get("excel.export.failure.title");
        final String failureMessage = Msg.get("excel.export.failure.message");

        final String uidLabel = Msg.get("object.user_account.uid.label");
        final String firstNameLabel = Msg.get("object.user_account.first_name.label");
        final String lastNameLabel = Msg.get("object.user_account.last_name.label");
        final String emailLabel = Msg.get("object.user_account.email.label");
        final String accountTypeLabel = Msg.get("object.user_account.type.label");
        final String isActiveLabel = Msg.get("object.user_account.is_active.label");
        final String rolesLabel = Msg.get("object.user_account.roles.label");
        final String permissionsLabel = Msg.get("object.user_account.permissions.label");

        getSysAdminUtils().scheduleOnce(false, "UserManager Excel Export",
                Duration.create(0, TimeUnit.MILLISECONDS), new Runnable() {
                    @Override
                    public void run() {
                        Table<IUserAccount> exportTemplate = new Table<IUserAccount>() {
                            {
                                this.addColumn("uid", "uid", uidLabel, SorterType.NONE);
                                this.addColumn("firstName", "firstName", firstNameLabel, SorterType.NONE);
                                this.addColumn("lastName", "lastName", lastNameLabel, SorterType.NONE);
                                this.addColumn("mail", "mail", emailLabel, SorterType.NONE);
                                this.addColumn("accountType", "accountType", accountTypeLabel, SorterType.NONE);
                                this.setJavaColumnFormatter("accountType", new IColumnFormatter<IUserAccount>() {
                                    @Override
                                    public String apply(IUserAccount userAccount, Object value) {
                                        return userAccount.getAccountType().getLabel();
                                    }
                                });
                                this.addColumn("active", "active", isActiveLabel, SorterType.NONE);
                                this.addColumn("mafUid", "mafUid", "BizDock Id", SorterType.NONE);
                                this.addColumn("systemLevelRoleTypeNames", "uid", rolesLabel, SorterType.NONE);
                                this.setJavaColumnFormatter("systemLevelRoleTypeNames",
                                        new IColumnFormatter<IUserAccount>() {
                                            @Override
                                            public String apply(IUserAccount userAccount, Object value) {
                                                return StringUtils.join(userAccount.getSystemLevelRoleTypeNames(),
                                                        ",");
                                            }
                                        });
                                this.addColumn("systemPermissionNames", "uid", permissionsLabel, SorterType.NONE);
                                this.setJavaColumnFormatter("systemPermissionNames",
                                        new IColumnFormatter<IUserAccount>() {
                                            @Override
                                            public String apply(IUserAccount userAccount, Object value) {
                                                List<String> permissions = new ArrayList<>();
                                                for (Role role : userAccount.getSelectableRoles()) {
                                                    permissions.add(role.getName());
                                                }
                                                return StringUtils.join(permissions, ",");
                                            }
                                        });
                                this.setIdFieldName("uid");
                            }
                        };

                        try {

                            Table<IUserAccount> exportTable = exportTemplate
                                    .fill(getAccountManagerPlugin().getUserAccountsFromName("*"));
                            byte[] excelFile = TableExcelRenderer.renderFormatted(exportTable);

                            OutputStream out = getPersonalStoragePlugin().createNewFile(uid, fileName);
                            IOUtils.copy(new ByteArrayInputStream(excelFile), out);

                            getNotificationManagerPlugin().sendNotification(uid,
                                    NotificationCategory.getByCode(Code.DOCUMENT), successTitle, successMessage,
                                    controllers.my.routes.MyPersonalStorage.index().url());

                        } catch (Exception e) {

                            log.error("Unable to export the users excel file", e);

                            getNotificationManagerPlugin().sendNotification(uid,
                                    NotificationCategory.getByCode(Code.ISSUE), failureTitle, failureMessage,
                                    controllers.admin.routes.UserManager.displayUserSearchForm().url());

                        }

                    }
                });

        Utilities.sendSuccessFlashMessage(Msg.get("admin.user_manager.export.success"));

        return redirect(controllers.admin.routes.UserManager.displayUserSearchForm());

    }

    /**
     * Delete the specified user.
     * 
     * @param uid
     *            a unique user id
     */
    public Result deleteUser(String uid) {

        try {
            getAccountManagerPlugin().deleteAccount(uid);

            Utilities.sendSuccessFlashMessage(Msg.get("admin.user_manager.delete.success", uid));

            getLicensesManagementService().updateConsumedUsers();

            getNotificationManagerPlugin().sendNotificationWithPermission(
                    IMafConstants.ADMIN_USER_ADMINISTRATION_PERMISSION,
                    NotificationCategory.getByCode(Code.USER_MANAGEMENT),
                    Msg.get("admin.user_manager.delete.notification.title"),
                    Msg.get("admin.user_manager.delete.notification.message", uid, new Date().toString()),
                    controllers.admin.routes.UserManager.displayUserSearchForm().url());
        } catch (AccountManagementException e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                    getI18nMessagesPlugin());
        }
        return redirect(routes.UserManager.displayUserSearchForm());
    }

    /**
     * Get the account manager service.
     */
    private IAccountManagerPlugin getAccountManagerPlugin() {
        return accountManagerPlugin;
    }

    /**
     * Get the licenses management service.
     */
    private ILicensesManagementService getLicensesManagementService() {
        return licensesManagementService;
    }

    /**
     * Get the notification manager service.
     */
    private INotificationManagerPlugin getNotificationManagerPlugin() {
        return notificationManagerPlugin;
    }

    /**
     * Get the personal storage service.
     */
    private IPersonalStoragePlugin getPersonalStoragePlugin() {
        return personalStoragePlugin;
    }

    /**
     * Get the user session manager service.
     */
    private IUserSessionManagerPlugin getUserSessionManagerPlugin() {
        return userSessionManagerPlugin;
    }

    /**
     * Get the system utils.
     */
    private ISysAdminUtils getSysAdminUtils() {
        return sysAdminUtils;
    }

    /**
     * Get the preference manager service.
     */
    private IPreferenceManagerPlugin getPreferenceManagerPlugin() {
        return preferenceManagerPlugin;
    }

    /**
     * Get the i18n messages service.
     */
    private II18nMessagesPlugin getI18nMessagesPlugin() {
        return i18nMessagesPlugin;
    }

    /**
     * Get the Play configuration service.
     */
    private Configuration getConfiguration() {
        return configuration;
    }

    /**
     * A wrapper for the {@link SystemLevelRoleType}.<br/>
     * This one is required by the "checkboxlist" view part to be used to
     * display the list of roles to be selected.
     * 
     * @author Pierre-Yves Cloux
     */
    public static class SelectableRole implements ISelectableValueHolder<String> {
        private SystemLevelRoleType systemLevelRoleType;

        /**
         * Construct a selectable role thanks a DB entry.
         * 
         * @param systemLevelRoleType
         *            the system level role type in the DB
         */
        private SelectableRole(SystemLevelRoleType systemLevelRoleType) {
            this.systemLevelRoleType = systemLevelRoleType;
        }

        /**
         * Get the list of possible roles.
         */
        public static List<ISelectableValueHolder<String>> getListOfPossibleRoles() {
            ArrayList<ISelectableValueHolder<String>> possibleValues = new ArrayList<ISelectableValueHolder<String>>();
            List<SystemLevelRoleType> systemLevelRoleTypes = SystemLevelRoleType.getAllActiveRoles();
            if (systemLevelRoleTypes != null) {
                for (SystemLevelRoleType systemLevelRoleType : systemLevelRoleTypes) {
                    possibleValues.add(new SelectableRole(systemLevelRoleType));
                }
            }
            return possibleValues;
        }

        @Override
        public String getDescription() {
            return getSystemLevelRoleType().getDescriptionAsMessage();
        }

        @Override
        public String getName() {
            return getSystemLevelRoleType().getName();
        }

        @Override
        public String getValue() {
            return getSystemLevelRoleType().getName();
        }

        /**
         * Get the system level role type.
         */
        private SystemLevelRoleType getSystemLevelRoleType() {
            return systemLevelRoleType;
        }

        @Override
        public String getUrl() {
            return null;
        }

        @Override
        public void setUrl(String url) {
        }

        @Override
        public boolean isSelectable() {
            return getSystemLevelRoleType().selectable;
        }

        @Override
        public boolean isDeleted() {
            return getSystemLevelRoleType().deleted;
        }

        @Override
        public int compareTo(Object o) {
            @SuppressWarnings("unchecked")
            ISelectableValueHolder<String> v = (ISelectableValueHolder<String>) o;
            return this.getName().compareTo(v.getName());
        }
    }

    /**
     * The object which contains the User search parameters.
     * 
     * @author Pierre-Yves Cloux
     * 
     */
    public static class UserSeachFormData {
        public String uid;
        public String mail;
        public String fullName;

        @Required
        public String searchType;

        /**
         * Default constructor.
         */
        public UserSeachFormData() {
            super();
        }
    }

    /**
     * The fields to select an actor for a user.
     * 
     * @author Johann Kohler
     * 
     */
    public static class SelectActorForUserFormData {

        public String uid;

        @Required
        public Long actor;

        /**
         * Default constructor.
         */
        public SelectActorForUserFormData() {
        }

    }

    /**
     * The data to be used to collect changes on a user account information.
     * 
     * @author Pierre-Yves Cloux
     * 
     */
    public static class UserAccountFormData {

        /**
         * The group for the change email form.
         * 
         * @author Pierre-Yves Cloux
         */
        public interface EmailChangeGroup {
        }

        /**
         * The group for the change basic data form.
         * 
         * @author Pierre-Yves Cloux
         */
        public interface BasicDataChangeGroup {
        }

        /**
         * The group for the change roles form.
         * 
         * @author Pierre-Yves Cloux
         */
        public interface RolesChangeGroup {
        }

        /**
         * The group for the create user form.
         * 
         * @author Pierre-Yves Cloux
         */
        public interface UserCreationGroup {
        }

        /**
         * The group for the update account type form.
         * 
         * @author Pierre-Yves Cloux
         */
        public interface AccountTypeChangeGroup {
        }

        /**
         * Default constructor.
         */
        public UserAccountFormData() {
            super();
            this.administratorDefinedPassword = false;
        }

        /**
         * Creates a form data holder from a {@link IUserAccount}.
         * 
         * @param userAccount
         *            the user account
         */
        public UserAccountFormData(IUserAccount userAccount) {
            this.administratorDefinedPassword = false;
            this.uid = userAccount.getUid();
            this.firstName = userAccount.getFirstName();
            this.lastName = userAccount.getLastName();
            this.mail = userAccount.getMail();
            this.accountType = userAccount.getAccountType().name();
            // Set the permissions
            for (Role role : userAccount.getRoles()) {
                this.permissions.add(role.getName());
            }
            // Set the roles
            for (String systemLevelRoleTypes : userAccount.getSystemLevelRoleTypeNames()) {
                this.systemLevelRoleTypes.add(systemLevelRoleTypes);
            }
            this.preferredLanguage = userAccount.getPreferredLanguage() != null
                    ? userAccount.getPreferredLanguage().toLowerCase()
                    : null;
        }

        @Required(groups = { UserCreationGroup.class, EmailChangeGroup.class, BasicDataChangeGroup.class,
                RolesChangeGroup.class })
        @MinLength(value = IModelConstants.MIN_UID_LENGTH, message = "object.user_account.uid.invalid", groups = {
                UserCreationGroup.class })
        @MaxLength(value = 60, message = "object.user_account.uid.invalid", groups = { UserCreationGroup.class })
        @Pattern(value = IModelConstants.UID_VALIDATION_PATTERN, message = "object.user_account.uid.invalid", groups = {
                UserCreationGroup.class })
        public String uid;

        @Required(groups = { UserCreationGroup.class, AccountTypeChangeGroup.class })
        public String accountType;

        @Required(groups = { UserCreationGroup.class, EmailChangeGroup.class })
        @Pattern(value = IModelConstants.EMAIL_VALIDATION_PATTERN, message = "object.user_account.email.invalid", groups = {
                UserCreationGroup.class, EmailChangeGroup.class })
        public String mail;

        @Required(groups = { UserCreationGroup.class, BasicDataChangeGroup.class })
        @MinLength(value = IModelConstants.MIN_NAME_LENGTH, message = "object.user_account.first_name.invalid", groups = {
                UserCreationGroup.class, BasicDataChangeGroup.class })
        @MaxLength(value = IModelConstants.MEDIUM_STRING, message = "object.user_account.first_name.invalid", groups = {
                UserCreationGroup.class, BasicDataChangeGroup.class })
        public String firstName;

        @Required(groups = { UserCreationGroup.class, BasicDataChangeGroup.class })
        @MinLength(value = IModelConstants.MIN_NAME_LENGTH, message = "object.user_account.last_name.invalid", groups = {
                UserCreationGroup.class, BasicDataChangeGroup.class })
        @MaxLength(value = IModelConstants.MEDIUM_STRING, message = "object.user_account.last_name.invalid", groups = {
                UserCreationGroup.class, BasicDataChangeGroup.class })
        public String lastName;

        public boolean administratorDefinedPassword;

        public String password;

        public String passwordCheck;

        @Required(groups = { UserCreationGroup.class, BasicDataChangeGroup.class })
        public String preferredLanguage;

        public List<String> permissions = new ArrayList<String>();

        public List<String> systemLevelRoleTypes = new ArrayList<String>();
    }

    /**
     * The menu item type.
     * 
     * @author Johann Kohler
     * 
     */
    public static enum MenuItemType {
        NONE, SEARCH, CREATE;
    }

}