summaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorArtur <artur@vaadin.com>2017-01-30 13:47:55 +0200
committerGitHub <noreply@github.com>2017-01-30 13:47:55 +0200
commit131e4f34bddf99d61a76fcd49890490c78c3efa8 (patch)
tree1276e94ddbec6a8fc612bedf87835cd366e5f0a5 /server
parent870a4d9ab386de41b0c5e344d16ebfd9c13b2951 (diff)
downloadvaadin-framework-131e4f34bddf99d61a76fcd49890490c78c3efa8.tar.gz
vaadin-framework-131e4f34bddf99d61a76fcd49890490c78c3efa8.zip
Make it possible to disallow user selection in Grid (#8144)
Fixes #7880
Diffstat (limited to 'server')
-rw-r--r--server/src/main/java/com/vaadin/ui/Grid.java127
-rw-r--r--server/src/test/java/com/vaadin/tests/server/component/grid/MultiSelectionModelTest.java41
-rw-r--r--server/src/test/java/com/vaadin/tests/server/component/grid/SingleSelectionModelTest.java13
-rw-r--r--server/src/test/java/com/vaadin/ui/ComponentTest.java119
4 files changed, 282 insertions, 18 deletions
diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java
index 1efc091104..f96b485cc0 100644
--- a/server/src/main/java/com/vaadin/ui/Grid.java
+++ b/server/src/main/java/com/vaadin/ui/Grid.java
@@ -105,6 +105,7 @@ import com.vaadin.shared.ui.grid.selection.MultiSelectionModelState;
import com.vaadin.shared.ui.grid.selection.SingleSelectionModelServerRpc;
import com.vaadin.shared.ui.grid.selection.SingleSelectionModelState;
import com.vaadin.shared.util.SharedUtil;
+import com.vaadin.ui.Grid.SelectionModel.HasUserSelectionAllowed;
import com.vaadin.ui.declarative.DesignAttributeHandler;
import com.vaadin.ui.declarative.DesignContext;
import com.vaadin.ui.declarative.DesignException;
@@ -569,11 +570,10 @@ public class Grid extends AbstractFocusable implements SelectionNotifier,
}
}
- private void bindFields(List<Field<?>> fields,
- Item itemDataSource) {
+ private void bindFields(List<Field<?>> fields, Item itemDataSource) {
for (Field<?> field : fields) {
- if (itemDataSource.getItemProperty(getPropertyId(field))
- != null) {
+ if (itemDataSource
+ .getItemProperty(getPropertyId(field)) != null) {
bind(field, getPropertyId(field));
}
}
@@ -1105,6 +1105,34 @@ public class Grid extends AbstractFocusable implements SelectionNotifier,
* SelectionModel should extend {@link AbstractGridExtension}.
*/
public interface SelectionModel extends Serializable, Extension {
+
+ /**
+ * Interface implemented by selection models which support disabling
+ * client side selection while still allowing programmatic selection on
+ * the server.
+ *
+ */
+ public interface HasUserSelectionAllowed extends SelectionModel {
+
+ /**
+ * Checks if the user is allowed to change the selection.
+ *
+ * @return <code>true</code> if the user is allowed to change the
+ * selection, <code>false</code> otherwise
+ */
+ public boolean isUserSelectionAllowed();
+
+ /**
+ * Sets whether the user is allowed to change the selection.
+ *
+ * @param userSelectionAllowed
+ * <code>true</code> if the user is allowed to change the
+ * selection, <code>false</code> otherwise
+ */
+ public void setUserSelectionAllowed(boolean userSelectionAllowed);
+
+ }
+
/**
* Checks whether an item is selected or not.
*
@@ -1464,7 +1492,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier,
* A default implementation of a {@link SelectionModel.Single}
*/
public static class SingleSelectionModel extends AbstractSelectionModel
- implements SelectionModel.Single {
+ implements SelectionModel.Single, HasUserSelectionAllowed {
@Override
protected void extend(AbstractClientConnector target) {
@@ -1473,6 +1501,11 @@ public class Grid extends AbstractFocusable implements SelectionNotifier,
@Override
public void select(String rowKey) {
+ if (!isUserSelectionAllowed()) {
+ throw new IllegalStateException(
+ "Client tried to select '" + rowKey
+ + "' although user selection is disallowed");
+ }
SingleSelectionModel.this.select(getItemId(rowKey), false);
}
});
@@ -1563,6 +1596,21 @@ public class Grid extends AbstractFocusable implements SelectionNotifier,
protected SingleSelectionModelState getState() {
return (SingleSelectionModelState) super.getState();
}
+
+ @Override
+ protected SingleSelectionModelState getState(boolean markAsDirty) {
+ return (SingleSelectionModelState) super.getState(markAsDirty);
+ }
+
+ @Override
+ public boolean isUserSelectionAllowed() {
+ return getState(false).userSelectionAllowed;
+ }
+
+ @Override
+ public void setUserSelectionAllowed(boolean userSelectionAllowed) {
+ getState().userSelectionAllowed = userSelectionAllowed;
+ }
}
/**
@@ -1590,13 +1638,15 @@ public class Grid extends AbstractFocusable implements SelectionNotifier,
public void reset() {
// NOOP
}
+
}
/**
* A default implementation of a {@link SelectionModel.Multi}
*/
public static class MultiSelectionModel extends AbstractSelectionModel
- implements SelectionModel.Multi {
+ implements SelectionModel.Multi,
+ SelectionModel.HasUserSelectionAllowed {
/**
* The default selection size limit.
@@ -1614,6 +1664,12 @@ public class Grid extends AbstractFocusable implements SelectionNotifier,
@Override
public void select(List<String> rowKeys) {
+ if (!isUserSelectionAllowed()) {
+ throw new IllegalStateException(
+ "Client tried to select '" + rowKeys
+ + "' although user selection is disallowed");
+ }
+
List<Object> items = new ArrayList<Object>();
for (String rowKey : rowKeys) {
items.add(getItemId(rowKey));
@@ -1623,6 +1679,12 @@ public class Grid extends AbstractFocusable implements SelectionNotifier,
@Override
public void deselect(List<String> rowKeys) {
+ if (!isUserSelectionAllowed()) {
+ throw new IllegalStateException(
+ "Client tried to deselect '" + rowKeys
+ + "' although user selection is disallowed");
+ }
+
List<Object> items = new ArrayList<Object>();
for (String rowKey : rowKeys) {
items.add(getItemId(rowKey));
@@ -1632,11 +1694,21 @@ public class Grid extends AbstractFocusable implements SelectionNotifier,
@Override
public void selectAll() {
+ if (!isUserSelectionAllowed()) {
+ throw new IllegalStateException(
+ "Client tried to select all although user selection is disallowed");
+ }
+
MultiSelectionModel.this.selectAll(false);
}
@Override
public void deselectAll() {
+ if (!isUserSelectionAllowed()) {
+ throw new IllegalStateException(
+ "Client tried to deselect all although user selection is disallowed");
+ }
+
MultiSelectionModel.this.deselectAll(false);
}
});
@@ -1920,6 +1992,21 @@ public class Grid extends AbstractFocusable implements SelectionNotifier,
protected MultiSelectionModelState getState() {
return (MultiSelectionModelState) super.getState();
}
+
+ @Override
+ protected MultiSelectionModelState getState(boolean markAsDirty) {
+ return (MultiSelectionModelState) super.getState(markAsDirty);
+ }
+
+ @Override
+ public boolean isUserSelectionAllowed() {
+ return getState(false).userSelectionAllowed;
+ }
+
+ @Override
+ public void setUserSelectionAllowed(boolean userSelectionAllowed) {
+ getState().userSelectionAllowed = userSelectionAllowed;
+ }
}
/**
@@ -2228,8 +2315,8 @@ public class Grid extends AbstractFocusable implements SelectionNotifier,
Item item = cell.getItem();
Property itemProperty = item.getItemProperty(cell.getPropertyId());
- Object modelValue =
- itemProperty == null ? null : itemProperty.getValue();
+ Object modelValue = itemProperty == null ? null
+ : itemProperty.getValue();
data.put(columnKeys.key(cell.getPropertyId()), AbstractRenderer
.encodeValue(modelValue, renderer, converter, getLocale()));
@@ -4563,8 +4650,8 @@ public class Grid extends AbstractFocusable implements SelectionNotifier,
private FieldGroup editorFieldGroup = new CustomFieldGroup();
/**
- * Poperty ID to Field mapping that stores editor fields set by {@link
- * #setEditorField(Object, Field)}.
+ * Poperty ID to Field mapping that stores editor fields set by
+ * {@link #setEditorField(Object, Field)}.
*/
private Map<Object, Field<?>> editorFields = new HashMap<Object, Field<?>>();
@@ -5271,10 +5358,12 @@ public class Grid extends AbstractFocusable implements SelectionNotifier,
}
/**
- * Sets the column resize mode to use. The default mode is {@link ColumnResizeMode#ANIMATED}.
+ * Sets the column resize mode to use. The default mode is
+ * {@link ColumnResizeMode#ANIMATED}.
*
- * @param mode a ColumnResizeMode value
-
+ * @param mode
+ * a ColumnResizeMode value
+ *
* @since 7.7.5
*/
public void setColumnResizeMode(ColumnResizeMode mode) {
@@ -5282,7 +5371,8 @@ public class Grid extends AbstractFocusable implements SelectionNotifier,
}
/**
- * Returns the current column resize mode. The default mode is {@link ColumnResizeMode#ANIMATED}.
+ * Returns the current column resize mode. The default mode is
+ * {@link ColumnResizeMode#ANIMATED}.
*
* @return a ColumnResizeMode value
*
@@ -6873,7 +6963,8 @@ public class Grid extends AbstractFocusable implements SelectionNotifier,
Field<?> editor = editorFieldGroup.getField(propertyId);
- // If field group has no field for this property, see if we have it stored
+ // If field group has no field for this property, see if we have it
+ // stored
if (editor == null) {
editor = editorFields.get(propertyId);
if (editor != null) {
@@ -6937,9 +7028,9 @@ public class Grid extends AbstractFocusable implements SelectionNotifier,
editorFieldGroup.setItemDataSource(item);
for (Column column : getColumns()) {
- column.getState().editorConnector =
- item.getItemProperty(column.getPropertyId()) == null
- ? null : getEditorField(column.getPropertyId());
+ column.getState().editorConnector = item
+ .getItemProperty(column.getPropertyId()) == null ? null
+ : getEditorField(column.getPropertyId());
}
editorActive = true;
diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/MultiSelectionModelTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/MultiSelectionModelTest.java
index f07c740b6c..49856dffa9 100644
--- a/server/src/test/java/com/vaadin/tests/server/component/grid/MultiSelectionModelTest.java
+++ b/server/src/test/java/com/vaadin/tests/server/component/grid/MultiSelectionModelTest.java
@@ -17,6 +17,7 @@ package com.vaadin.tests.server.component.grid;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import org.junit.After;
@@ -28,8 +29,11 @@ import com.vaadin.data.Container;
import com.vaadin.data.util.IndexedContainer;
import com.vaadin.event.SelectionEvent;
import com.vaadin.event.SelectionEvent.SelectionListener;
+import com.vaadin.shared.ui.grid.selection.MultiSelectionModelServerRpc;
import com.vaadin.shared.ui.grid.selection.MultiSelectionModelState;
+import com.vaadin.ui.ComponentTest;
import com.vaadin.ui.Grid;
+import com.vaadin.ui.Grid.SelectionModel.HasUserSelectionAllowed;
public class MultiSelectionModelTest {
@@ -187,4 +191,41 @@ public class MultiSelectionModelTest {
}
Assert.fail("Not all items were correctly selected");
}
+
+ @Test(expected = IllegalStateException.class)
+ public void refuseSelectWhenUserSelectionDisallowed() {
+ ((HasUserSelectionAllowed) grid.getSelectionModel())
+ .setUserSelectionAllowed(false);
+ MultiSelectionModelServerRpc serverRpc = ComponentTest.getRpcProxy(
+ grid.getSelectionModel(), MultiSelectionModelServerRpc.class);
+ serverRpc.select(Collections.singletonList("a"));
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void refuseDeselectWhenUserSelectionDisallowed() {
+ ((HasUserSelectionAllowed) grid.getSelectionModel())
+ .setUserSelectionAllowed(false);
+ MultiSelectionModelServerRpc serverRpc = ComponentTest.getRpcProxy(
+ grid.getSelectionModel(), MultiSelectionModelServerRpc.class);
+ serverRpc.deselect(Collections.singletonList("a"));
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void refuseSelectAllWhenUserSelectionDisallowed() {
+ ((HasUserSelectionAllowed) grid.getSelectionModel())
+ .setUserSelectionAllowed(false);
+ MultiSelectionModelServerRpc serverRpc = ComponentTest.getRpcProxy(
+ grid.getSelectionModel(), MultiSelectionModelServerRpc.class);
+ serverRpc.selectAll();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void refuseDeselectAllWhenUserSelectionDisallowed() {
+ ((HasUserSelectionAllowed) grid.getSelectionModel())
+ .setUserSelectionAllowed(false);
+ MultiSelectionModelServerRpc serverRpc = ComponentTest.getRpcProxy(
+ grid.getSelectionModel(), MultiSelectionModelServerRpc.class);
+ serverRpc.deselectAll();
+ }
+
}
diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/SingleSelectionModelTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/SingleSelectionModelTest.java
index 8b66ab625d..e3331cd0be 100644
--- a/server/src/test/java/com/vaadin/tests/server/component/grid/SingleSelectionModelTest.java
+++ b/server/src/test/java/com/vaadin/tests/server/component/grid/SingleSelectionModelTest.java
@@ -24,8 +24,11 @@ import com.vaadin.data.Container;
import com.vaadin.data.util.IndexedContainer;
import com.vaadin.event.SelectionEvent;
import com.vaadin.event.SelectionEvent.SelectionListener;
+import com.vaadin.shared.ui.grid.selection.SingleSelectionModelServerRpc;
+import com.vaadin.ui.ComponentTest;
import com.vaadin.ui.Grid;
import com.vaadin.ui.Grid.SelectionMode;
+import com.vaadin.ui.Grid.SelectionModel.HasUserSelectionAllowed;
import com.vaadin.ui.Grid.SingleSelectionModel;
public class SingleSelectionModelTest {
@@ -150,4 +153,14 @@ public class SingleSelectionModelTest {
}
});
}
+
+ @Test(expected = IllegalStateException.class)
+ public void refuseSelectionWhenUserSelectionDisallowed() {
+ ((HasUserSelectionAllowed) grid.getSelectionModel())
+ .setUserSelectionAllowed(false);
+ SingleSelectionModelServerRpc serverRpc = ComponentTest.getRpcProxy(
+ grid.getSelectionModel(), SingleSelectionModelServerRpc.class);
+ serverRpc.select("a");
+ }
+
}
diff --git a/server/src/test/java/com/vaadin/ui/ComponentTest.java b/server/src/test/java/com/vaadin/ui/ComponentTest.java
new file mode 100644
index 0000000000..7077f8b22e
--- /dev/null
+++ b/server/src/test/java/com/vaadin/ui/ComponentTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * 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.vaadin.ui;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashSet;
+
+import org.junit.Assert;
+
+import com.vaadin.server.ClientConnector;
+import com.vaadin.server.ServerRpcManager;
+import com.vaadin.shared.communication.ServerRpc;
+
+import elemental.json.JsonObject;
+
+/**
+ * Base class for component unit tests, providing helper methods for e.g.
+ * invoking RPC and updating diff state.
+ */
+public class ComponentTest {
+
+ /**
+ * Perform operations on the component similar to what would be done when
+ * the component state is communicated to the client, e.g. update diff state
+ * and mark as clean.
+ *
+ * @param component
+ * the component to update
+ */
+ public static void syncToClient(AbstractComponent component) {
+ updateDiffState(component);
+ component.getUI().getConnectorTracker().markClean(component);
+ }
+
+ /**
+ * Checks if the connector has been marked dirty.
+ *
+ * @param connector
+ * the connector to check
+ * @return <code>true</code> if the connector has been marked dirty,
+ * <code>false</code> otherwise
+ */
+ public static boolean isDirty(ClientConnector connector) {
+ return connector.getUI().getConnectorTracker().isDirty(connector);
+ }
+
+ /**
+ * Updates the stored diff state from the current component state.
+ *
+ * @param rta
+ * the component to update
+ */
+ public static void updateDiffState(AbstractComponent component) {
+ component.getUI().getSession().getCommunicationManager()
+ .encodeState(component, component.getState());
+
+ }
+
+ /**
+ * Gets the server rpc handler registered for a component.
+ *
+ * @param connector
+ * the connector which listens to the RPC
+ * @param serverRpcClass
+ * the server RPC class
+ * @return the server RPC handler
+ */
+ public static <T extends ServerRpc> T getRpcProxy(ClientConnector connector,
+ Class<T> serverRpcClass) {
+ try {
+ ServerRpcManager<?> rpcManager = connector
+ .getRpcManager(serverRpcClass.getName());
+ Method method = ServerRpcManager.class
+ .getDeclaredMethod("getImplementation");
+ method.setAccessible(true);
+ return serverRpcClass.cast(method.invoke(rpcManager));
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Asserts the set of properties that would be sent as state changes for the
+ * given connector.
+ *
+ * @param connector
+ * the connector that has state changes
+ * @param message
+ * the message to show if the properties are not as expected
+ * @param expectedProperties
+ * names of the expected properties
+ */
+ public static void assertEncodedStateProperties(ClientConnector connector,
+ String message, String... expectedProperties) {
+ assert connector.isAttached();
+
+ JsonObject encodeState = connector.encodeState();
+
+ // Collect to HashSet so that order doesn't matter
+ Assert.assertEquals(message,
+ new HashSet<String>(Arrays.asList(expectedProperties)),
+ new HashSet<String>(Arrays.asList(encodeState.keys())));
+ }
+
+} \ No newline at end of file