/* * Copyright 2016 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.uberfire.ext.security.management.client.widgets.management.editor.user; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.annotation.PostConstruct; import javax.enterprise.context.Dependent; import javax.enterprise.event.Event; import javax.enterprise.event.Observes; import javax.inject.Inject; import javax.validation.ConstraintViolation; import com.google.gwt.cell.client.Cell; import com.google.gwt.cell.client.EditTextCell; import com.google.gwt.cell.client.FieldUpdater; import com.google.gwt.cell.client.TextCell; import com.google.gwt.user.cellview.client.ColumnSortEvent; import com.google.gwt.user.client.ui.HasHorizontalAlignment; import com.google.gwt.user.client.ui.IsWidget; import com.google.gwt.user.client.ui.Widget; import com.google.gwt.view.client.HasData; import com.google.gwt.view.client.ListDataProvider; import com.google.gwt.view.client.ProvidesKey; import org.gwtbootstrap3.client.ui.constants.ButtonSize; import org.gwtbootstrap3.client.ui.constants.ButtonType; import org.gwtbootstrap3.client.ui.constants.IconType; import org.gwtbootstrap3.client.ui.gwt.ButtonCell; import org.jboss.errai.security.shared.api.identity.User; import org.uberfire.client.mvp.UberView; import org.uberfire.ext.security.management.api.Capability; import org.uberfire.ext.security.management.api.UserManager; import org.uberfire.ext.security.management.client.ClientUserSystemManager; import org.uberfire.ext.security.management.client.resources.i18n.UsersManagementWidgetsConstants; import org.uberfire.ext.security.management.client.widgets.management.events.CreateUserAttributeEvent; import org.uberfire.ext.security.management.client.widgets.management.events.DeleteUserAttributeEvent; import org.uberfire.ext.security.management.client.widgets.management.events.OnErrorEvent; import org.uberfire.ext.security.management.client.widgets.management.events.UpdateUserAttributeEvent; import org.uberfire.ext.security.management.client.widgets.popup.ConfirmBox; /** * <p>Presenter class for user editor attributes widget.</p> */ @Dependent public class UserAttributesEditor implements IsWidget, org.uberfire.ext.security.management.client.editor.user.UserAttributesEditor { /** * The provider that holds the list of containers. */ final ListDataProvider<Entry<String, String>> userAttributesProvider = new ListDataProvider<Entry<String, String>>(); final ProvidesKey<Entry<String, String>> KEY_PROVIDER = new ProvidesKey<Map.Entry<String, String>>() { @Override public Object getKey(final Map.Entry<String, String> item) { return item == null ? null : item.getKey(); } }; public View view; ClientUserSystemManager userSystemManager; ConfirmBox confirmBox; Event<CreateUserAttributeEvent> createUserAttributeEventEvent; Event<UpdateUserAttributeEvent> updateUserAttributeEventEvent; Event<DeleteUserAttributeEvent> deleteUserAttributeEventEvent; Event<OnErrorEvent> errorEvent; NewUserAttributeEditor newUserAttributeEditor; Map<UserManager.UserAttribute, String> attributes; private final FieldUpdater keyModifiedEventHandler = new FieldUpdater<Entry<String, String>, String>() { @Override public void update(final int index, final Entry<String, String> object, final String value) { final String k = object != null ? object.getKey() : null; if (hasValueChanged(k, value)) { updateUserAttribute(index, value, object != null ? object.getValue() : null); } } }; private final FieldUpdater valueModifiedEventHandler = new FieldUpdater<Entry<String, String>, String>() { @Override public void update(final int index, final Entry<String, String> object, final String value) { final String k = object != null ? object.getValue() : null; if (hasValueChanged(k, value)) { updateUserAttribute(index, object != null ? object.getKey() : null, value); } } }; private final FieldUpdater removeButtonHandler = new FieldUpdater<Entry<String, String>, String>() { @Override public void update(final int index, final Entry<String, String> object, final String value) { if (value == null) { // Attribute is mandatory and cannot be removed. errorEvent.fire(new OnErrorEvent(UserAttributesEditor.this, UsersManagementWidgetsConstants.INSTANCE.attributeIsMandatory())); } else { confirmBox.show(UsersManagementWidgetsConstants.INSTANCE.confirmAction(), UsersManagementWidgetsConstants.INSTANCE.ensureRemoveAttribute(), () -> removeUserAttribute(index, object), () -> { }); } } }; /* ****************************************************************************************************** PUBLIC PRESENTER API ****************************************************************************************************** */ boolean isEditMode; @Inject public UserAttributesEditor(final ClientUserSystemManager userSystemManager, final ConfirmBox confirmBox, final Event<CreateUserAttributeEvent> createUserAttributeEventEvent, final Event<UpdateUserAttributeEvent> updateUserAttributeEventEvent, final Event<DeleteUserAttributeEvent> deleteUserAttributeEventEvent, final Event<OnErrorEvent> errorEvent, final NewUserAttributeEditor newUserAttributeEditor, final View view) { this.userSystemManager = userSystemManager; this.confirmBox = confirmBox; this.createUserAttributeEventEvent = createUserAttributeEventEvent; this.updateUserAttributeEventEvent = updateUserAttributeEventEvent; this.deleteUserAttributeEventEvent = deleteUserAttributeEventEvent; this.errorEvent = errorEvent; this.newUserAttributeEditor = newUserAttributeEditor; this.view = view; } @PostConstruct public void init() { view.init(this); view.initWidgets(newUserAttributeEditor.view); buildViewColumns(); } @Override public void show(final User user) { this.isEditMode = false; open(user); } @Override public void edit(final User user) { this.isEditMode = true; open(user); } @Override public void flush() { assert isEditMode; // No additional flush logic to perform here. } @Override public Map<String, String> getValue() { final Map<String, String> result = new HashMap<String, String>(attributes.size()); for (Map.Entry<UserManager.UserAttribute, String> entry : attributes.entrySet()) { result.put(entry.getKey().getName(), entry.getValue()); } return result; } /* ****************************************************************************************************** VIEW CALLBACKS ****************************************************************************************************** */ @Override public void setViolations(Set<ConstraintViolation<User>> constraintViolations) { // Currently no violations expected. } public void clear() { userAttributesProvider.getList().clear(); attributes = null; isEditMode = false; } /* ****************************************************************************************************** PRIVATE METHODS AND VALIDATORS ****************************************************************************************************** */ @Override public Widget asWidget() { return view.asWidget(); } /** * View callback for getting the list. */ void addDataDisplay(HasData<Entry<String, String>> display) { userAttributesProvider.addDataDisplay(display); } protected void loadUserAttributes(final User user) { final Map<String, String> properties = user.getProperties(); if (properties != null) { final Map<UserManager.UserAttribute, String> _properties = new HashMap<UserManager.UserAttribute, String>(properties.size()); for (final Map.Entry<String, String> entry : properties.entrySet()) { final String name = entry.getKey(); UserManager.UserAttribute attribute = getSupportedAttribute(name); final String value = entry.getValue(); if (attribute != null) { _properties.put(attribute, value); } } this.attributes = _properties; } } protected UserManager.UserAttribute getSupportedAttribute(final String name) { UserManager.UserAttribute attribute = userSystemManager.getUserSupportedAttribute(name); if (attribute == null) { attribute = userSystemManager.createUserAttribute(name, false, true, null); } return attribute; } protected void open(final User user) { // User attributes editor settings. newUserAttributeEditor.clear().showAddButton(); view.setCanCreate(canManageAttributes()); // User load. if (user != null) { loadUserAttributes(user); redraw(); } } protected void redraw() { userAttributesProvider.getList().clear(); buildViewColumns(); if (attributes != null) { for (final Map.Entry<UserManager.UserAttribute, String> entry : attributes.entrySet()) { final UserManager.UserAttribute attribute = entry.getKey(); final String value = entry.getValue(); final Map.Entry<String, String> attrEntry = createAttributeEntry(attribute.getName(), value); addAttributeEntry(attrEntry); } view.redraw(); } else { view.showEmpty(); } } protected void addAttributeEntry(final Map.Entry<String, String> entry) { List<Map.Entry<String, String>> contacts = userAttributesProvider.getList(); contacts.remove(entry); contacts.add(entry); } boolean canManageAttributes() { if (!isEditMode) { return false; } final boolean canManageAttrs = userSystemManager.isUserCapabilityEnabled(Capability.CAN_MANAGE_ATTRIBUTES); return canManageAttrs; } protected void buildViewColumns() { int columnCount = view.getColumnCount(); while (columnCount > 0) { view.removeColumn(0); columnCount = view.getColumnCount(); } // Attach a column sort handler to the ListDataProvider to sort the list. ColumnSortEvent.ListHandler<Map.Entry<String, String>> sortHandler = new ColumnSortEvent.ListHandler<Map.Entry<String, String>>(userAttributesProvider.getList()); view.setColumnSortHandler(sortHandler); // Attribute name. final com.google.gwt.user.cellview.client.Column<Map.Entry<String, String>, String> keyColumn = createAttributeNameColumn(sortHandler); if (keyColumn != null) { view.addColumn(keyColumn, UsersManagementWidgetsConstants.INSTANCE.name()); } // Attribute value. final com.google.gwt.user.cellview.client.Column<Map.Entry<String, String>, String> valueColumn = createAttributeValueColumn(sortHandler); if (valueColumn != null) { view.addColumn(valueColumn, UsersManagementWidgetsConstants.INSTANCE.value()); } // Create remove button column. final com.google.gwt.user.cellview.client.Column<Map.Entry<String, String>, String> removeColumn = createAttributeRemoveColumn(); if (removeColumn != null) { view.addColumn(removeColumn, UsersManagementWidgetsConstants.INSTANCE.remove()); } } protected com.google.gwt.user.cellview.client.Column<Entry<String, String>, String> createAttributeNameColumn(ColumnSortEvent.ListHandler<Entry<String, String>> sortHandler) { // Attribute name. final Cell<String> nameCell = canManageAttributes() ? new EditTextCell() : new TextCell(); final com.google.gwt.user.cellview.client.Column<Entry<String, String>, String> keyColumn = new com.google.gwt.user.cellview.client.Column<Entry<String, String>, String>( nameCell) { @Override public String getValue(final Entry<String, String> object) { return object.getKey() != null ? object.getKey() : ""; } }; keyColumn.setSortable(true); sortHandler.setComparator(keyColumn, new Comparator<Entry<String, String>>() { @Override public int compare(Entry<String, String> o1, Entry<String, String> o2) { return o1.getKey().compareTo(o2.getKey()); } }); keyColumn.setFieldUpdater(canManageAttributes() ? keyModifiedEventHandler : null); return keyColumn; } protected com.google.gwt.user.cellview.client.Column<Entry<String, String>, String> createAttributeValueColumn(ColumnSortEvent.ListHandler<Entry<String, String>> sortHandler) { // Attribute value. final Cell<String> valueCell = canManageAttributes() ? new EditTextCell() : new TextCell(); final com.google.gwt.user.cellview.client.Column<Entry<String, String>, String> valueColumn = new com.google.gwt.user.cellview.client.Column<Entry<String, String>, String>( valueCell) { @Override public String getValue(final Entry<String, String> object) { return object.getValue() != null ? object.getValue() : ""; } }; valueColumn.setSortable(true); sortHandler.setComparator(valueColumn, new Comparator<Entry<String, String>>() { @Override public int compare(Entry<String, String> o1, Entry<String, String> o2) { return o1.getValue().compareTo(o2.getValue()); } }); valueColumn.setFieldUpdater(canManageAttributes() ? valueModifiedEventHandler : null); return valueColumn; } protected com.google.gwt.user.cellview.client.Column<Entry<String, String>, String> createAttributeRemoveColumn() { // On read mode, remove button not present. if (!canManageAttributes()) { return null; } // Create remove button column. final ButtonCell removeButtonCell = new ButtonCell(IconType.CLOSE, ButtonType.LINK, ButtonSize.SMALL); final com.google.gwt.user.cellview.client.Column<Entry<String, String>, String> removeColumn = new com.google.gwt.user.cellview.client.Column<Entry<String, String>, String>(removeButtonCell) { @Override public String getValue(final Entry<String, String> object) { // if can be removed return empty string, if not, return null if (object != null) { final UserManager.UserAttribute attribute = getAttribute(object.getKey()); if (attribute != null && !attribute.isMandatory() && attribute.isEditable()) { removeButtonCell.setEnabled(true); return ""; } } removeButtonCell.setEnabled(false); return null; } }; removeColumn.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER); removeColumn.setFieldUpdater(canManageAttributes() ? removeButtonHandler : null); return removeColumn; } private UserManager.UserAttribute getAttribute(final String name) { if (!isEmpty(name) && attributes != null && !attributes.isEmpty()) { for (final Map.Entry<UserManager.UserAttribute, String> entry : attributes.entrySet()) { final UserManager.UserAttribute attribute = entry.getKey(); if (name.equals(attribute.getName())) { return attribute; } } } return null; } private Map.Entry<String, String> createAttributeEntry(final String key, final String value) { return new Map.Entry<String, String>() { @Override public String getKey() { return key; } @Override public String getValue() { return value; } @Override public String setValue(String value) { return null; } }; } private boolean hasValueChanged(final String s1, final String s2) { if (s1 != null && !s1.equals(s2)) { return true; } if (s2 != null && !s2.equals(s1)) { return true; } return false; } private boolean isEmpty(final String str) { return str == null || str.trim().length() == 0; } void updateUserAttribute(final int index, final String key, final String value) { final Entry<String, String> attr = createAttributeEntry(key, value); UserManager.UserAttribute attribute = getAttribute(key); attributes.put(attribute, value); redraw(); updateUserAttributeEventEvent.fire(new UpdateUserAttributeEvent(UserAttributesEditor.this, attr)); } void removeUserAttribute(final int index, final Entry<String, String> entry) { UserManager.UserAttribute attribute = getAttribute(entry.getKey()); attributes.remove(attribute); redraw(); deleteUserAttributeEventEvent.fire(new DeleteUserAttributeEvent(UserAttributesEditor.this, entry)); } void onAttributeCreated(@Observes final CreateUserAttributeEvent createUserAttributeEvent) { final Entry<String, String> entry = createUserAttributeEvent.getAttribute(); final String name = entry.getKey(); final String value = entry.getValue(); UserManager.UserAttribute attribute = getSupportedAttribute(name); attributes.put(attribute, value); redraw(); } public interface View extends UberView<UserAttributesEditor> { View initWidgets(NewUserAttributeEditor.View newUserAttributeEditorView); View setCanCreate(boolean isCreateAllowed); View setColumnSortHandler(ColumnSortEvent.ListHandler<Map.Entry<String, String>> sortHandler); View addColumn(com.google.gwt.user.cellview.client.Column<Entry<String, String>, String> column, String name); View removeColumn(int index); int getColumnCount(); View showEmpty(); View redraw(); } }