package org.sigmah.client.ui.view.admin.users; /* * #%L * Sigmah * %% * Copyright (C) 2010 - 2016 URD * %% * 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, either version 3 of the * License, or (at your option) any later version. * * 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/gpl-3.0.html>. * #L% */ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map.Entry; import org.sigmah.client.dispatch.monitor.LoadingMask; import org.sigmah.client.i18n.I18N; import org.sigmah.client.ui.presenter.admin.users.UsersAdminPresenter; import org.sigmah.client.ui.presenter.admin.users.UsersAdminPresenter.GridEditHandler; import org.sigmah.client.ui.res.icon.IconImageBundle; import org.sigmah.client.ui.view.base.AbstractView; import org.sigmah.client.ui.widget.Loadable; import org.sigmah.client.ui.widget.button.Button; import org.sigmah.client.ui.widget.form.Forms; import org.sigmah.client.ui.widget.layout.Layouts; import org.sigmah.client.ui.widget.layout.Layouts.Margin; import org.sigmah.client.ui.widget.panel.Panels; import org.sigmah.client.ui.widget.toolbar.ActionToolBar; import org.sigmah.client.util.ClientUtils; import org.sigmah.client.util.DateUtils; import org.sigmah.shared.dto.ContactDTO; import org.sigmah.shared.dto.UserDTO; import org.sigmah.shared.dto.orgunit.OrgUnitDTO; import org.sigmah.shared.dto.profile.PrivacyGroupDTO; import org.sigmah.shared.dto.profile.ProfileDTO; import org.sigmah.shared.dto.referential.GlobalPermissionEnum; import org.sigmah.shared.dto.referential.PrivacyGroupPermissionEnum; import com.extjs.gxt.ui.client.Style; import com.extjs.gxt.ui.client.Style.LayoutRegion; import com.extjs.gxt.ui.client.data.ModelData; import com.extjs.gxt.ui.client.event.ButtonEvent; import com.extjs.gxt.ui.client.event.ComponentEvent; import com.extjs.gxt.ui.client.event.KeyListener; import com.extjs.gxt.ui.client.event.SelectionListener; import com.extjs.gxt.ui.client.store.ListStore; import com.extjs.gxt.ui.client.store.Store; import com.extjs.gxt.ui.client.store.StoreFilter; import com.extjs.gxt.ui.client.widget.ContentPanel; import com.extjs.gxt.ui.client.widget.LayoutContainer; import com.extjs.gxt.ui.client.widget.Text; import com.extjs.gxt.ui.client.widget.Window; import com.extjs.gxt.ui.client.widget.form.ComboBox; import com.extjs.gxt.ui.client.widget.form.Field; import com.extjs.gxt.ui.client.widget.form.FormPanel; import com.extjs.gxt.ui.client.widget.form.TextField; import com.extjs.gxt.ui.client.widget.grid.ColumnConfig; import com.extjs.gxt.ui.client.widget.grid.ColumnData; import com.extjs.gxt.ui.client.widget.grid.ColumnModel; import com.extjs.gxt.ui.client.widget.grid.Grid; import com.extjs.gxt.ui.client.widget.grid.GridCellRenderer; import com.extjs.gxt.ui.client.widget.grid.GridSelectionModel; import com.extjs.gxt.ui.client.widget.layout.FitLayout; import com.extjs.gxt.ui.client.widget.toolbar.LabelToolItem; import com.google.gwt.i18n.client.DateTimeFormat; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.inject.Singleton; /** * Admin users view implementation. * * @author Maxime Lombard (mlombard@ideia.fr) v1.3 * @author Mehdi Benabdeslam (mehdi.benabdeslam@netapsys.fr) v2.0 * @author Denis Colliot (dcolliot@ideia.fr) (v2.0) */ @Singleton public class UsersAdminView extends AbstractView implements UsersAdminPresenter.View { // CSS style names. private static final String STYLE_PROJECT_GRID_LEAF = "project-grid-leaf"; /** * Grids edit button width (in pixels). */ private static final int GRID_EDIT_BUTTON_WIDTH = 50; // -- // Containers. // -- private LayoutContainer mainContainer; private LayoutContainer southContainer; // -- // Users Panel. // -- private Grid<UserDTO> usersGrid; private ContentPanel usersListPanel; private Button usersAddButton; private Button usersAddByEmailButton; private Button usersDesactiveActiveButton; private Button usersRefreshButton; private Field<String> usersFilterField; // -- // Profiles Panel. // -- private Grid<ProfileDTO> profilesGrid; private ContentPanel profilesListPanel; private Button profilesAddButton; private Button profilesDeleteButton; private Button profilesRefreshButton; // -- // Privacy Groups Panel. // -- private ContentPanel privacyGroupsPanel; private Grid<PrivacyGroupDTO> privacyGroupsGrid; private Button privacyGroupsAddButton; private Button privacyGroupsDeleteButton; private GridEditHandler gridEditHandler; /** * {@inheritDoc} */ @Override public void initialize() { // -- // Main container. // -- mainContainer = Layouts.vBox(); southContainer = Layouts.border(); // -- // Users panel. // -- usersListPanel = Panels.content(I18N.CONSTANTS.adminUsersPanel()); usersListPanel.add(usersGrid = buildUsersGrid()); usersListPanel.setTopComponent(initToolBar(USERS)); // After grid initialization. // -- // Profiles Panel. // -- profilesListPanel = Panels.content(I18N.CONSTANTS.adminProfilesPanel()); profilesListPanel.add(profilesGrid = buildProfilesGrid()); profilesListPanel.setTopComponent(initToolBar(PROFILES)); // After grid initialization. // -- // Privacy groups. // -- privacyGroupsPanel = Panels.content(I18N.CONSTANTS.adminPrivacyGroups()); privacyGroupsPanel.add(privacyGroupsGrid = buildPrivacyGroupsGrid()); privacyGroupsPanel.setTopComponent(initToolBar(PRIVACY_GROUPS)); // After grid initialization. // -- // General layout. // -- southContainer.add(profilesListPanel, Layouts.borderLayoutData(LayoutRegion.CENTER, Margin.HALF_RIGHT)); southContainer.add(privacyGroupsPanel, Layouts.borderLayoutData(LayoutRegion.EAST, 300f, Margin.HALF_LEFT)); mainContainer.add(usersListPanel, Layouts.vBoxData(Margin.HALF_BOTTOM)); mainContainer.add(southContainer, Layouts.vBoxData(Margin.HALF_TOP)); add(mainContainer); } /** * {@inheritDoc} */ @Override public List<UserDTO> getUsersSelection() { return usersGrid.getSelectionModel().getSelectedItems(); } /** * {@inheritDoc} */ @Override public List<ProfileDTO> getProfilesSelection() { return profilesGrid.getSelectionModel().getSelectedItems(); } /** * {@inheritDoc} */ @Override public List<PrivacyGroupDTO> getPrivacyGroupsSelection() { return privacyGroupsGrid.getSelectionModel().getSelectedItems(); } /** * {@inheritDoc} */ @Override public ListStore<UserDTO> getUsersStore() { return usersGrid.getStore(); } /** * {@inheritDoc} */ @Override public Loadable[] getUsersLoadable() { return new Loadable[] { new LoadingMask(usersListPanel) }; } @Override public void buildAddUserByEmailWindow(List<ContactDTO> availableContacts, final UsersAdminPresenter.AddUserByEmailHandler handler) { final Window window = new Window(); window.setHeadingText(I18N.CONSTANTS.adminContactAddUserByEmail()); window.setPlain(true); window.setModal(true); window.setBlinkModal(true); window.setLayout(new FitLayout()); window.setSize(650, 150); final ComboBox<ContactDTO> combobox = Forms.combobox(I18N.CONSTANTS.email(), true, ContactDTO.ID, ContactDTO.EMAIL_WITH_FULLNAME); combobox.getStore().add(availableContacts); combobox.setEditable(true); Button button = Forms.button(I18N.CONSTANTS.addUser()); button.addSelectionListener(new SelectionListener<ButtonEvent>() { @Override public void componentSelected(ButtonEvent ce) { handler.handleSubmit(combobox.getValue()); window.hide(); } }); FormPanel formPanel = new FormPanel(); formPanel.setHeaderVisible(false); formPanel.setLayout(Forms.layout(100, 500)); formPanel.add(combobox); formPanel.getButtonBar().add(button); window.add(formPanel); window.show(); } /** * {@inheritDoc} */ @Override public void clearFilters() { getUsersStore().clearFilters(); usersFilterField.clear(); } /** * {@inheritDoc} */ @Override public ListStore<ProfileDTO> getProfilesStore() { return profilesGrid.getStore(); } /** * {@inheritDoc} */ @Override public Loadable[] getProfilesLoadable() { return new Loadable[] { new LoadingMask(profilesListPanel) }; } /** * {@inheritDoc} */ @Override public ListStore<PrivacyGroupDTO> getPrivacyGroupsStore() { return privacyGroupsGrid.getStore(); } /** * {@inheritDoc} */ @Override public Loadable[] getPrivacyGroupsLoadable() { return new Loadable[] { new LoadingMask(privacyGroupsPanel) }; } /** * {@inheritDoc} */ @Override public Button getPrivacyGroupsAddButton() { return privacyGroupsAddButton; } /** * {@inheritDoc} */ @Override public Button getPrivacyGroupsDeleteButton() { return privacyGroupsDeleteButton; } /** * {@inheritDoc} */ @Override public Button getProfilesAddButton() { return profilesAddButton; } /** * {@inheritDoc} */ @Override public Button getProfilesDeleteButton() { return profilesDeleteButton; } /** * {@inheritDoc} */ @Override public Button getProfilesRefreshButton() { return profilesRefreshButton; } /** * {@inheritDoc} */ @Override public Button getUsersAddButton() { return usersAddButton; } @Override public Button getUsersAddByEmailButton() { return usersAddByEmailButton; } /** * {@inheritDoc} */ @Override public Button getUsersActiveButton() { return usersDesactiveActiveButton; } /** * {@inheritDoc} */ @Override public Button getUsersRefreshButton() { return usersRefreshButton; } /** * {@inheritDoc} */ @Override public void setGridEditHandler(GridEditHandler handler) { this.gridEditHandler = handler; } // ---------------------------------------------------------------------------------------------------- // // UTILITY METHODS. // // ---------------------------------------------------------------------------------------------------- /** * Create the {@link PrivacyGroupDTO} grid. * * @return The grid component. */ private Grid<PrivacyGroupDTO> buildPrivacyGroupsGrid() { final List<ColumnConfig> configs = new ArrayList<ColumnConfig>(); // -- // Code column. // -- configs.add(new ColumnConfig(PrivacyGroupDTO.CODE, I18N.CONSTANTS.adminPrivacyGroupsCode(), 60)); // -- // Title column. // -- configs.add(new ColumnConfig(PrivacyGroupDTO.TITLE, I18N.CONSTANTS.adminPrivacyGroupsName(), 100)); // -- // Edit button column. // -- final ColumnConfig column = new ColumnConfig(null, GRID_EDIT_BUTTON_WIDTH); column.setAlignment(Style.HorizontalAlignment.RIGHT); column.setRenderer(new GridCellRenderer<PrivacyGroupDTO>() { @Override public Object render(final PrivacyGroupDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<PrivacyGroupDTO> store, final Grid<PrivacyGroupDTO> grid) { return Forms.button(I18N.CONSTANTS.edit(), new SelectionListener<ButtonEvent>() { @Override public void componentSelected(final ButtonEvent be) { gridEditHandler.onEditAction(model); }; }); } }); configs.add(column); return createGrid(configs, PrivacyGroupDTO.TITLE); } /** * Create the {@link ProfileDTO} grid. * * @return The grid component. */ private Grid<ProfileDTO> buildProfilesGrid() { final List<ColumnConfig> configs = new ArrayList<ColumnConfig>(); // -- // Name column. // -- configs.add(new ColumnConfig(ProfileDTO.NAME, I18N.CONSTANTS.adminProfilesName(), 100)); // -- // Global permissions column. // -- ColumnConfig column = new ColumnConfig(ProfileDTO.GLOBAL_PERMISSIONS, I18N.CONSTANTS.adminProfilesGlobalPermissions(), 200); column.setRenderer(new GridCellRenderer<ProfileDTO>() { @Override public Object render(final ProfileDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<ProfileDTO> store, final Grid<ProfileDTO> grid) { final StringBuilder builder = new StringBuilder(); // get selected permissions for (final GlobalPermissionEnum globalPermission : model.getGlobalPermissions()) { if (builder.length() > 0) { builder.append(TEXT_VALUES_SEPARATOR); } builder.append(GlobalPermissionEnum.getName(globalPermission)); } return createTextWidget(builder.toString()); } }); configs.add(column); // -- // Privacy groups column. // -- column = new ColumnConfig(ProfileDTO.PRIVACY_GROUPS, I18N.CONSTANTS.adminProfilesPrivacyGroups(), 100); column.setRenderer(new GridCellRenderer<ProfileDTO>() { @Override public Object render(final ProfileDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<ProfileDTO> store, final Grid<ProfileDTO> grid) { final StringBuilder builder = new StringBuilder(); for (final Entry<PrivacyGroupDTO, PrivacyGroupPermissionEnum> privacyGroup : model.getPrivacyGroups().entrySet()) { if (builder.length() > 0) { builder.append(TEXT_VALUES_SEPARATOR); } builder.append('(').append(privacyGroup.getKey().getTitle()).append(", "); builder.append(PrivacyGroupPermissionEnum.getName(privacyGroup.getValue())).append(')'); } return createTextWidget(builder.toString()); } }); configs.add(column); // -- // Edit button column. // -- column = new ColumnConfig(null, GRID_EDIT_BUTTON_WIDTH); column.setAlignment(Style.HorizontalAlignment.RIGHT); column.setRenderer(new GridCellRenderer<ProfileDTO>() { @Override public Object render(final ProfileDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<ProfileDTO> store, final Grid<ProfileDTO> grid) { return new Button(I18N.CONSTANTS.edit(), new SelectionListener<ButtonEvent>() { @Override public void componentSelected(final ButtonEvent be) { gridEditHandler.onEditAction(model); } }); } }); configs.add(column); return createGrid(configs, ProfileDTO.GLOBAL_PERMISSIONS); } /** * Create the {@link UserDTO} grid. * * @return The grid component. */ private Grid<UserDTO> buildUsersGrid() { final List<ColumnConfig> configs = new ArrayList<ColumnConfig>(); // -- // Name column. // -- configs.add(new ColumnConfig(UserDTO.NAME, I18N.CONSTANTS.adminUsersName(), 100)); // -- // First name column. // -- configs.add(new ColumnConfig(UserDTO.FIRST_NAME, I18N.CONSTANTS.adminUsersFirstName(), 100)); // -- // Active flag column. // -- ColumnConfig column = new ColumnConfig(UserDTO.ACTIVE, I18N.CONSTANTS.adminUsersActive(), 50); column.setRenderer(new GridCellRenderer<UserDTO>() { @Override public Object render(final UserDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<UserDTO> store, final Grid<UserDTO> grid) { return createTextWidget(model.getActive() ? I18N.CONSTANTS.adminUsersIsActive() : I18N.CONSTANTS.adminUsersNotActive()); } }); configs.add(column); // -- // Email column. // -- configs.add(new ColumnConfig(UserDTO.EMAIL, I18N.CONSTANTS.adminUsersEmail(), 150)); // -- // Locale (language) column. // -- configs.add(new ColumnConfig(UserDTO.LOCALE, I18N.CONSTANTS.adminUsersLocale(), 50)); // -- // OrgUnit column. // -- column = new ColumnConfig(UserDTO.MAIN_ORG_UNIT, I18N.CONSTANTS.adminUsersOrgUnit(), 110); column.setRenderer(new GridCellRenderer<UserDTO>() { @Override public Object render(final UserDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<UserDTO> store, final Grid<UserDTO> grid) { final OrgUnitDTO orgUnit = model.getMainOrgUnit(); return createTextWidget(orgUnit != null ? (ClientUtils.isNotBlank(orgUnit.getFullName()) ? orgUnit.getFullName() : "") : ""); } }); configs.add(column); // -- // Password change date column. // -- column = new ColumnConfig(UserDTO.PWD_CHANGE_DATE, I18N.CONSTANTS.adminUsersDatePasswordChange(), 120); final DateTimeFormat format = DateUtils.DATE_SHORT; column.setHeaderHtml(I18N.CONSTANTS.adminUsersDatePasswordChange()); column.setDateTimeFormat(format); column.setRenderer(new GridCellRenderer<UserDTO>() { @Override public Object render(final UserDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<UserDTO> store, final Grid<UserDTO> grid) { final Date changePasswordDate = model.getDateChangePasswordKeyIssued(); return createTextWidget(changePasswordDate != null ? format.format(changePasswordDate) : ""); } }); configs.add(column); // -- // Profiles column. // -- column = new ColumnConfig(UserDTO.PROFILES, I18N.CONSTANTS.adminUsersProfiles(), 300); column.setRenderer(new GridCellRenderer<UserDTO>() { @Override public Object render(final UserDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<UserDTO> store, final Grid<UserDTO> grid) { final StringBuilder builder = new StringBuilder(); if (model.getProfiles() != null) { // User has profile(s). for (final ProfileDTO profile : model.getProfiles()) { if (builder.length() > 0) { builder.append(TEXT_VALUES_SEPARATOR); } builder.append(profile.getName()); } } else { // No profiles. builder.append(I18N.CONSTANTS.adminUsersNoProfiles()); } return createTextWidget(builder.toString()); } }); configs.add(column); // -- // Edit button column. // -- column = new ColumnConfig(null, GRID_EDIT_BUTTON_WIDTH); column.setAlignment(Style.HorizontalAlignment.RIGHT); column.setRenderer(new GridCellRenderer<UserDTO>() { @Override public Object render(final UserDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<UserDTO> store, final Grid<UserDTO> grid) { return Forms.button(I18N.CONSTANTS.edit(), new SelectionListener<ButtonEvent>() { @Override public void componentSelected(final ButtonEvent be) { gridEditHandler.onEditAction(model); } }); } }); configs.add(column); return createGrid(configs, UserDTO.PROFILES); } /** * Initializes the grids toolbars. * * @param panel * The grid type. * @return The toolbar component. */ private ActionToolBar initToolBar(final int type) { final ActionToolBar toolBar = new ActionToolBar(); switch (type) { case USERS: usersAddButton = toolBar.addButton(I18N.CONSTANTS.addUser(), IconImageBundle.ICONS.addUser()); usersAddByEmailButton = toolBar.addButton(I18N.CONSTANTS.adminContactAddUserByEmail(), IconImageBundle.ICONS.addUser()); usersDesactiveActiveButton = toolBar.addButton(I18N.CONSTANTS.adminUserDisable(), IconImageBundle.ICONS.deleteUser()); toolBar.add(new LabelToolItem(I18N.CONSTANTS.adminUsersSearchByName())); // -- // Adds a 'name/email' filter implementation. // -- usersGrid.getStore().clearFilters(); usersGrid.getStore().addFilter(new StoreFilter<UserDTO>() { @Override public boolean select(final Store<UserDTO> store, final UserDTO parent, final UserDTO item, final String property) { if (ClientUtils.isBlank(property)) { return true; } return item.getName().toUpperCase().contains(property.toUpperCase()) || item.getFirstName().toUpperCase().contains(property.toUpperCase()) || item.getEmail().toUpperCase().contains(property.toUpperCase()); } }); // -- // Adds a search field to filter store data. // -- usersFilterField = new TextField<String>(); usersFilterField.addKeyListener(new KeyListener() { @Override public void componentKeyUp(final ComponentEvent event) { final String fieldValue = usersFilterField.getValue(); if (ClientUtils.isNotBlank(fieldValue)) { // Applies user filter. usersGrid.getStore().applyFilters(fieldValue); } else { // Clears filter. usersGrid.getStore().clearFilters(); } } }); toolBar.add(usersFilterField); usersRefreshButton = toolBar.addButton(I18N.CONSTANTS.refresh(), IconImageBundle.ICONS.refresh()); break; case PROFILES: profilesAddButton = toolBar.addButton(I18N.CONSTANTS.adminProfileAdd(), IconImageBundle.ICONS.add()); profilesRefreshButton = toolBar.addButton(I18N.CONSTANTS.refresh(), IconImageBundle.ICONS.refresh()); profilesDeleteButton = toolBar.addButton(I18N.CONSTANTS.adminProfileDelete(), IconImageBundle.ICONS.delete()); break; case PRIVACY_GROUPS: privacyGroupsAddButton = toolBar.addButton(I18N.CONSTANTS.addItem(), IconImageBundle.ICONS.add()); privacyGroupsDeleteButton = toolBar.addButton(I18N.CONSTANTS.adminPrivacyGroupDelete(), IconImageBundle.ICONS.delete()); break; default: break; } return toolBar; } /** * Creates a new {@link Grid} instance for the given {@code columnConfigs}. * * @param columnConfigs * The columns configurations list. * @param autoExpandColumn * The auto expand column. * @return The grid component. */ private static <T extends ModelData> Grid<T> createGrid(final List<ColumnConfig> columnConfigs, final String autoExpandColumn) { final Grid<T> grid = new Grid<T>(new ListStore<T>(), new ColumnModel(columnConfigs)); grid.setStyleAttribute("borderTop", "none"); grid.setBorders(false); grid.setStripeRows(true); grid.setSelectionModel(new GridSelectionModel<T>()); grid.setAutoExpandColumn(autoExpandColumn); grid.getView().setForceFit(true); return grid; } /** * Builds a new {@link Text} widget instance with the given {@code content}. * * @param content * The text content. * @return The widget instance. */ private static Text createTextWidget(final String content) { final Text label = new Text(content); label.addStyleName(STYLE_PROJECT_GRID_LEAF); return label; } }