mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-16 10:48:47 +00:00
bug6239: addRemoveAction is refactored to be a core method for permission check with per row counter/button enabling
This commit is contained in:
@@ -1,182 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* 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 com.google.gwt.view.client;
|
||||
|
||||
import com.google.gwt.view.client.SelectionModel.AbstractSelectionModel;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A simple selection model that allows multiple items to be selected.
|
||||
*
|
||||
* @param <T> the data type of the items
|
||||
*/
|
||||
public class MultiSelectionModel<T> extends AbstractSelectionModel<T>
|
||||
implements SetSelectionModel<T> {
|
||||
|
||||
/**
|
||||
* Stores an item and its pending selection state.
|
||||
*
|
||||
* @param <T> the data type of the item
|
||||
*/
|
||||
static class SelectionChange<T> {
|
||||
private final T item;
|
||||
private final boolean isSelected;
|
||||
|
||||
SelectionChange(T item, boolean isSelected) {
|
||||
this.item = item;
|
||||
this.isSelected = isSelected;
|
||||
}
|
||||
|
||||
public T getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
public boolean isSelected() {
|
||||
return isSelected;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure one value per key
|
||||
final Map<Object, T> selectedSet;
|
||||
|
||||
/**
|
||||
* A map of keys to the item and its pending selection state.
|
||||
*/
|
||||
private final Map<Object, SelectionChange<T>> selectionChanges;
|
||||
|
||||
/**
|
||||
* Constructs a MultiSelectionModel without a key provider.
|
||||
*/
|
||||
public MultiSelectionModel() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a MultiSelectionModel with the given key provider.
|
||||
*
|
||||
* @param keyProvider an instance of ProvidesKey<T>, or null if the item
|
||||
* should act as its own key
|
||||
*/
|
||||
public MultiSelectionModel(ProvidesKey<T> keyProvider) {
|
||||
this(keyProvider, new HashMap<Object, T>(), new HashMap<Object, SelectionChange<T>>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a MultiSelectionModel with the given key provider and
|
||||
* implementations of selectedSet and selectionChanges. Different
|
||||
* implementations allow for enforcing order on selection.
|
||||
*
|
||||
* @param keyProvider an instance of ProvidesKey<T>, or null if the item
|
||||
* should act as its own key
|
||||
* @param selectedSet an instance of Map
|
||||
* @param selectionChanges an instance of Map
|
||||
*/
|
||||
MultiSelectionModel(ProvidesKey<T> keyProvider, Map<Object, T> selectedSet,
|
||||
Map<Object, SelectionChange<T>> selectionChanges) {
|
||||
super(keyProvider);
|
||||
this.selectedSet = selectedSet;
|
||||
this.selectionChanges = selectionChanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deselect all selected values.
|
||||
*/
|
||||
@Override
|
||||
public void clear() {
|
||||
// Clear the current list of pending changes.
|
||||
selectionChanges.clear();
|
||||
|
||||
/*
|
||||
* Add a pending change to deselect each key that is currently selected. We
|
||||
* cannot just clear the selected set, because then we would not know which
|
||||
* keys were selected before we cleared, which we need to know to determine
|
||||
* if we should fire an event.
|
||||
*/
|
||||
for (T value : selectedSet.values()) {
|
||||
selectionChanges.put(getKey(value), new SelectionChange<T>(value, false));
|
||||
}
|
||||
scheduleSelectionChangeEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the set of selected items as a copy. If multiple selected items share
|
||||
* the same key, only the last selected item is included in the set.
|
||||
*
|
||||
* @return the set of selected items
|
||||
*/
|
||||
@Override
|
||||
public Set<T> getSelectedSet() {
|
||||
resolveChanges();
|
||||
return new HashSet<T>(selectedSet.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSelected(T item) {
|
||||
resolveChanges();
|
||||
return selectedSet.containsKey(getKey(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelected(T item, boolean selected) {
|
||||
selectionChanges.put(getKey(item), new SelectionChange<T>(item, selected));
|
||||
scheduleSelectionChangeEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fireSelectionChangeEvent() {
|
||||
if (isEventScheduled()) {
|
||||
setEventCancelled(true);
|
||||
}
|
||||
resolveChanges();
|
||||
}
|
||||
|
||||
void resolveChanges() {
|
||||
if (selectionChanges.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean changed = false;
|
||||
for (Map.Entry<Object, SelectionChange<T>> entry : selectionChanges.entrySet()) {
|
||||
Object key = entry.getKey();
|
||||
SelectionChange<T> value = entry.getValue();
|
||||
boolean selected = value.isSelected;
|
||||
|
||||
T oldValue = selectedSet.get(key);
|
||||
if (selected) {
|
||||
selectedSet.put(key, value.item);
|
||||
Object oldKey = getKey(oldValue);
|
||||
if (!changed) {
|
||||
changed = (oldKey == null) ? (key != null) : !oldKey.equals(key);
|
||||
}
|
||||
} else {
|
||||
if (oldValue != null) {
|
||||
selectedSet.remove(key);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
selectionChanges.clear();
|
||||
|
||||
// Fire a selection change event.
|
||||
if (changed) {
|
||||
SelectionChangeEvent.fire(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
-9
@@ -111,10 +111,13 @@ public class AccessControlledButtonPanel extends Composite {
|
||||
/**
|
||||
* Like {@link #addRemoveAction(String, SetSelectionModel, boolean, Command)} but for rows that are not
|
||||
* {@link SecuredDTO} instances themselves — use this when the permission to remove entries is governed by a parent
|
||||
* secured object (e.g. UPDATE permission on the owning {@link UserGroup}) rather than per-row permissions. The
|
||||
* button is {@link Button#setEnabled(boolean) enabled} when the selection is non-empty and the current user has
|
||||
* {@link DefaultActions#UPDATE UPDATE} permission on the object supplied by {@code parentSecuredObject}; it shows
|
||||
* the selected count in its label.
|
||||
* secured object rather than per-row permissions. The button is {@link Button#setEnabled(boolean) enabled} when the
|
||||
* selection is non-empty and the current user has the specified {@code permissionAction} on the object supplied by
|
||||
* {@code parentSecuredObject}; it shows the selected count in its label.
|
||||
*
|
||||
* <p>Example: removing a role or user from a {@code UserGroup} is semantically an UPDATE to that group, so the
|
||||
* caller should pass {@link DefaultActions#UPDATE} as {@code permissionAction} even though the button is labelled
|
||||
* "Remove".
|
||||
*
|
||||
* @param text
|
||||
* the {@link String text} to show on the button
|
||||
@@ -122,20 +125,23 @@ public class AccessControlledButtonPanel extends Composite {
|
||||
* the {@link MultiSelectionModel} of the sub-table; drives the count shown in the button label and the
|
||||
* enabled state
|
||||
* @param parentSecuredObject
|
||||
* supplies the parent {@link SecuredDTO} whose UPDATE permission gates the button; may return
|
||||
* {@code null} when nothing is selected, which disables the button
|
||||
* supplies the parent {@link SecuredDTO} whose permission gates the button; may return {@code null} when
|
||||
* nothing is selected, which disables the button
|
||||
* @param permissionAction
|
||||
* the {@link DefaultActions action} to check on the parent secured object (e.g.
|
||||
* {@link DefaultActions#UPDATE} or {@link DefaultActions#DELETE})
|
||||
* @param callback
|
||||
* the {@link Command callback} to execute on button click, if permission is granted
|
||||
* @return the created {@link Button} instance
|
||||
*/
|
||||
public <T> Button addRemoveAction(final String text, final MultiSelectionModel<T> selectionModel,
|
||||
final Supplier<SecuredDTO> parentSecuredObject, final Command callback) {
|
||||
public <T> Button addCountingActionWithParentPermission(final String text, final MultiSelectionModel<T> selectionModel,
|
||||
final Supplier<SecuredDTO> parentSecuredObject, final DefaultActions permissionAction, final Command callback) {
|
||||
final Button button = resolveButtonVisibility(removePermissionCheck,
|
||||
new Button(text, wrap(removePermissionCheck, callback)));
|
||||
selectionModel.addSelectionChangeHandler(event -> {
|
||||
final int count = selectionModel.getSelectedSet().size();
|
||||
button.setText(count > 0 ? text + " (" + count + ")" : text);
|
||||
button.setEnabled(count > 0 && userService.hasPermission(parentSecuredObject.get(), DefaultActions.UPDATE));
|
||||
button.setEnabled(count > 0 && userService.hasPermission(parentSecuredObject.get(), permissionAction));
|
||||
});
|
||||
button.setEnabled(false);
|
||||
return button;
|
||||
|
||||
+3
-2
@@ -123,9 +123,10 @@ public class GroupRoleDefinitionPanel extends Composite
|
||||
}
|
||||
});
|
||||
addButton.ensureDebugId("AddGroupUserButton");
|
||||
buttonPanel.addRemoveAction(stringMessages.removeRole(),
|
||||
// Removing a role from a group is semantically an UPDATE to the UserGroup, not a per-role DELETE.
|
||||
buttonPanel.addCountingActionWithParentPermission(stringMessages.removeRole(),
|
||||
roleDefinitionTableWrapper.getSelectionModel(),
|
||||
() -> (SecuredDTO) TableWrapper.getSingleSelectedObjectOrNull(userGroupSelectionModel), () -> {
|
||||
() -> (SecuredDTO) TableWrapper.getSingleSelectedObjectOrNull(userGroupSelectionModel), UPDATE, () -> {
|
||||
final Pair<StrippedRoleDefinitionDTO, Boolean> selectedRole = TableWrapper.getSingleSelectedObjectOrNull(roleDefinitionTableWrapper.getSelectionModel());
|
||||
if (selectedRole == null) {
|
||||
Window.alert(stringMessages.youHaveToSelectAUserGroup());
|
||||
|
||||
+3
-2
@@ -99,9 +99,10 @@ public class UserGroupDetailPanel extends Composite
|
||||
});
|
||||
addButton.ensureDebugId("AddUserButton");
|
||||
// add remove button
|
||||
buttonPanel.addRemoveAction(stringMessages.actionRemove(),
|
||||
// Removing a user from a group is semantically an UPDATE to the UserGroup, not a per-user DELETE.
|
||||
buttonPanel.addCountingActionWithParentPermission(stringMessages.actionRemove(),
|
||||
tenantUsersTable.getSelectionModel(),
|
||||
() -> (SecuredDTO) TableWrapper.getSingleSelectedObjectOrNull(userGroupSelectionModel), () -> {
|
||||
() -> (SecuredDTO) TableWrapper.getSingleSelectedObjectOrNull(userGroupSelectionModel), UPDATE, () -> {
|
||||
final Set<UserGroupDTO> selectedUserGroups = userGroupSelectionModel.getSelectedSet();
|
||||
if (selectedUserGroups != null && selectedUserGroups.size() == 1) {
|
||||
final UserGroupDTO selectedUserGroup = selectedUserGroups.iterator().next();
|
||||
|
||||
Reference in New Issue
Block a user