From: Bruno Sinou Date: Thu, 7 Nov 2013 17:02:11 +0000 (+0000) Subject: Merge from commons 1.2.x, see following bugs: X-Git-Tag: argeo-commons-2.1.30~638 X-Git-Url: http://git.argeo.org/?a=commitdiff_plain;h=368bd27f3da5356a1e45ae3645f64e674eb89904;p=lgpl%2Fargeo-commons.git Merge from commons 1.2.x, see following bugs: https://www.argeo.org/bugzilla/show_bug.cgi?id=161 https://www.argeo.org/bugzilla/show_bug.cgi?id=171 https://www.argeo.org/bugzilla/show_bug.cgi?id=178 https://www.argeo.org/bugzilla/show_bug.cgi?id=181 https://www.argeo.org/bugzilla/show_bug.cgi?id=182 https://www.argeo.org/bugzilla/show_bug.cgi?id=185 https://www.argeo.org/bugzilla/show_bug.cgi?id=186 git-svn-id: https://svn.argeo.org/commons/trunk@6597 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- diff --git a/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/JcrUiUtils.java b/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/JcrUiUtils.java new file mode 100644 index 000000000..8ee13aab0 --- /dev/null +++ b/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/JcrUiUtils.java @@ -0,0 +1,146 @@ +package org.argeo.eclipse.ui.jcr; + +import java.util.Calendar; + +import javax.jcr.Node; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; + +import org.argeo.ArgeoException; +import org.argeo.eclipse.ui.jcr.lists.NodeViewerComparator; +import org.argeo.eclipse.ui.jcr.lists.RowViewerComparator; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.widgets.Table; + +/** Utility methods to simplify UI development using eclipse and JCR. */ +public class JcrUiUtils { + + /** + * Centralizes management of updating property value. Among other to avoid + * infinite loop when the new value is the same as the ones that is already + * stored in JCR. + * + * @return true if the value as changed + */ + public static boolean setJcrProperty(Node node, String propName, + int propertyType, Object value) { + try { + // int propertyType = getPic().getProperty(propName).getType(); + switch (propertyType) { + case PropertyType.STRING: + if ("".equals((String) value) + && (!node.hasProperty(propName) || node + .hasProperty(propName) + && "".equals(node.getProperty(propName) + .getString()))) + // workaround the fact that the Text widget value cannot be + // set to null + return false; + else if (node.hasProperty(propName) + && node.getProperty(propName).getString() + .equals((String) value)) + // nothing changed yet + return false; + else { + node.setProperty(propName, (String) value); + return true; + } + case PropertyType.BOOLEAN: + if (node.hasProperty(propName) + && node.getProperty(propName).getBoolean() == (Boolean) value) + // nothing changed yet + return false; + else { + node.setProperty(propName, (Boolean) value); + return true; + } + case PropertyType.DATE: + if (node.hasProperty(propName) + && node.getProperty(propName).getDate() + .equals((Calendar) value)) + // nothing changed yet + return false; + else { + node.setProperty(propName, (Calendar) value); + return true; + } + case PropertyType.LONG: + Long lgValue = (Long) value; + + if (lgValue == null) + lgValue = 0L; + + if (node.hasProperty(propName) + && node.getProperty(propName).getLong() == lgValue) + // nothing changed yet + return false; + else { + node.setProperty(propName, lgValue); + return true; + } + + default: + throw new ArgeoException("Unimplemented property save"); + } + } catch (RepositoryException re) { + throw new ArgeoException("Unexpected error while setting property", + re); + } + } + + /** + * Creates a new selection adapter in order to provide sorting abitily on a + * swt table that display a row list + **/ + public static SelectionAdapter getRowSelectionAdapter(final int index, + final int propertyType, final String selectorName, + final String propertyName, final RowViewerComparator comparator, + final TableViewer viewer) { + SelectionAdapter selectionAdapter = new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + Table table = viewer.getTable(); + comparator.setColumn(propertyType, selectorName, propertyName); + int dir = table.getSortDirection(); + if (table.getSortColumn() == table.getColumn(index)) { + dir = dir == SWT.UP ? SWT.DOWN : SWT.UP; + } else { + dir = SWT.DOWN; + } + table.setSortDirection(dir); + table.setSortColumn(table.getColumn(index)); + viewer.refresh(); + } + }; + return selectionAdapter; + } + + /** + * Creates a new selection adapter in order to provide sorting abitily on a + * swt table that display a row list + **/ + public static SelectionAdapter getNodeSelectionAdapter(final int index, + final int propertyType, final String propertyName, + final NodeViewerComparator comparator, final TableViewer viewer) { + SelectionAdapter selectionAdapter = new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + Table table = viewer.getTable(); + comparator.setColumn(propertyType, propertyName); + int dir = table.getSortDirection(); + if (table.getSortColumn() == table.getColumn(index)) { + dir = dir == SWT.UP ? SWT.DOWN : SWT.UP; + } else { + dir = SWT.DOWN; + } + table.setSortDirection(dir); + table.setSortColumn(table.getColumn(index)); + viewer.refresh(); + } + }; + return selectionAdapter; + } +} diff --git a/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/ColumnDefinition.java b/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/ColumnDefinition.java new file mode 100644 index 000000000..9e338276a --- /dev/null +++ b/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/ColumnDefinition.java @@ -0,0 +1,88 @@ +package org.argeo.eclipse.ui.jcr.lists; + +/** + * Utility object to manage column in various tables and extracts displaying + * data from JCR + */ +public class ColumnDefinition { + private final static int DEFAULT_COLUMN_SIZE = 120; + + private String selectorName; + private String propertyName; + private String headerLabel; + private int propertyType; + private int columnSize = DEFAULT_COLUMN_SIZE; + + /** + * new column using default width + * + * @param selectorName + * @param propertyName + * @param propertyType + * @param headerLabel + */ + public ColumnDefinition(String selectorName, String propertyName, + int propertyType, String headerLabel) { + this.selectorName = selectorName; + this.propertyName = propertyName; + this.propertyType = propertyType; + this.headerLabel = headerLabel; + } + + /** + * + * @param selectorName + * @param propertyName + * @param propertyType + * @param headerLabel + * @param columnSize + */ + public ColumnDefinition(String selectorName, String propertyName, + int propertyType, String headerLabel, int columnSize) { + this.selectorName = selectorName; + this.propertyName = propertyName; + this.propertyType = propertyType; + this.headerLabel = headerLabel; + this.columnSize = columnSize; + } + + public String getSelectorName() { + return selectorName; + } + + public void setSelectorName(String selectorName) { + this.selectorName = selectorName; + } + + public String getPropertyName() { + return propertyName; + } + + public void setPropertyName(String propertyName) { + this.propertyName = propertyName; + } + + public String getHeaderLabel() { + return headerLabel; + } + + public void setHeaderLabel(String headerLabel) { + this.headerLabel = headerLabel; + } + + public int getPropertyType() { + return propertyType; + } + + public void setPropertyType(int propertyType) { + this.propertyType = propertyType; + } + + public int getColumnSize() { + return columnSize; + } + + public void setColumnSize(int columnSize) { + this.columnSize = columnSize; + } +} diff --git a/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/IListProvider.java b/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/IListProvider.java new file mode 100644 index 000000000..622e2e259 --- /dev/null +++ b/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/IListProvider.java @@ -0,0 +1,20 @@ +package org.argeo.eclipse.ui.jcr.lists; + +import java.util.List; + +/** + * Views and editors can implement this interface so that one of the row list + * that is displayed in the part (For instance in a Table or a Tree Viewer) can + * be rebuilt externally. typically to generate csv or calc extract. + */ +public interface IListProvider { + /** + * Returns an array of current and relevant elements + */ + public Object[] getElements(String extractId); + + /** + * Returns the column definition for passed ID + */ + public List getColumnDefinition(String extractId); +} diff --git a/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/NodeViewerComparator.java b/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/NodeViewerComparator.java new file mode 100644 index 000000000..11f12e6f5 --- /dev/null +++ b/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/NodeViewerComparator.java @@ -0,0 +1,192 @@ +package org.argeo.eclipse.ui.jcr.lists; + +import java.math.BigDecimal; +import java.util.Calendar; + +import javax.jcr.Node; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Value; +import javax.jcr.ValueFormatException; + +import org.argeo.ArgeoException; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; + +/** + * Base comparator to enable ordering on Table or Tree viewer that display Jcr + * Nodes. + * + * Note that the following snippet must be added before setting the comparator + * to the corresponding control: + * // IMPORTANT: initialize comparator before setting it + * ColumnDefinition firstCol = colDefs.get(0); + * comparator.setColumn(firstCol.getPropertyType(), + * firstCol.getPropertyName()); + * viewer.setComparator(comparator); + */ +public class NodeViewerComparator extends ViewerComparator { + + protected String propertyName; + + protected int propertyType; + public static final int ASCENDING = 0, DESCENDING = 1; + protected int direction = DESCENDING; + + public NodeViewerComparator() { + } + + /** + * e1 and e2 must both be Jcr nodes. + * + * @param viewer + * @param e1 + * @param e2 + * @return + */ + @Override + public int compare(Viewer viewer, Object e1, Object e2) { + int rc = 0; + long lc = 0; + + try { + + Node n1 = (Node) e1; + Node n2 = (Node) e2; + + Value v1 = null; + Value v2 = null; + if (n1.hasProperty(propertyName)) + v1 = n1.getProperty(propertyName).getValue(); + if (n2.hasProperty(propertyName)) + v2 = n2.getProperty(propertyName).getValue(); + + if (v2 == null && v1 == null) + return 0; + else if (v2 == null) + return -1; + else if (v1 == null) + return 1; + + switch (propertyType) { + case PropertyType.STRING: + rc = v1.getString().compareTo(v2.getString()); + break; + case PropertyType.BOOLEAN: + boolean b1 = v1.getBoolean(); + boolean b2 = v2.getBoolean(); + if (b1 == b2) + rc = 0; + else + // we assume true is greater than false + rc = b1 ? 1 : -1; + break; + case PropertyType.DATE: + Calendar c1 = v1.getDate(); + Calendar c2 = v2.getDate(); + if (c1 == null || c2 == null) + // log.trace("undefined date"); + ; + lc = c1.getTimeInMillis() - c2.getTimeInMillis(); + if (lc < Integer.MIN_VALUE) + // rc = Integer.MIN_VALUE; + rc = -1; + else if (lc > Integer.MAX_VALUE) + // rc = Integer.MAX_VALUE; + rc = 1; + else + rc = (int) lc; + break; + case PropertyType.LONG: + long l1; + long l2; + // FIXME sometimes an empty string is set instead of a long + try { + l1 = v1.getLong(); + } catch (ValueFormatException ve) { + l1 = 0; + } + try { + l2 = v2.getLong(); + } catch (ValueFormatException ve) { + l2 = 0; + } + + lc = l1 - l2; + if (lc < Integer.MIN_VALUE) + rc = -1; + else if (lc > Integer.MAX_VALUE) + rc = 1; + else + rc = (int) lc; + break; + case PropertyType.DECIMAL: + BigDecimal bd1 = v1.getDecimal(); + BigDecimal bd2 = v2.getDecimal(); + rc = bd1.compareTo(bd2); + break; + case PropertyType.DOUBLE: + Double d1 = v1.getDouble(); + Double d2 = v2.getDouble(); + rc = d1.compareTo(d2); + break; + default: + throw new ArgeoException( + "Unimplemented comparaison for PropertyType " + + propertyType); + } + // If descending order, flip the direction + if (direction == DESCENDING) { + rc = -rc; + } + + } catch (RepositoryException re) { + throw new ArgeoException("Unexpected error " + + "while comparing nodes", re); + } + return rc; + } + + /** + * @param propertyType + * Corresponding JCR type + * @param propertyName + * name of the property to use. + */ + public void setColumn(int propertyType, String propertyName) { + if (this.propertyName != null && this.propertyName.equals(propertyName)) { + // Same column as last sort; toggle the direction + direction = 1 - direction; + } else { + // New column; do an ascending sort + this.propertyType = propertyType; + this.propertyName = propertyName; + direction = ASCENDING; + } + } + + // Getters and setters + protected String getPropertyName() { + return propertyName; + } + + protected void setPropertyName(String propertyName) { + this.propertyName = propertyName; + } + + protected int getPropertyType() { + return propertyType; + } + + protected void setPropertyType(int propertyType) { + this.propertyType = propertyType; + } + + protected int getDirection() { + return direction; + } + + protected void setDirection(int direction) { + this.direction = direction; + } +} \ No newline at end of file diff --git a/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/RowViewerComparator.java b/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/RowViewerComparator.java new file mode 100644 index 000000000..509f72324 --- /dev/null +++ b/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/RowViewerComparator.java @@ -0,0 +1,62 @@ +package org.argeo.eclipse.ui.jcr.lists; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.query.Row; + +import org.argeo.ArgeoException; +import org.eclipse.jface.viewers.Viewer; + +/** + * Base comparator to enable ordering on Table or Tree viewer that display Jcr + * rows + */ +public class RowViewerComparator extends NodeViewerComparator { + + protected String selectorName; + + public RowViewerComparator() { + } + + /** + * e1 and e2 must both be Jcr rows. + * + * @param viewer + * @param e1 + * @param e2 + * @return + */ + @Override + public int compare(Viewer viewer, Object e1, Object e2) { + try { + Node n1 = ((Row) e1).getNode(selectorName); + Node n2 = ((Row) e2).getNode(selectorName); + return super.compare(viewer, n1, n2); + } catch (RepositoryException re) { + throw new ArgeoException("Unexpected error " + + "while comparing nodes", re); + } + } + + /** + * @param propertyType + * Corresponding JCR type + * @param propertyName + * name of the property to use. + */ + public void setColumn(int propertyType, String selectorName, + String propertyName) { + if (this.selectorName != null && getPropertyName() != null + && this.selectorName.equals(selectorName) + && this.getPropertyName().equals(propertyName)) { + // Same column as last sort; toggle the direction + setDirection(1 - getDirection()); + } else { + // New column; do a descending sort + setPropertyType(propertyType); + setPropertyName(propertyName); + this.selectorName = selectorName; + setDirection(NodeViewerComparator.ASCENDING); + } + } +} \ No newline at end of file diff --git a/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/SimpleJcrNodeLabelProvider.java b/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/SimpleJcrNodeLabelProvider.java new file mode 100644 index 000000000..83b081b14 --- /dev/null +++ b/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/SimpleJcrNodeLabelProvider.java @@ -0,0 +1,96 @@ +package org.argeo.eclipse.ui.jcr.lists; + +import java.text.DateFormat; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.text.SimpleDateFormat; + +import javax.jcr.Node; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Value; + +import org.argeo.ArgeoException; +import org.eclipse.jface.viewers.ColumnLabelProvider; + +/** + * Base implementation of a label provider for widgets that display JCR Rows. + */ +public class SimpleJcrNodeLabelProvider extends ColumnLabelProvider { + + private final static String DEFAULT_DATE_FORMAT = "EEE, dd MMM yyyy"; + private final static String DEFAULT_NUMBER_FORMAT = "#,##0.0"; + + private DateFormat dateFormat; + private NumberFormat numberFormat; + + final private String propertyName; + + /** + * Default Label provider for a given property of a node. Using default + * pattern for date and number formating + */ + public SimpleJcrNodeLabelProvider(String propertyName) { + this.propertyName = propertyName; + dateFormat = new SimpleDateFormat(DEFAULT_DATE_FORMAT); + numberFormat = DecimalFormat.getInstance(); + ((DecimalFormat) numberFormat).applyPattern(DEFAULT_NUMBER_FORMAT); + } + + /** + * Label provider for a given property of a node optionally precising date + * and/or number format patterns + */ + public SimpleJcrNodeLabelProvider(String propertyName, + String dateFormatPattern, String numberFormatPattern) { + this.propertyName = propertyName; + dateFormat = new SimpleDateFormat( + dateFormatPattern == null ? DEFAULT_DATE_FORMAT + : dateFormatPattern); + numberFormat = DecimalFormat.getInstance(); + ((DecimalFormat) numberFormat) + .applyPattern(numberFormatPattern == null ? DEFAULT_NUMBER_FORMAT + : numberFormatPattern); + } + + @Override + public String getText(Object element) { + try { + Node currNode = (Node) element; + + Value value = null; + if (currNode.hasProperty(propertyName)) + value = currNode.getProperty(propertyName).getValue(); + else + return ""; + + switch (value.getType()) { + case PropertyType.STRING: + return value.getString(); + case PropertyType.BOOLEAN: + return "" + value.getBoolean(); + case PropertyType.DATE: + return dateFormat.format(value.getDate().getTime()); + case PropertyType.LONG: + return "" + value.getLong(); + case PropertyType.DECIMAL: + return numberFormat.format(value.getDecimal()); + case PropertyType.DOUBLE: + return numberFormat.format(value.getDouble()); + default: + throw new ArgeoException("Unimplemented label provider " + + "for property type " + value.getType()); + } + } catch (RepositoryException re) { + throw new ArgeoException("Unable to get text from row", re); + } + } + + public void setDateFormat(String dateFormatPattern) { + dateFormat = new SimpleDateFormat(dateFormatPattern); + } + + public void setNumberFormat(String numberFormatPattern) { + ((DecimalFormat) numberFormat).applyPattern(numberFormatPattern); + } +} \ No newline at end of file diff --git a/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/SimpleJcrRowLabelProvider.java b/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/SimpleJcrRowLabelProvider.java new file mode 100644 index 000000000..c44aa45cf --- /dev/null +++ b/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/SimpleJcrRowLabelProvider.java @@ -0,0 +1,45 @@ +package org.argeo.eclipse.ui.jcr.lists; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.query.Row; + +import org.argeo.ArgeoException; + +/** + * Base implementation of a label provider for widgets that display JCR Rows. + */ +public class SimpleJcrRowLabelProvider extends SimpleJcrNodeLabelProvider { + + final private String selectorName; + + /** + * Default Label provider for a given property of a row. Using default + * pattern for date and number formating + */ + public SimpleJcrRowLabelProvider(String selectorName, String propertyName) { + super(propertyName); + this.selectorName = selectorName; + } + + /** + * Label provider for a given property of a node optionally precising date + * and/or number format patterns + */ + public SimpleJcrRowLabelProvider(String selectorName, String propertyName, + String dateFormatPattern, String numberFormatPattern) { + super(propertyName, dateFormatPattern, numberFormatPattern); + this.selectorName = selectorName; + } + + @Override + public String getText(Object element) { + try { + Row currRow = (Row) element; + Node currNode = currRow.getNode(selectorName); + return super.getText(currNode); + } catch (RepositoryException re) { + throw new ArgeoException("Unable to get text from row", re); + } + } +} \ No newline at end of file diff --git a/demo/log4j.properties b/demo/log4j.properties index 9f9b7c645..13f949ff5 100644 --- a/demo/log4j.properties +++ b/demo/log4j.properties @@ -9,6 +9,7 @@ log4j.logger.org.argeo.server.webextender.TomcatDeployer=INFO #log4j.logger.org.apache.commons.exec=DEBUG #log4j.logger.org.apache.jackrabbit.webdav=DEBUG #log4j.logger.org.apache.jackrabbit.remote=DEBUG +#log4j.logger.org.apache.jackrabbit.core.observation=DEBUG log4j.logger.org.apache.catalina=INFO log4j.logger.org.apache.coyote=INFO diff --git a/security/plugins/org.argeo.security.ui.admin/META-INF/spring/commands.xml b/security/plugins/org.argeo.security.ui.admin/META-INF/spring/commands.xml index 65a88be37..e0a435e07 100644 --- a/security/plugins/org.argeo.security.ui.admin/META-INF/spring/commands.xml +++ b/security/plugins/org.argeo.security.ui.admin/META-INF/spring/commands.xml @@ -26,6 +26,14 @@ scope="prototype"> + + + + + + + + + @@ -104,6 +109,12 @@ + + + + + + 4.0.0 org.argeo.commons.security @@ -49,6 +51,12 @@ + + org.argeo.commons.security + org.argeo.security.ui + 2.1.3-SNAPSHOT + + org.argeo.commons.security org.argeo.security.core @@ -56,6 +64,12 @@ + + org.argeo.commons.base + org.argeo.eclipse.ui.jcr + 2.1.3-SNAPSHOT + + org.argeo.commons.base org.argeo.eclipse.ui diff --git a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/SecurityAdminImages.java b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/SecurityAdminImages.java new file mode 100644 index 000000000..f7ffa9c8d --- /dev/null +++ b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/SecurityAdminImages.java @@ -0,0 +1,33 @@ +/* + * Argeo Connect - Data management and communications + * Copyright (C) 2012 Argeo GmbH + * + * 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 + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this Program, or any covered work, by linking or combining it + * with software covered by the terms of the Eclipse Public License, the + * licensors of this Program grant you additional permission to convey the + * resulting work. Corresponding Source for a non-source form of such a + * combination shall include the source code for the parts of such software + * which are used as well as that of the covered work. + */ +package org.argeo.security.ui.admin; + +/** Shared icons that must be declared programmatically . */ +public class SecurityAdminImages { + @SuppressWarnings("unused") + private final static String PREFIX = "icons/"; +} diff --git a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/UserTableComposite.java b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/UserTableComposite.java new file mode 100644 index 000000000..5e969653c --- /dev/null +++ b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/UserTableComposite.java @@ -0,0 +1,412 @@ +package org.argeo.security.ui.admin; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.query.QueryManager; +import javax.jcr.query.QueryResult; +import javax.jcr.query.qom.Constraint; +import javax.jcr.query.qom.Ordering; +import javax.jcr.query.qom.QueryObjectModel; +import javax.jcr.query.qom.QueryObjectModelFactory; +import javax.jcr.query.qom.Selector; +import javax.jcr.query.qom.StaticOperand; + +import org.argeo.ArgeoException; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.jcr.JcrUiUtils; +import org.argeo.eclipse.ui.jcr.lists.ColumnDefinition; +import org.argeo.eclipse.ui.jcr.lists.NodeViewerComparator; +import org.argeo.eclipse.ui.jcr.lists.SimpleJcrNodeLabelProvider; +import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils; +import org.argeo.eclipse.ui.utils.ViewerUtils; +import org.argeo.jcr.ArgeoNames; +import org.argeo.jcr.ArgeoTypes; +import org.argeo.jcr.JcrUtils; +import org.eclipse.jface.viewers.CheckboxTableViewer; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.Text; + +public class UserTableComposite extends Composite implements ArgeoNames { + // private final static Log log = + // LogFactory.getLog(UserTableComposite.class); + + private TableViewer usersViewer; + private Text filterTxt; + private final static String FILTER_HELP_MSG = "Type filter criterion " + + "separated by a space"; + private Session session; + + private Font italic; + private Font bold; + + private boolean hasFilter; + private boolean hasSelectionColumn; + + // private List selectedItems = new ArrayList(); + + /** + * Overwrite to display other columns + */ + public List getColumnsDef() { + List columnDefs = new ArrayList(); + + // User ID + columnDefs.add(new ColumnDefinition(null, ARGEO_USER_ID, + PropertyType.STRING, "User ID", 100)); + // Displayed name + columnDefs.add(new ColumnDefinition(null, Property.JCR_TITLE, + PropertyType.STRING, "Name", 150)); + + // E-mail + columnDefs.add(new ColumnDefinition(null, ARGEO_PRIMARY_EMAIL, + PropertyType.STRING, "E-mail", 150)); + + // Description + columnDefs.add(new ColumnDefinition(null, Property.JCR_DESCRIPTION, + PropertyType.STRING, "Description", 200)); + + return columnDefs; + } + + public UserTableComposite(Composite parent, int style, Session session) { + super(parent, style); + this.session = session; + } + + /** + * + * @param addFilter + * choose to add a field to filter results or not + * @param addSelection + * choose to add a column to select some of the displayed results + * or not + */ + public void populate(boolean addFilter, boolean addSelection) { + // initialization + Composite parent = this; + italic = EclipseUiUtils.getItalicFont(parent); + bold = EclipseUiUtils.getBoldFont(parent); + hasFilter = addFilter; + hasSelectionColumn = addSelection; + + // Main Layout + this.setLayout(new GridLayout(1, false)); + if (hasFilter) + createFilterPart(parent); + usersViewer = createTableViewer(parent); + EclipseUiSpecificUtils.enableToolTipSupport(usersViewer); + usersViewer.setContentProvider(new UsersContentProvider()); + refreshFilteredList(); + } + + public List getSelectedUsers() { + if (hasSelectionColumn) { + Object[] elements = ((CheckboxTableViewer) usersViewer) + .getCheckedElements(); + + List result = new ArrayList(); + for (Object obj : elements) { + result.add((Node) obj); + } + return result; + } else + throw new ArgeoException("Unvalid request: no selection column " + + "has been created for the current table"); + } + + /** Returns the User table viewer, typically to add doubleclick listener */ + public TableViewer getTableViewer() { + return usersViewer; + } + + private TableViewer createTableViewer(final Composite parent) { + int style = SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL; + if (hasSelectionColumn) + style = style | SWT.CHECK; + + Table table = new Table(parent, style); + table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + TableViewer viewer; + if (hasSelectionColumn) + viewer = new CheckboxTableViewer(table); + else + viewer = new TableViewer(table); + table.setLinesVisible(true); + table.setHeaderVisible(true); + + // pass a mapping between col index and property name to the comparator. + // List propertiesList = new ArrayList(); + + TableViewerColumn column; + int offset = 0; + // if (hasSelectionColumn) { + if (hasSelectionColumn) { + offset = 1; + column = ViewerUtils.createTableViewerColumn(viewer, "", SWT.NONE, + 25); + // column.setEditingSupport(new SelectedEditingSupport(viewer)); + column.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + return null; + } + }); + SelectionAdapter selectionAdapter = new SelectionAdapter() { + boolean allSelected = false; + + @Override + public void widgetSelected(SelectionEvent e) { + allSelected = !allSelected; + ((CheckboxTableViewer) usersViewer) + .setAllChecked(allSelected); + } + }; + column.getColumn().addSelectionListener(selectionAdapter); + } + + // Create other columns + List colDefs = getColumnsDef(); + + NodeViewerComparator comparator = new NodeViewerComparator(); + int i = offset; + for (ColumnDefinition colDef : colDefs) { + column = ViewerUtils.createTableViewerColumn(viewer, + colDef.getHeaderLabel(), SWT.NONE, colDef.getColumnSize()); + column.setLabelProvider(new CLProvider(colDef.getPropertyName())); + column.getColumn().addSelectionListener( + JcrUiUtils.getNodeSelectionAdapter(i, + colDef.getPropertyType(), colDef.getPropertyName(), + comparator, viewer)); + i++; + } + + // IMPORTANT: initialize comparator before setting it + ColumnDefinition firstCol = colDefs.get(0); + comparator.setColumn(firstCol.getPropertyType(), + firstCol.getPropertyName()); + viewer.setComparator(comparator); + + return viewer; + } + + // private class SelectedEditingSupport extends EditingSupport { + // private final TableViewer viewer; + // + // public SelectedEditingSupport(TableViewer viewer) { + // super(viewer); + // this.viewer = viewer; + // } + // + // @Override + // protected CellEditor getCellEditor(Object element) { + // return new CheckboxCellEditor(viewer.getTable()); + // } + // + // @Override + // protected boolean canEdit(Object element) { + // return true; + // } + // + // @Override + // protected Object getValue(Object element) { + // return selectedItems.contains(element); + // } + // + // @Override + // protected void setValue(Object element, Object value) { + // if ((Boolean) value && !selectedItems.contains(element)) + // selectedItems.add((Node) element); + // else if (!(Boolean) value && selectedItems.contains(element)) + // selectedItems.remove((Node) element); + // viewer.update(element, null); + // } + // } + + private class CLProvider extends SimpleJcrNodeLabelProvider { + + public CLProvider(String propertyName) { + super(propertyName); + } + + public String getToolTipText(Object element) { + return getText(element); + } + + @Override + public Font getFont(Object elem) { + // self + String username = getProperty(elem, ARGEO_USER_ID); + if (username.equals(session.getUserID())) + return bold; + + // disabled + try { + Node userProfile = (Node) elem; + // Node userProfile = userHome.getNode(ARGEO_PROFILE); + if (!userProfile.getProperty(ARGEO_ENABLED).getBoolean()) + return italic; + else + return null; + } catch (RepositoryException e) { + throw new ArgeoException("Cannot get font for " + username, e); + } + } + } + + @Override + public boolean setFocus() { + usersViewer.getTable().setFocus(); + return true; + } + + @Override + public void dispose() { + super.dispose(); + } + + public void refresh() { + refreshFilteredList(); + } + + private String getProperty(Object element, String name) { + try { + Node userProfile = (Node) element; + return userProfile.hasProperty(name) ? userProfile + .getProperty(name).getString() : ""; + } catch (RepositoryException e) { + throw new ArgeoException("Cannot get property " + name, e); + } + } + + private class UsersContentProvider implements IStructuredContentProvider { + + public Object[] getElements(Object inputElement) { + return (Object[]) inputElement; + } + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + } + + /* MANAGE FILTER */ + private void createFilterPart(Composite parent) { + // Text Area for the filter + filterTxt = new Text(parent, SWT.BORDER | SWT.SEARCH | SWT.ICON_SEARCH + | SWT.ICON_CANCEL); + filterTxt.setMessage(FILTER_HELP_MSG); + filterTxt.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL + | GridData.HORIZONTAL_ALIGN_FILL)); + filterTxt.addModifyListener(new ModifyListener() { + + public void modifyText(ModifyEvent event) { + refreshFilteredList(); + } + }); + } + + /** + * Refresh the user list: caller might overwrite in order to display a + * subset of all users + */ + protected void refreshFilteredList() { + // if (hasSelectionColumn) + // selectedItems.clear(); + + List nodes; + try { + nodes = JcrUtils.nodeIteratorToList(listFilteredElements(session, + hasFilter ? filterTxt.getText() : null)); + usersViewer.setInput(nodes.toArray()); + } catch (RepositoryException e) { + throw new ArgeoException("Unable to list users", e); + } + } + + // /** + // * Enable a children class to directly set an array of users to the + // viewer, + // * when such a behaviour cannot be achieved by overwriting the + // * refreshFilteredList() method + // */ + // protected void setFilteredList(Object[] nodes) { + // // if (hasSelectionColumn) + // // selectedItems.clear(); + // usersViewer.setInput(nodes); + // } + + /** + * Build repository request : caller might overwrite in order to display a + * subset of all users + */ + protected NodeIterator listFilteredElements(Session session, String filter) + throws RepositoryException { + QueryManager queryManager = session.getWorkspace().getQueryManager(); + QueryObjectModelFactory factory = queryManager.getQOMFactory(); + + Selector source = factory.selector(ArgeoTypes.ARGEO_USER_PROFILE, + ArgeoTypes.ARGEO_USER_PROFILE); + + // // Create a dynamic operand for each property on which we want to + // filter + // DynamicOperand userIdDO = factory.propertyValue( + // source.getSelectorName(), ARGEO_USER_ID); + // DynamicOperand fullNameDO = factory.propertyValue( + // source.getSelectorName(), Property.JCR_TITLE); + // DynamicOperand mailDO = + // factory.propertyValue(source.getSelectorName(), + // ARGEO_PRIMARY_EMAIL); + + // Default Constraint: no source artifacts + Constraint defaultC = null; + // Build constraints based the textArea content + if (filter != null && !"".equals(filter.trim())) { + // Parse the String + String[] strs = filter.trim().split(" "); + for (String token : strs) { + StaticOperand so = factory.literal(session.getValueFactory() + .createValue("*" + token + "*")); + Constraint currC = factory.fullTextSearch( + source.getSelectorName(), null, so); + if (defaultC == null) + defaultC = currC; + else + defaultC = factory.and(defaultC, currC); + } + } + + Ordering order = factory.ascending(factory.propertyValue( + source.getSelectorName(), ARGEO_USER_ID)); + Ordering[] orderings = { order }; + + QueryObjectModel query = factory.createQuery(source, defaultC, + orderings, null); + + QueryResult result = query.execute(); + return result.getNodes(); + } +} \ No newline at end of file diff --git a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/DeleteUser.java b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/DeleteUser.java index 0b180c3cc..ccf360fd0 100644 --- a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/DeleteUser.java +++ b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/DeleteUser.java @@ -29,7 +29,6 @@ import org.argeo.ArgeoException; import org.argeo.jcr.ArgeoNames; import org.argeo.jcr.JcrUtils; import org.argeo.security.UserAdminService; -import org.argeo.security.ui.admin.views.UsersView; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; @@ -38,7 +37,7 @@ import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.ui.handlers.HandlerUtil; -/** Deletes the select user nodes */ +/** Deletes the selected user nodes */ public class DeleteUser extends AbstractHandler { private final static Log log = LogFactory.getLog(DeleteUser.class); @@ -92,10 +91,10 @@ public class DeleteUser extends AbstractHandler { } userAdminService.synchronize(); - UsersView view = (UsersView) HandlerUtil - .getActiveWorkbenchWindow(event).getActivePage() - .findView(UsersView.ID); - view.refresh(); + // UsersView view = (UsersView) HandlerUtil + // .getActiveWorkbenchWindow(event).getActivePage() + // .findView(UsersView.ID); + // view.refresh(); return null; } diff --git a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/NewUser.java b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/NewUser.java index 60146099a..660896de8 100644 --- a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/NewUser.java +++ b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/NewUser.java @@ -28,7 +28,7 @@ import org.eclipse.core.commands.ExecutionException; import org.eclipse.jface.wizard.WizardDialog; import org.eclipse.ui.handlers.HandlerUtil; -/** Command handler to set visible or open a Argeo user. */ +/** Launch a wizard that enables creation of a new user. */ public class NewUser extends AbstractHandler { private Repository repository; private UserAdminService userAdminService; diff --git a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/RefreshUsersList.java b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/RefreshUsersList.java index 54c6f4f71..0904ed2fb 100644 --- a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/RefreshUsersList.java +++ b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/RefreshUsersList.java @@ -36,7 +36,7 @@ import org.eclipse.core.commands.ExecutionException; import org.eclipse.ui.handlers.HandlerUtil; /** - * Refreshes the main EBI list, removing nodes which are not referenced by user + * Refreshes the main user list, removing nodes which are not referenced by user * admin service. */ public class RefreshUsersList extends AbstractHandler { diff --git a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/UserBatchUpdate.java b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/UserBatchUpdate.java new file mode 100644 index 000000000..657dfc70a --- /dev/null +++ b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/UserBatchUpdate.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * 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.argeo.security.ui.admin.commands; + +import javax.jcr.Repository; +import javax.jcr.Session; + +import org.argeo.jcr.JcrUtils; +import org.argeo.security.UserAdminService; +import org.argeo.security.jcr.JcrSecurityModel; +import org.argeo.security.ui.admin.wizards.UserBatchUpdateWizard; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.ui.handlers.HandlerUtil; + +/** Launch a wizard to update various properties about users in JCR. */ +public class UserBatchUpdate extends AbstractHandler { + private Repository repository; + private UserAdminService userAdminService; + private JcrSecurityModel jcrSecurityModel; + + public Object execute(ExecutionEvent event) throws ExecutionException { + Session session = null; + try { + session = repository.login(); + UserBatchUpdateWizard userBatchUpdateWizard = new UserBatchUpdateWizard(session, + userAdminService, jcrSecurityModel); + WizardDialog dialog = new WizardDialog( + HandlerUtil.getActiveShell(event), userBatchUpdateWizard); + dialog.open(); + } catch (Exception e) { + throw new ExecutionException("Cannot open wizard", e); + } finally { + JcrUtils.logoutQuietly(session); + } + return null; + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + + public void setUserAdminService(UserAdminService userAdminService) { + this.userAdminService = userAdminService; + } + + public void setJcrSecurityModel(JcrSecurityModel jcrSecurityModel) { + this.jcrSecurityModel = jcrSecurityModel; + } + +} diff --git a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/views/UsersView.java b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/views/UsersView.java index 6c8986b2b..5f47741f5 100644 --- a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/views/UsersView.java +++ b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/views/UsersView.java @@ -15,293 +15,71 @@ */ package org.argeo.security.ui.admin.views; -import java.util.ArrayList; -import java.util.List; - import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.Property; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.observation.Event; import javax.jcr.observation.EventIterator; import javax.jcr.observation.EventListener; -import javax.jcr.query.QueryManager; -import javax.jcr.query.QueryResult; -import javax.jcr.query.qom.Constraint; -import javax.jcr.query.qom.DynamicOperand; -import javax.jcr.query.qom.Ordering; -import javax.jcr.query.qom.QueryObjectModel; -import javax.jcr.query.qom.QueryObjectModelFactory; -import javax.jcr.query.qom.Selector; -import javax.jcr.query.qom.StaticOperand; import org.argeo.ArgeoException; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils; import org.argeo.eclipse.ui.utils.CommandUtils; import org.argeo.jcr.ArgeoJcrConstants; import org.argeo.jcr.ArgeoNames; import org.argeo.jcr.ArgeoTypes; import org.argeo.jcr.JcrUtils; import org.argeo.security.ui.admin.SecurityAdminPlugin; +import org.argeo.security.ui.admin.UserTableComposite; import org.argeo.security.ui.admin.commands.OpenArgeoUserEditor; -import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; -import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.TableViewerColumn; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.graphics.Font; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.Display; import org.eclipse.ui.part.ViewPart; -/** List all users. */ +/** List all users with filter. */ public class UsersView extends ViewPart implements ArgeoNames { public final static String ID = SecurityAdminPlugin.PLUGIN_ID + ".adminUsersView"; - private TableViewer viewer; - private Text filterTxt; - private final static String FILTER_HELP_MSG = "Type filter criterion " - + "separated by a space (on user ID, name and E-mail)"; - private final static Image FILTER_RESET = SecurityAdminPlugin - .getImageDescriptor("icons/clear.gif").createImage(); - + private UserTableComposite userTableCmp; private Session session; - - private UserStructureListener userStructureListener; - private UserPropertiesListener userPropertiesListener; - - private Font italic; - private Font bold; + private JcrUserListener userStructureListener; + private JcrUserListener userPropertiesListener; @Override public void createPartControl(Composite parent) { - italic = EclipseUiUtils.getItalicFont(parent); - bold = EclipseUiUtils.getBoldFont(parent); + parent.setLayout(new FillLayout()); - // Main Layout - GridLayout layout = new GridLayout(1, false); - parent.setLayout(layout); - createFilterPart(parent); + // Create the composite that displays the list and a filter + userTableCmp = new UserTableComposite(parent, SWT.NO_FOCUS, session); + userTableCmp.populate(true, false); - viewer = createTableViewer(parent); - EclipseUiSpecificUtils.enableToolTipSupport(viewer); - viewer.setContentProvider(new UsersContentProvider()); - viewer.addDoubleClickListener(new ViewDoubleClickListener()); - getViewSite().setSelectionProvider(viewer); + // Configure + userTableCmp.getTableViewer().addDoubleClickListener( + new ViewDoubleClickListener()); + getViewSite().setSelectionProvider(userTableCmp.getTableViewer()); - userStructureListener = new UserStructureListener(); + // Add listener to refresh the list when something changes + userStructureListener = new JcrUserListener(getSite().getShell() + .getDisplay()); JcrUtils.addListener(session, userStructureListener, Event.NODE_ADDED | Event.NODE_REMOVED, ArgeoJcrConstants.PEOPLE_BASE_PATH, null); - userPropertiesListener = new UserPropertiesListener(); + userPropertiesListener = new JcrUserListener(getSite().getShell() + .getDisplay()); JcrUtils.addListener(session, userStructureListener, Event.PROPERTY_CHANGED | Event.PROPERTY_ADDED | Event.PROPERTY_REMOVED, ArgeoJcrConstants.PEOPLE_BASE_PATH, ArgeoTypes.ARGEO_USER_PROFILE); - - refreshFilteredList(); - } - - protected TableViewer createTableViewer(final Composite parent) { - - Table table = new Table(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); - GridData gd = new GridData(SWT.FILL, SWT.FILL, false, false); - gd.grabExcessHorizontalSpace = true; - gd.grabExcessVerticalSpace = true; - table.setLayoutData(gd); - - TableViewer viewer = new TableViewer(table); - table.setLinesVisible(true); - table.setHeaderVisible(true); - - // pass a mapping between col index and property name to the comparator. - List propertiesList = new ArrayList(); - - // User ID - TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE); - column.getColumn().setText("User ID"); - column.getColumn().setWidth(100); - column.getColumn().addSelectionListener(getSelectionAdapter(0)); - propertiesList.add(ARGEO_USER_ID); - column.setLabelProvider(new CLProvider() { - public String getText(Object elem) { - return getProperty(elem, ARGEO_USER_ID); - // if (username.equals(session.getUserID())) - // return "[" + username + "]"; - // else - // return username; - } - }); - - // Displayed name - column = new TableViewerColumn(viewer, SWT.NONE); - column.getColumn().setText("Name"); - column.getColumn().setWidth(150); - column.getColumn().addSelectionListener(getSelectionAdapter(1)); - propertiesList.add(Property.JCR_TITLE); - column.setLabelProvider(new CLProvider() { - public String getText(Object elem) { - return getProperty(elem, Property.JCR_TITLE); - } - }); - - // E-mail - column = new TableViewerColumn(viewer, SWT.NONE); - column.getColumn().setText("E-mail"); - column.getColumn().setWidth(150); - column.getColumn().addSelectionListener(getSelectionAdapter(2)); - propertiesList.add(ARGEO_PRIMARY_EMAIL); - column.setLabelProvider(new CLProvider() { - public String getText(Object elem) { - return getProperty(elem, ARGEO_PRIMARY_EMAIL); - } - }); - - // Description - column = new TableViewerColumn(viewer, SWT.NONE); - column.getColumn().setText("Description"); - column.getColumn().setWidth(200); - column.getColumn().addSelectionListener(getSelectionAdapter(3)); - propertiesList.add(Property.JCR_DESCRIPTION); - column.setLabelProvider(new CLProvider() { - public String getText(Object elem) { - return getProperty(elem, Property.JCR_DESCRIPTION); - } - }); - - viewer.setComparator(new UsersViewerComparator(propertiesList)); - - return viewer; - } - - private class CLProvider extends ColumnLabelProvider { - - public String getToolTipText(Object element) { - return getText(element); - } - - @Override - public Font getFont(Object elem) { - // self - String username = getProperty(elem, ARGEO_USER_ID); - if (username.equals(session.getUserID())) - return bold; - - // disabled - try { - Node userProfile = (Node) elem; - // Node userProfile = userHome.getNode(ARGEO_PROFILE); - if (!userProfile.getProperty(ARGEO_ENABLED).getBoolean()) - return italic; - else - return null; - } catch (RepositoryException e) { - throw new ArgeoException("Cannot get font for " + username, e); - } - } - - } - - private SelectionAdapter getSelectionAdapter(final int index) { - SelectionAdapter selectionAdapter = new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - UsersViewerComparator comparator = (UsersViewerComparator) viewer - .getComparator(); - - if (index == comparator.getSortColumn()) { - comparator.setAscending(!comparator.isAscending()); - } - comparator.setSortColumn(index); - viewer.getTable().setSortColumn( - viewer.getTable().getColumn(index)); - viewer.getTable().setSortDirection( - comparator.isAscending() ? SWT.UP : SWT.DOWN); - viewer.refresh(false); - } - }; - return selectionAdapter; - } - - private class UsersViewerComparator extends ViewerComparator { - - private List propertyList; - private int sortColumn = 0; - private boolean ascending = true; - // use this to enable two levels sort - @SuppressWarnings("unused") - private int lastSortColumn = 0; - @SuppressWarnings("unused") - private boolean lastAscending = true; - - public UsersViewerComparator(List propertyList) { - super(); - this.propertyList = propertyList; - } - - public int compare(Viewer viewer, Object e1, Object e2) { - String s1 = getProperty(e1, propertyList.get(sortColumn)); - String s2 = getProperty(e2, propertyList.get(sortColumn)); - int result = super.compare(viewer, s1, s2); - return ascending ? result : (-1) * result; - } - - /** - * @return Returns the sortColumn. - */ - public int getSortColumn() { - return sortColumn; - } - - /** - * @param sortColumn - * The sortColumn to set. - */ - public void setSortColumn(int sortColumn) { - if (this.sortColumn != sortColumn) { - lastSortColumn = this.sortColumn; - lastAscending = this.ascending; - this.sortColumn = sortColumn; - } - } - - /** - * @return Returns the ascending. - */ - public boolean isAscending() { - return ascending; - } - - /** - * @param ascending - * The ascending to set. - */ - public void setAscending(boolean ascending) { - this.ascending = ascending; - } } @Override public void setFocus() { - viewer.getTable().setFocus(); + userTableCmp.setFocus(); } @Override @@ -316,76 +94,31 @@ public class UsersView extends ViewPart implements ArgeoNames { } public void refresh() { - // viewer.refresh(); - refreshFilteredList(); - } - - protected String getProperty(Node userProfile, String name) - throws RepositoryException { - return userProfile.hasProperty(name) ? userProfile.getProperty(name) - .getString() : ""; - } - - protected String getProperty(Object element, String name) { - try { - Node userProfile = (Node) element; - // Node userProfile = userHome.getNode(ARGEO_PROFILE); - return userProfile.hasProperty(name) ? userProfile - .getProperty(name).getString() : ""; - } catch (RepositoryException e) { - throw new ArgeoException("Cannot get property " + name, e); - } + this.getSite().getShell().getDisplay().asyncExec(new Runnable() { + @Override + public void run() { + userTableCmp.refresh(); + } + }); } - private class UserStructureListener implements EventListener { + private class JcrUserListener implements EventListener { + private final Display display; - @Override - public void onEvent(EventIterator events) { - // viewer.refresh(); - refreshFilteredList(); + public JcrUserListener(Display display) { + super(); + this.display = display; } - } - - private class UserPropertiesListener implements EventListener { @Override public void onEvent(EventIterator events) { - // viewer.refresh(); - refreshFilteredList(); - } - } - - private class UsersContentProvider implements IStructuredContentProvider { - - public Object[] getElements(Object inputElement) { - return (Object[]) inputElement; - - // try { - // Query query = session - // .getWorkspace() - // .getQueryManager() - // .createQuery( - // "select * from [" - // + ArgeoTypes.ARGEO_USER_PROFILE + "]", - // Query.JCR_SQL2); - // NodeIterator nit = query.execute().getNodes(); - // List userProfiles = new ArrayList(); - // while (nit.hasNext()) { - // userProfiles.add(nit.nextNode()); - // } - // return userProfiles.toArray(); - // } catch (RepositoryException e) { - // throw new ArgeoException("Cannot list users", e); - // } - // // return userAdminService.listUsers().toArray(); - } - - public void dispose() { - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + display.asyncExec(new Runnable() { + @Override + public void run() { + userTableCmp.refresh(); + } + }); } - } class ViewDoubleClickListener implements IDoubleClickListener { @@ -408,109 +141,4 @@ public class UsersView extends ViewPart implements ArgeoNames { } } } - - /* MANAGE FILTER */ - private void createFilterPart(Composite parent) { - Composite header = new Composite(parent, SWT.FILL); - header.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); - header.setLayout(new GridLayout(2, false)); - - // Text Area to filter - filterTxt = new Text(header, SWT.BORDER | SWT.SINGLE); - filterTxt.setMessage(FILTER_HELP_MSG); - GridData gd = new GridData(SWT.FILL, SWT.FILL, false, false); - gd.grabExcessHorizontalSpace = true; - filterTxt.setLayoutData(gd); - filterTxt.addModifyListener(new ModifyListener() { - public void modifyText(ModifyEvent event) { - refreshFilteredList(); - } - }); - - Button resetBtn = new Button(header, SWT.PUSH); - resetBtn.setImage(FILTER_RESET); - resetBtn.addSelectionListener(new SelectionListener() { - public void widgetSelected(SelectionEvent e) { - resetFilter(); - } - - public void widgetDefaultSelected(SelectionEvent e) { - } - }); - - } - - private void resetFilter() { - filterTxt.setText(""); - filterTxt.setMessage(FILTER_HELP_MSG); - } - - private void refreshFilteredList() { - List nodes; - try { - nodes = JcrUtils.nodeIteratorToList(listFilteredElements(session, - filterTxt.getText())); - viewer.setInput(nodes.toArray()); - } catch (RepositoryException e) { - throw new ArgeoException("Unable to list users", e); - } - } - - /** Build repository request */ - private NodeIterator listFilteredElements(Session session, String filter) - throws RepositoryException { - QueryManager queryManager = session.getWorkspace().getQueryManager(); - QueryObjectModelFactory factory = queryManager.getQOMFactory(); - - final String bundleArtifactsSelector = "userProfiles"; - Selector source = factory.selector(ArgeoTypes.ARGEO_USER_PROFILE, - bundleArtifactsSelector); - - // Create a dynamic operand for each property on which we want to filter - DynamicOperand userIdDO = factory.propertyValue( - source.getSelectorName(), ARGEO_USER_ID); - DynamicOperand fullNameDO = factory.propertyValue( - source.getSelectorName(), Property.JCR_TITLE); - DynamicOperand mailDO = factory.propertyValue(source.getSelectorName(), - ARGEO_PRIMARY_EMAIL); - - // Default Constraint: no source artifacts - Constraint defaultC = null; - - // Build constraints based the textArea content - if (filter != null && !"".equals(filter.trim())) { - // Parse the String - String[] strs = filter.trim().split(" "); - for (String token : strs) { - token = token.replace('*', '%'); - StaticOperand so = factory.literal(session.getValueFactory() - .createValue("%" + token + "%")); - - Constraint currC = factory.comparison(userIdDO, - QueryObjectModelFactory.JCR_OPERATOR_LIKE, so); - currC = factory.or(currC, factory.comparison(fullNameDO, - QueryObjectModelFactory.JCR_OPERATOR_LIKE, so)); - currC = factory.or(currC, factory.comparison(mailDO, - QueryObjectModelFactory.JCR_OPERATOR_LIKE, so)); - - if (defaultC == null) - defaultC = currC; - else - defaultC = factory.and(defaultC, currC); - } - } - - Ordering order = factory.ascending(factory.propertyValue( - bundleArtifactsSelector, ARGEO_USER_ID)); - // Ordering order2 = factory.ascending(factory.propertyValue( - // bundleArtifactsSelector, ARGEO_PRIMARY_EMAIL)); - // Ordering[] orderings = { order, order2 }; - Ordering[] orderings = { order }; - - QueryObjectModel query = factory.createQuery(source, defaultC, - orderings, null); - - QueryResult result = query.execute(); - return result.getNodes(); - } } \ No newline at end of file diff --git a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/wizards/UserBatchUpdateWizard.java b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/wizards/UserBatchUpdateWizard.java new file mode 100644 index 000000000..933a39019 --- /dev/null +++ b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/wizards/UserBatchUpdateWizard.java @@ -0,0 +1,570 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * 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.argeo.security.ui.admin.wizards; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.version.VersionManager; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.ArgeoException; +import org.argeo.ArgeoMonitor; +import org.argeo.eclipse.ui.EclipseArgeoMonitor; +import org.argeo.jcr.ArgeoNames; +import org.argeo.jcr.JcrUtils; +import org.argeo.security.UserAdminService; +import org.argeo.security.jcr.JcrSecurityModel; +import org.argeo.security.jcr.JcrUserDetails; +import org.argeo.security.ui.PrivilegedJob; +import org.argeo.security.ui.admin.SecurityAdminPlugin; +import org.argeo.security.ui.admin.UserTableComposite; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.dialogs.IPageChangeProvider; +import org.eclipse.jface.dialogs.IPageChangedListener; +import org.eclipse.jface.dialogs.PageChangedEvent; +import org.eclipse.jface.wizard.IWizardContainer; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** Wizard to update users */ +public class UserBatchUpdateWizard extends Wizard { + private final static Log log = LogFactory + .getLog(UserBatchUpdateWizard.class); + private Session session; + private UserAdminService userAdminService; + + // pages + private ChooseCommandWizardPage chooseCommandPage; + private ChooseUsersWizardPage userListPage; + private ValidateAndLaunchWizardPage validatePage; + + // ///////////////////////////////////////////////// + // / Definition of the various implemented commands + private final static String CMD_UPDATE_PASSWORD = "resetPassword"; + private final static String CMD_GROUP_MEMBERSHIP = "groupMembership"; + + private final Map commands = new HashMap() { + private static final long serialVersionUID = 1L; + { + put("Enable user(s)", ArgeoNames.ARGEO_ENABLED); + put("Expire credentials", ArgeoNames.ARGEO_CREDENTIALS_NON_EXPIRED); + put("Expire account(s)", ArgeoNames.ARGEO_ACCOUNT_NON_EXPIRED); + put("Lock account(s)", ArgeoNames.ARGEO_ACCOUNT_NON_LOCKED); + put("Reset password(s)", CMD_UPDATE_PASSWORD); + // TODO implement role / group management + // put("Add/Remove from group", CMD_GROUP_MEMBERSHIP); + } + }; + + public UserBatchUpdateWizard(Session session, + UserAdminService userAdminService, JcrSecurityModel jcrSecurityModel) { + this.session = session; + this.userAdminService = userAdminService; + // this.jcrSecurityModel = jcrSecurityModel; + } + + @Override + public void addPages() { + chooseCommandPage = new ChooseCommandWizardPage(); + addPage(chooseCommandPage); + userListPage = new ChooseUsersWizardPage(session); + addPage(userListPage); + validatePage = new ValidateAndLaunchWizardPage(session); + addPage(validatePage); + } + + @Override + public boolean performFinish() { + if (!canFinish()) + return false; + + UpdateJob job = null; + if (ArgeoNames.ARGEO_ENABLED.equals(chooseCommandPage.getCommand())) { + job = new UpdateBoolean(session, userListPage.getSelectedUsers(), + ArgeoNames.ARGEO_ENABLED, + chooseCommandPage.getBoleanValue()); + } else if (ArgeoNames.ARGEO_CREDENTIALS_NON_EXPIRED + .equals(chooseCommandPage.getCommand())) { + job = new UpdateBoolean(session, userListPage.getSelectedUsers(), + ArgeoNames.ARGEO_CREDENTIALS_NON_EXPIRED, + chooseCommandPage.getBoleanValue()); + } else if (ArgeoNames.ARGEO_ACCOUNT_NON_EXPIRED + .equals(chooseCommandPage.getCommand())) { + job = new UpdateBoolean(session, userListPage.getSelectedUsers(), + ArgeoNames.ARGEO_ACCOUNT_NON_EXPIRED, + chooseCommandPage.getBoleanValue()); + } else if (ArgeoNames.ARGEO_ACCOUNT_NON_LOCKED.equals(chooseCommandPage + .getCommand())) { + job = new UpdateBoolean(session, userListPage.getSelectedUsers(), + ArgeoNames.ARGEO_ACCOUNT_NON_LOCKED, + chooseCommandPage.getBoleanValue()); + } else if (CMD_UPDATE_PASSWORD.equals(chooseCommandPage.getCommand())) { + String newValue = chooseCommandPage.getPwdValue(); + if (newValue == null) + throw new ArgeoException( + "Password cannot be null or an empty string"); + job = new ResetPassword(session, userAdminService, + userListPage.getSelectedUsers(), newValue); + } + + if (job != null) + job.schedule(); + return true; + } + + public void setSession(Session session) { + this.session = session; + } + + public boolean canFinish() { + if (this.getContainer().getCurrentPage() == validatePage) + return true; + return false; + } + + // ///////////////////////// + // REEL UPDATE JOB + private class UpdateBoolean extends UpdateJob { + private String propertyName; + private boolean value; + + public UpdateBoolean(Session session, List nodesToUpdate, + String propertyName, boolean value) { + super(session, nodesToUpdate); + this.propertyName = propertyName; + this.value = value; + } + + protected void doUpdate(Node node) { + try { + node.setProperty(propertyName, value); + } catch (RepositoryException re) { + throw new ArgeoException( + "Unable to update boolean value for node " + node, re); + } + } + } + + private class ResetPassword extends UpdateJob { + private String newValue; + private UserAdminService userAdminService; + + public ResetPassword(Session session, + UserAdminService userAdminService, List nodesToUpdate, + String newValue) { + super(session, nodesToUpdate); + this.newValue = newValue; + this.userAdminService = userAdminService; + } + + protected void doUpdate(Node node) { + try { + String userId = node.getProperty(ArgeoNames.ARGEO_USER_ID) + .getString(); + if (userAdminService.userExists(userId)) { + JcrUserDetails userDetails = (JcrUserDetails) userAdminService + .loadUserByUsername(userId); + userAdminService.updateUser(userDetails + .cloneWithNewPassword(newValue)); + } + } catch (RepositoryException re) { + throw new ArgeoException( + "Unable to update boolean value for node " + node, re); + } + } + } + + @SuppressWarnings("unused") + private class AddToGroup extends UpdateJob { + private String groupID; + private Session session; + + public AddToGroup(Session session, List nodesToUpdate, + String groupID) { + super(session, nodesToUpdate); + this.session = session; + this.groupID = groupID; + } + + protected void doUpdate(Node node) { + log.info("Add/Remove to group actions are not yet implemented"); + // TODO implement this + // try { + // throw new ArgeoException("Not yet implemented"); + // } catch (RepositoryException re) { + // throw new ArgeoException( + // "Unable to update boolean value for node " + node, re); + // } + } + } + + /** + * Base privileged job that will be run asynchronously to perform the batch + * update + */ + private abstract class UpdateJob extends PrivilegedJob { + + private final Session currSession; + private final List nodesToUpdate; + + protected abstract void doUpdate(Node node); + + public UpdateJob(Session session, List nodesToUpdate) { + super("Perform update"); + try { + this.currSession = session.getRepository().login(); + // "move" nodes to update in the new session + // the "old" session will be closed by the calling command + // before the job has effectively ran + // TODO there must be a cleaner way to do. + List nodes = new ArrayList(); + for (Node node : nodesToUpdate) { + nodes.add(currSession.getNode(node.getPath())); + } + this.nodesToUpdate = nodes; + } catch (RepositoryException e) { + throw new ArgeoException("Error while dupplicating " + + "session for job", e); + } + } + + @Override + protected IStatus doRun(IProgressMonitor progressMonitor) { + try { + ArgeoMonitor monitor = new EclipseArgeoMonitor(progressMonitor); + VersionManager vm = currSession.getWorkspace() + .getVersionManager(); + int total = nodesToUpdate.size(); + monitor.beginTask("Performing change", total); + for (Node node : nodesToUpdate) { + String path = node.getPath(); + vm.checkout(path); + doUpdate(node); + currSession.save(); + vm.checkin(path); + monitor.worked(1); + } + } catch (Exception e) { + log.error("Cannot perform batch update on users", e); + // e.printStackTrace(); + + // Dig exception to find the root cause that will enable the + // user to understand the problem + Throwable cause = e; + Throwable originalCause = e; + while (cause != null) { + if (log.isTraceEnabled()) + log.trace("Parent Cause message : " + + cause.getMessage()); + originalCause = cause; + cause = cause.getCause(); + } + return new Status(IStatus.ERROR, SecurityAdminPlugin.PLUGIN_ID, + "Cannot perform updates.", originalCause); + } finally { + JcrUtils.logoutQuietly(currSession); + } + return Status.OK_STATUS; + } + } + + // ////////////////////// + // Pages definition + /** Displays a combo box that enables user to choose which action to perform */ + private class ChooseCommandWizardPage extends WizardPage { + private Combo chooseCommandCmb; + private Button trueChk; + private Text valueTxt; + private Text pwdTxt; + private Text pwd2Txt; + + public ChooseCommandWizardPage() { + super("Choose a command to run."); + setTitle("Choose a command to run."); + } + + @Override + public void createControl(Composite parent) { + GridLayout gl = new GridLayout(); + Composite container = new Composite(parent, SWT.NO_FOCUS); + container.setLayout(gl); + + chooseCommandCmb = new Combo(container, SWT.NO_FOCUS); + String[] values = commands.keySet().toArray( + new String[commands.size()]); + chooseCommandCmb.setItems(values); + chooseCommandCmb.setLayoutData(new GridData(SWT.FILL, SWT.TOP, + true, false)); + + final Composite bottomPart = new Composite(container, SWT.NO_FOCUS); + gl = new GridLayout(); + gl.horizontalSpacing = gl.marginWidth = gl.verticalSpacing = 0; + bottomPart.setLayout(gl); + bottomPart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, + true)); + + chooseCommandCmb.addSelectionListener(new SelectionListener() { + @Override + public void widgetSelected(SelectionEvent e) { + if (getCommand().equals(CMD_UPDATE_PASSWORD)) + populatePasswordCmp(bottomPart); + else if (getCommand().equals(CMD_GROUP_MEMBERSHIP)) + populateGroupCmp(bottomPart); + else + populateBooleanFlagCmp(bottomPart); + bottomPart.pack(true); + bottomPart.layout(); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + }); + + setControl(container); + } + + private void cleanParent(Composite parent) { + if (parent.getChildren().length > 0) { + for (Control control : parent.getChildren()) + control.dispose(); + } + } + + private void populateBooleanFlagCmp(Composite parent) { + cleanParent(parent); + trueChk = new Button(parent, SWT.CHECK); + trueChk.setText("Do it. (It will to the contrary if unchecked)"); + trueChk.setSelection(true); + trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); + } + + private void populatePasswordCmp(Composite parent) { + cleanParent(parent); + Composite body = new Composite(parent, SWT.NO_FOCUS); + body.setLayout(new GridLayout(2, false)); + body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + pwdTxt = createLP(body, "New password", ""); + pwd2Txt = createLP(body, "Repeat password", ""); + } + + /** Creates label and password. */ + protected Text createLP(Composite body, String label, String value) { + Label lbl = new Label(body, SWT.NONE); + lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); + lbl.setText(label); + Text text = new Text(body, SWT.BORDER | SWT.PASSWORD); + text.setText(value); + text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + return text; + } + + private void populateGroupCmp(Composite parent) { + if (parent.getChildren().length > 0) { + for (Control control : parent.getChildren()) + control.dispose(); + } + trueChk = new Button(parent, SWT.CHECK); + trueChk.setText("Add to group. (It will remove user(s) from the " + + "corresponding group if unchecked)"); + trueChk.setSelection(true); + trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); + } + + protected String getCommand() { + return commands.get(chooseCommandCmb.getItem(chooseCommandCmb + .getSelectionIndex())); + } + + protected String getCommandLbl() { + return chooseCommandCmb.getItem(chooseCommandCmb + .getSelectionIndex()); + } + + protected boolean getBoleanValue() { + // FIXME this is not consistent and will lead to errors. + if (ArgeoNames.ARGEO_ENABLED.equals(getCommand())) + return trueChk.getSelection(); + else + return !trueChk.getSelection(); + } + + @SuppressWarnings("unused") + protected String getStringValue() { + String value = null; + if (valueTxt != null) { + value = valueTxt.getText(); + if ("".equals(value.trim())) + value = null; + } + return value; + } + + protected String getPwdValue() { + String newPwd = null; + if (pwdTxt == null || pwd2Txt == null) + return null; + if (!pwdTxt.getText().equals("") || !pwd2Txt.getText().equals("")) { + if (pwdTxt.getText().equals(pwd2Txt.getText())) { + newPwd = pwdTxt.getText(); + pwdTxt.setText(""); + pwd2Txt.setText(""); + } else { + pwdTxt.setText(""); + pwd2Txt.setText(""); + throw new ArgeoException("Passwords are not equals"); + } + } + return newPwd; + } + } + + /** + * Displays a list of users with a check box to be able to choose some of + * them + */ + private class ChooseUsersWizardPage extends WizardPage implements + IPageChangedListener { + private UserTableComposite userTableCmp; + private Composite container; + private Session session; + + public ChooseUsersWizardPage(Session session) { + super("Choose Users"); + this.session = session; + setTitle("Select users who will be impacted"); + } + + @Override + public void createControl(Composite parent) { + container = new Composite(parent, SWT.NONE); + container.setLayout(new FillLayout()); + userTableCmp = new UserTableComposite(container, SWT.NO_FOCUS, + session); + userTableCmp.populate(true, true); + setControl(container); + + // Add listener to update message when shown + final IWizardContainer container = this.getContainer(); + if (container instanceof IPageChangeProvider) { + ((IPageChangeProvider) container).addPageChangedListener(this); + } + + } + + @Override + public void pageChanged(PageChangedEvent event) { + if (event.getSelectedPage() == this) { + String msg = "Chosen batch action: " + + chooseCommandPage.getCommandLbl(); + ((WizardPage) event.getSelectedPage()).setMessage(msg); + } + } + + protected List getSelectedUsers() { + return userTableCmp.getSelectedUsers(); + } + } + + /** + * Recapitulation of input data before running real update + */ + private class ValidateAndLaunchWizardPage extends WizardPage implements + IPageChangedListener { + private UserTableComposite userTableCmp; + private Session session; + + public ValidateAndLaunchWizardPage(Session session) { + super("Validate and launch"); + this.session = session; + setTitle("Validate and launch"); + } + + @Override + public void createControl(Composite parent) { + Composite mainCmp = new Composite(parent, SWT.NO_FOCUS); + mainCmp.setLayout(new FillLayout()); + + // Add listener to update user list when shown + final IWizardContainer container = this.getContainer(); + if (container instanceof IPageChangeProvider) { + ((IPageChangeProvider) container).addPageChangedListener(this); + } + + userTableCmp = new UserTableComposite(mainCmp, SWT.NO_FOCUS, + session); + userTableCmp.populate(false, false); + setControl(mainCmp); + } + + @Override + public void pageChanged(PageChangedEvent event) { + if (event.getSelectedPage() == this) { + @SuppressWarnings({ "unchecked", "rawtypes" }) + Object[] values = ((ArrayList) userListPage.getSelectedUsers()) + .toArray(new Object[userListPage.getSelectedUsers() + .size()]); + userTableCmp.getTableViewer().setInput(values); + String msg = "Following batch action: [" + + chooseCommandPage.getCommandLbl() + + "] will be perfomed on the users listed below.\n" + + "Are you sure you want to proceed?"; + ((WizardPage) event.getSelectedPage()).setMessage(msg); + } + } + + // private class MyUserTableCmp extends UserTableComposite { + // public MyUserTableCmp(Composite parent, int style, Session session) { + // super(parent, style, session); + // } + // + // @Override + // protected void refreshFilteredList() { + // @SuppressWarnings({ "unchecked", "rawtypes" }) + // + // setFilteredList(values); + // } + // + // @Override + // public void setVisible(boolean visible) { + // super.setVisible(visible); + // if (visible) + // refreshFilteredList(); + // } + // } + } +} \ No newline at end of file diff --git a/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/PrivilegedJob.java b/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/PrivilegedJob.java new file mode 100644 index 000000000..1ded50fdb --- /dev/null +++ b/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/PrivilegedJob.java @@ -0,0 +1,46 @@ +package org.argeo.security.ui; + +import java.security.AccessController; +import java.security.PrivilegedAction; + +import javax.security.auth.Subject; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.jobs.Job; +import org.springframework.security.Authentication; +import org.springframework.security.context.SecurityContextHolder; + +/** + * Propagate authentication to an eclipse job. Typically to execute a privileged + * action outside the UI thread + */ +public abstract class PrivilegedJob extends Job { + + private final Authentication authentication; + private Subject subject; + + public PrivilegedJob(String jobName) { + super(jobName); + authentication = SecurityContextHolder.getContext().getAuthentication(); + subject = Subject.getSubject(AccessController.getContext()); + } + + @Override + protected IStatus run(final IProgressMonitor progressMonitor) { + PrivilegedAction privilegedAction = new PrivilegedAction() { + public IStatus run() { + SecurityContextHolder.getContext().setAuthentication( + authentication); + return doRun(progressMonitor); + } + }; + return Subject.doAs(subject, privilegedAction); + } + + /** + * Implement here what should be executed with default context + * authentication + */ + protected abstract IStatus doRun(IProgressMonitor progressMonitor); +} \ No newline at end of file