First draft of a wizard to launch batch updates on users.
authorBruno Sinou <bsinou@argeo.org>
Fri, 25 Oct 2013 12:37:06 +0000 (12:37 +0000)
committerBruno Sinou <bsinou@argeo.org>
Fri, 25 Oct 2013 12:37:06 +0000 (12:37 +0000)
https://www.argeo.org/bugzilla/show_bug.cgi?id=171

git-svn-id: https://svn.argeo.org/commons/branches/1.x@6574 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

12 files changed:
security/plugins/org.argeo.security.ui.admin/META-INF/spring/commands.xml
security/plugins/org.argeo.security.ui.admin/icons/batch.gif [new file with mode: 0644]
security/plugins/org.argeo.security.ui.admin/plugin.xml
security/plugins/org.argeo.security.ui.admin/pom.xml
security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/SecurityAdminImages.java [new file with mode: 0644]
security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/UserTableComposite.java [new file with mode: 0644]
security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/DeleteUser.java
security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/NewUser.java
security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/RefreshUsersList.java
security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/UserBatchUpdate.java [new file with mode: 0644]
security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/views/UsersView.java
security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/wizards/UserBatchUpdateWizard.java [new file with mode: 0644]

index 65a88be37119622359723023db6a70b21f5b62a2..e0a435e07a11dda1b65ca8640be9685dd9a97610 100644 (file)
                scope="prototype">
                <property name="userAdminService" ref="userAdminService" />
        </bean>
+       
+       <bean id="userBatchUpdate" class="org.argeo.security.ui.admin.commands.UserBatchUpdate"
+               scope="prototype">
+               <property name="repository" ref="nodeRepository" />
+               <property name="userAdminService" ref="userAdminService" />
+               <property name="jcrSecurityModel" ref="jcrSecurityModel" />
+       </bean>
+       
 
        <!-- ROLES -->
        <bean id="refreshRoles" class="org.argeo.security.ui.admin.commands.RefreshRoles"
diff --git a/security/plugins/org.argeo.security.ui.admin/icons/batch.gif b/security/plugins/org.argeo.security.ui.admin/icons/batch.gif
new file mode 100644 (file)
index 0000000..b8ca14a
Binary files /dev/null and b/security/plugins/org.argeo.security.ui.admin/icons/batch.gif differ
index a67d8b12d761ac41d2050eaa7225d315a19c3eb2..a3880e5d5582cea1940b0877422060fbeaa6a584 100644 (file)
             id="org.argeo.security.ui.admin.deleteRole"
             name="deleteRole">
       </command>
+      <command
+            defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+            id="org.argeo.security.ui.admin.userBatchUpdate"
+            name="New User">
+      </command>
     </extension>
      <extension
          point="org.eclipse.ui.menus">
         </menuContribution>
        <menuContribution
             locationURI="toolbar:org.argeo.security.ui.admin.adminUsersView">
+            <command
+                  commandId="org.argeo.security.ui.admin.refreshUsersList"
+                  icon="icons/refresh.png"
+                  label="Refresh list"
+                  tooltip="Force the full refresh of the user list">
+            </command>
             <command
                   commandId="org.argeo.security.ui.admin.deleteUser"
                   icon="icons/remove.gif"
                   label="Add User"
                   tooltip="Add new user">
             </command>
+            <command
+                  commandId="org.argeo.security.ui.admin.userBatchUpdate"
+                  icon="icons/batch.gif"
+                  label="Update users"
+                  tooltip="Perform maintenance activities on a list of chosen users">
+            </command>
             <command
                   commandId="org.argeo.security.ui.admin.refreshUsersList"
                   icon="icons/sync.gif"
index 3843c2762106b3bcbfa7745adb7a5b5d5356e993..eda738f5ad5caa98d4afc74f3e32a2d202f65118 100644 (file)
@@ -1,4 +1,6 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
                <groupId>org.argeo.commons.security</groupId>
        </build>
        <dependencies>
                <!-- Argeo Security -->
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.ui</artifactId>
+                       <version>1.2.3-SNAPSHOT</version>
+               </dependency>
+
                <dependency>
                        <groupId>org.argeo.commons.security</groupId>
                        <artifactId>org.argeo.security.core</artifactId>
                </dependency>
 
                <!-- Argeo Eclipse -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.ui.jcr</artifactId>
+                       <version>1.2.3-SNAPSHOT</version>
+               </dependency>
+
                <dependency>
                        <groupId>org.argeo.commons.base</groupId>
                        <artifactId>org.argeo.eclipse.ui</artifactId>
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 (file)
index 0000000..f7ffa9c
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>
+ *
+ * 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 (file)
index 0000000..5e96965
--- /dev/null
@@ -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<Node> selectedItems = new ArrayList<Node>();
+
+       /**
+        * Overwrite to display other columns
+        */
+       public List<ColumnDefinition> getColumnsDef() {
+               List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
+
+               // 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<Node> getSelectedUsers() {
+               if (hasSelectionColumn) {
+                       Object[] elements = ((CheckboxTableViewer) usersViewer)
+                                       .getCheckedElements();
+
+                       List<Node> result = new ArrayList<Node>();
+                       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<String> propertiesList = new ArrayList<String>();
+
+               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<ColumnDefinition> 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<Node> 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
index 0b180c3cc32f8d12b8ccdea86266fdeffa0dd5d1..ccf360fd045adcf9a38a026fa5484975bbb5ba50 100644 (file)
@@ -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;
        }
 
index 60146099a9a74dc54fa320743a830c5e479b2a48..660896de8a493cf1c0522bf037d569b8b6dfe85a 100644 (file)
@@ -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;
index 54c6f4f712684db2b0f7c2469b16a849c1475488..0904ed2fb0e36645f0c34d87035d2af5bf9945b4 100644 (file)
@@ -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 (file)
index 0000000..657dfc7
--- /dev/null
@@ -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;
+       }
+
+}
index 6c8986b2ba16af973ceeda1660b8b2f7d901a6bc..5f47741f5edb951cff6fd40797d48c1e6cbfcbf3 100644 (file)
  */
 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<String> propertiesList = new ArrayList<String>();
-
-               // 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<String> 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<String> 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<Node> userProfiles = new ArrayList<Node>();
-                       // 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<Node> 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 (file)
index 0000000..3d59d80
--- /dev/null
@@ -0,0 +1,568 @@
+/*
+ * 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<String, String> commands = new HashMap<String, String>() {
+               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<Node> 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<Node> 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<Node> 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<Node> nodesToUpdate;
+
+               protected abstract void doUpdate(Node node);
+
+               public UpdateJob(Session session, List<Node> 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<Node> nodes = new ArrayList<Node>();
+                               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);
+                               }
+                       } catch (Exception 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<Node> 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