Enhance dashboard, provide a generic quick search view
authorbsinou <bsinou@argeo.org>
Tue, 11 Apr 2017 16:07:17 +0000 (18:07 +0200)
committerbsinou <bsinou@argeo.org>
Tue, 11 Apr 2017 16:07:17 +0000 (18:07 +0200)
org.argeo.suite.core/src/org/argeo/suite/core/DefaultSuiteMaintenanceService.java
org.argeo.suite.workbench.rap/META-INF/spring/parts.xml
org.argeo.suite.workbench.rap/plugin.xml
org.argeo.suite.workbench.rap/src/org/argeo/suite/workbench/DashboardPerspective.java
org.argeo.suite.workbench.rap/src/org/argeo/suite/workbench/internal/EntitySingleColumnLabelProvider.java [new file with mode: 0644]
org.argeo.suite.workbench.rap/src/org/argeo/suite/workbench/parts/QuickSearchView.java [new file with mode: 0644]
org.argeo.suite.workbench.rap/theme/argeo-classic/icons/search.png [new file with mode: 0644]

index 2ccb6cdf884783357dc369945c0513f5dd961481..1c9feb210cdbb663df17301c6e3264211c3cb112 100644 (file)
@@ -40,9 +40,8 @@ public class DefaultSuiteMaintenanceService implements SystemMaintenanceService
                }
        }
 
                }
        }
 
-       // TODO Hard-coded model initialisation
        // To be cleaned once first init and config mechanisms have been implemented
        // To be cleaned once first init and config mechanisms have been implemented
-       private final static String publicPath = "/public";
+       // private final static String publicPath = "/public";
        // FIXME Users must have read access on the jcr:system/jcr:versionStorage
        // node under JackRabbit to be able to manage versions
        private final static String jackRabbitVersionSystemPath = "/jcr:system";
        // FIXME Users must have read access on the jcr:system/jcr:versionStorage
        // node under JackRabbit to be able to manage versions
        private final static String jackRabbitVersionSystemPath = "/jcr:system";
@@ -51,7 +50,7 @@ public class DefaultSuiteMaintenanceService implements SystemMaintenanceService
        public boolean prepareJcrTree(Session session) {
                boolean hasCHanged = false;
                try {
        public boolean prepareJcrTree(Session session) {
                boolean hasCHanged = false;
                try {
-                       JcrUtils.mkdirs(session, publicPath, NodeType.NT_UNSTRUCTURED);
+                       // JcrUtils.mkdirs(session, publicPath, NodeType.NT_UNSTRUCTURED);
                        if (session.hasPendingChanges()) {
                                session.save();
                                hasCHanged = true;
                        if (session.hasPendingChanges()) {
                                session.save();
                                hasCHanged = true;
@@ -76,9 +75,9 @@ public class DefaultSuiteMaintenanceService implements SystemMaintenanceService
                                        Privilege.JCR_READ);
                        // Default configuration of the workspace
                        JcrUtils.addPrivilege(session, "/", NodeConstants.ROLE_ADMIN, Privilege.JCR_ALL);
                                        Privilege.JCR_READ);
                        // Default configuration of the workspace
                        JcrUtils.addPrivilege(session, "/", NodeConstants.ROLE_ADMIN, Privilege.JCR_ALL);
-                       JcrUtils.addPrivilege(session, publicPath, NodeConstants.ROLE_USER, Privilege.JCR_READ);
-                       JcrUtils.addPrivilege(session, publicPath, "anonymous", Privilege.JCR_READ);
-                       JcrUtils.addPrivilege(session, publicPath, NodeConstants.ROLE_ANONYMOUS, Privilege.JCR_READ);
+                       // JcrUtils.addPrivilege(session, publicPath, NodeConstants.ROLE_USER, Privilege.JCR_READ);
+                       // JcrUtils.addPrivilege(session, publicPath, "anonymous", Privilege.JCR_READ);
+                       // JcrUtils.addPrivilege(session, publicPath, NodeConstants.ROLE_ANONYMOUS, Privilege.JCR_READ);
 
                        session.save();
                } catch (RepositoryException e) {
 
                        session.save();
                } catch (RepositoryException e) {
index 9bf6e6aec08fa505817688d4b1953cfa7cd866c3..d67695a7048af002352c12d6b9341578572444c4 100644 (file)
        </bean>
 
        <!-- VIEWS -->
        </bean>
 
        <!-- VIEWS -->
+       <bean id="quickSearchView" class="org.argeo.suite.workbench.parts.QuickSearchView"
+               scope="prototype">
+               <property name="repository" ref="repository" />
+               <property name="resourcesService" ref="resourcesService" />
+               <property name="activitiesService" ref="activitiesService" />
+               <property name="peopleService" ref="peopleService" />
+               <property name="systemWorkbenchService" ref="systemWorkbenchService" />
+       </bean>
+
 
        <!-- EDITORS -->
        <bean id="defaultDashboardEditor" class="org.argeo.suite.workbench.parts.DefaultDashboardEditor"
 
        <!-- EDITORS -->
        <bean id="defaultDashboardEditor" class="org.argeo.suite.workbench.parts.DefaultDashboardEditor"
index d41dc87345504b30ddf651e5f866fca1068897f4..7d53ad59a88050816c8c57299a65a46410efae01 100644 (file)
        <!-- VIEWS -->
         <extension
                point="org.eclipse.ui.views">
        <!-- VIEWS -->
         <extension
                point="org.eclipse.ui.views">
+               <view
+                       class="org.argeo.eclipse.spring.SpringExtensionFactory"
+                       icon="theme/argeo-classic/icons/search.png"
+                       id="org.argeo.suite.workbench.rap.quickSearchView"
+                       name="Search"
+                       restorable="true">
+               </view>
+               
        </extension>
                
        <!-- EDITORS --> 
        </extension>
                
        <!-- EDITORS --> 
                alias="/ui/suite/js/Chart.min.js"  
                base-name="js/Chart.min.js">  
          </resource>  
                alias="/ui/suite/js/Chart.min.js"  
                base-name="js/Chart.min.js">  
          </resource>  
-         <!--<resource  
+         <resource  
                alias="/ui/suite/js/leaflet.js"  
                base-name="js/leaflet.js">  
          </resource>  
          <resource  
                alias="/ui/suite/js/leaflet.css"  
                base-name="js/leaflet.css">  
                alias="/ui/suite/js/leaflet.js"  
                base-name="js/leaflet.js">  
          </resource>  
          <resource  
                alias="/ui/suite/js/leaflet.css"  
                base-name="js/leaflet.css">  
-         </resource>   -->
+         </resource>
     </extension>  
 </plugin>
     </extension>  
 </plugin>
index e75bccb3320d38f5d7a492942bfc719bf8e06755..765de22e62b3b95981ee4f7ea80234b89d6cd231 100644 (file)
@@ -1,7 +1,7 @@
 package org.argeo.suite.workbench;
 
 import org.argeo.documents.workbench.parts.MyFilesView;
 package org.argeo.suite.workbench;
 
 import org.argeo.documents.workbench.parts.MyFilesView;
-import org.argeo.people.workbench.rap.parts.QuickSearchView;
+import org.argeo.suite.workbench.parts.QuickSearchView;
 import org.eclipse.ui.IFolderLayout;
 import org.eclipse.ui.IPageLayout;
 import org.eclipse.ui.IPerspectiveFactory;
 import org.eclipse.ui.IFolderLayout;
 import org.eclipse.ui.IPageLayout;
 import org.eclipse.ui.IPerspectiveFactory;
diff --git a/org.argeo.suite.workbench.rap/src/org/argeo/suite/workbench/internal/EntitySingleColumnLabelProvider.java b/org.argeo.suite.workbench.rap/src/org/argeo/suite/workbench/internal/EntitySingleColumnLabelProvider.java
new file mode 100644 (file)
index 0000000..48341c7
--- /dev/null
@@ -0,0 +1,84 @@
+package org.argeo.suite.workbench.internal;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.activities.ActivitiesService;
+import org.argeo.activities.ActivitiesTypes;
+import org.argeo.activities.ui.ActivityListLabelProvider;
+import org.argeo.connect.resources.ResourcesService;
+import org.argeo.connect.ui.ConnectUiConstants;
+import org.argeo.connect.ui.ConnectUiUtils;
+import org.argeo.connect.ui.util.TagLabelProvider;
+import org.argeo.connect.workbench.SystemWorkbenchService;
+import org.argeo.people.PeopleException;
+import org.argeo.people.PeopleNames;
+import org.argeo.people.PeopleService;
+import org.argeo.people.PeopleTypes;
+import org.argeo.people.workbench.rap.providers.GroupLabelProvider;
+import org.argeo.people.workbench.rap.providers.OrgListLabelProvider;
+import org.argeo.people.workbench.rap.providers.PersonListLabelProvider;
+import org.argeo.tracker.TrackerTypes;
+import org.argeo.tracker.ui.TrackerSingleColLP;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * Provide a single column label provider for entity lists. Icon and displayed
+ * text vary with the element node type
+ */
+public class EntitySingleColumnLabelProvider extends LabelProvider implements PeopleNames {
+       private static final long serialVersionUID = 3111885324210673320L;
+
+       private SystemWorkbenchService systemWorkbenchService;
+
+       private ActivityListLabelProvider activityLP;
+       private TrackerSingleColLP trackerLP;
+       private OrgListLabelProvider orgLp;
+       private PersonListLabelProvider personLp;
+       private GroupLabelProvider groupLp = new GroupLabelProvider(ConnectUiConstants.LIST_TYPE_SMALL);
+       private TagLabelProvider mlInstanceLp;
+
+       public EntitySingleColumnLabelProvider(ResourcesService resourceService, ActivitiesService activitiesService,
+                       PeopleService peopleService, SystemWorkbenchService systemWorkbenchService) {
+               this.systemWorkbenchService = systemWorkbenchService;
+               activityLP = new ActivityListLabelProvider(activitiesService);
+               trackerLP = new TrackerSingleColLP(activitiesService);
+               personLp = new PersonListLabelProvider(peopleService);
+               orgLp = new OrgListLabelProvider(resourceService, peopleService);
+               mlInstanceLp = new TagLabelProvider(resourceService, ConnectUiConstants.LIST_TYPE_SMALL);
+       }
+
+       @Override
+       public String getText(Object element) {
+               try {
+                       Node entity = (Node) element;
+                       String result;
+
+                       if (entity.isNodeType(TrackerTypes.TRACKER_TASK) || entity.isNodeType(TrackerTypes.TRACKER_PROJECT)
+                                       || entity.isNodeType(TrackerTypes.TRACKER_MILESTONE))
+                               result = trackerLP.getText(element);
+                       else if (entity.isNodeType(ActivitiesTypes.ACTIVITIES_ACTIVITY))
+                               result = activityLP.getText(element);
+                       else if (entity.isNodeType(PeopleTypes.PEOPLE_PERSON))
+                               result = personLp.getText(element);
+                       else if (entity.isNodeType(PeopleTypes.PEOPLE_ORG))
+                               result = orgLp.getText(element);
+                       else if (entity.isNodeType(PeopleTypes.PEOPLE_MAILING_LIST))
+                               result = mlInstanceLp.getText(element);
+                       else if (entity.isNodeType(PeopleTypes.PEOPLE_GROUP))
+                               result = groupLp.getText(element);
+                       else
+                               result = "";
+                       return ConnectUiUtils.replaceAmpersand(result);
+               } catch (RepositoryException re) {
+                       throw new PeopleException("Unable to get formatted value for node", re);
+               }
+       }
+
+       /** Overwrite this method to provide project specific images */
+       @Override
+       public Image getImage(Object element) {
+               return systemWorkbenchService.getIconForType((Node) element);
+       }
+}
diff --git a/org.argeo.suite.workbench.rap/src/org/argeo/suite/workbench/parts/QuickSearchView.java b/org.argeo.suite.workbench.rap/src/org/argeo/suite/workbench/parts/QuickSearchView.java
new file mode 100644 (file)
index 0000000..c9e9bf1
--- /dev/null
@@ -0,0 +1,229 @@
+package org.argeo.suite.workbench.parts;
+
+import static org.argeo.eclipse.ui.EclipseUiUtils.notEmpty;
+
+import javax.jcr.NodeIterator;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryResult;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.activities.ActivitiesService;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.connect.ConnectTypes;
+import org.argeo.connect.resources.ResourcesService;
+import org.argeo.connect.ui.ConnectUiConstants;
+import org.argeo.connect.ui.util.BasicNodeListContentProvider;
+import org.argeo.connect.ui.widgets.DelayedText;
+import org.argeo.connect.util.ConnectJcrUtils;
+import org.argeo.connect.util.XPathUtils;
+import org.argeo.connect.workbench.Refreshable;
+import org.argeo.connect.workbench.SystemWorkbenchService;
+import org.argeo.connect.workbench.util.JcrViewerDClickListener;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.people.PeopleService;
+import org.argeo.suite.SuiteException;
+import org.argeo.suite.workbench.AsUiPlugin;
+import org.argeo.suite.workbench.internal.EntitySingleColumnLabelProvider;
+import org.eclipse.jface.layout.TableColumnLayout;
+import org.eclipse.jface.viewers.ColumnWeightData;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.rap.rwt.service.ServerPushSession;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+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.TableColumn;
+import org.eclipse.ui.part.ViewPart;
+
+/** A table with a quick search field. */
+public class QuickSearchView extends ViewPart implements Refreshable {
+       private final static Log log = LogFactory.getLog(QuickSearchView.class);
+       public static final String ID = AsUiPlugin.PLUGIN_ID + ".quickSearchView";
+
+       /* DEPENDENCY INJECTION */
+       private Repository repository;
+       private Session session;
+       private ResourcesService resourcesService;
+       private ActivitiesService activitiesService;
+       private PeopleService peopleService;
+       private SystemWorkbenchService systemWorkbenchService;
+
+       // This page widgets
+       private TableViewer entityViewer;
+       private DelayedText filterTxt;
+
+       @Override
+       public void createPartControl(Composite parent) {
+               session = ConnectJcrUtils.login(repository);
+               // MainLayout
+               parent.setLayout(new GridLayout());
+               addFilterPanel(parent);
+               entityViewer = createListPart(parent, new EntitySingleColumnLabelProvider(resourcesService, activitiesService,
+                               peopleService, systemWorkbenchService));
+               refreshFilteredList();
+       }
+
+       public void addFilterPanel(Composite parent) {
+               // Use a delayed text: the query won't be done until the user stop
+               // typing for 800ms
+               int style = SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL;
+               filterTxt = new DelayedText(parent, style, ConnectUiConstants.SEARCH_TEXT_DELAY);
+               filterTxt.setLayoutData(EclipseUiUtils.fillWidth());
+
+               final ServerPushSession pushSession = new ServerPushSession();
+               filterTxt.addDelayedModifyListener(pushSession, new ModifyListener() {
+                       private static final long serialVersionUID = 5003010530960334977L;
+
+                       public void modifyText(ModifyEvent event) {
+                               filterTxt.getDisplay().asyncExec(new Runnable() {
+                                       @Override
+                                       public void run() {
+                                               refreshFilteredList();
+                                       }
+                               });
+                               pushSession.stop();
+                       }
+               });
+
+               // Jump to the first item of the list using the down arrow
+               filterTxt.addKeyListener(new KeyListener() {
+                       private static final long serialVersionUID = -4523394262771183968L;
+
+                       @Override
+                       public void keyReleased(KeyEvent e) {
+                       }
+
+                       @Override
+                       public void keyPressed(KeyEvent e) {
+                               // boolean shiftPressed = (e.stateMask & SWT.SHIFT) != 0;
+                               // boolean altPressed = (e.stateMask & SWT.ALT) != 0;
+                               if (e.keyCode == SWT.ARROW_DOWN || e.keyCode == SWT.TAB) {
+                                       Object first = entityViewer.getElementAt(0);
+                                       if (first != null) {
+                                               entityViewer.getTable().setFocus();
+                                               entityViewer.setSelection(new StructuredSelection(first), true);
+                                       }
+                                       e.doit = false;
+                               }
+                       }
+               });
+       }
+
+       protected TableViewer createListPart(Composite parent, ILabelProvider labelProvider) {
+               parent.setLayout(new GridLayout());
+
+               Composite tableComposite = new Composite(parent, SWT.NONE);
+               GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_VERTICAL
+                               | GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL);
+               tableComposite.setLayoutData(gd);
+
+               TableViewer v = new TableViewer(tableComposite);
+               v.setLabelProvider(labelProvider);
+
+               TableColumn singleColumn = new TableColumn(v.getTable(), SWT.V_SCROLL);
+               TableColumnLayout tableColumnLayout = new TableColumnLayout();
+               tableColumnLayout.setColumnData(singleColumn, new ColumnWeightData(85));
+               tableComposite.setLayout(tableColumnLayout);
+
+               // Corresponding table & style
+               Table table = v.getTable();
+               table.setLinesVisible(true);
+               table.setHeaderVisible(false);
+               CmsUtils.markup(table);
+               CmsUtils.setItemHeight(table, 26);
+
+               v.setContentProvider(new BasicNodeListContentProvider());
+               v.addDoubleClickListener(new JcrViewerDClickListener());
+               return v;
+       }
+
+       @Override
+       public void dispose() {
+               JcrUtils.logoutQuietly(session);
+               super.dispose();
+       }
+
+       @Override
+       public void setFocus() {
+               refreshFilteredList();
+               filterTxt.setFocus();
+       }
+
+       @Override
+       public void forceRefresh(Object object) {
+               refreshFilteredList();
+       }
+
+       protected void refreshFilteredList() {
+               try {
+                       String filter = filterTxt.getText();
+                       // Prevents the query on the full repository
+                       // if (isEmpty(filter)) {
+                       // entityViewer.setInput(null);
+                       // return;
+                       // }
+
+                       // XPATH Query
+                       String xpathQueryStr = "//element(*, " + ConnectTypes.CONNECT_ENTITY + ")";
+                       String xpathFilter = XPathUtils.getFreeTextConstraint(filter);
+                       if (notEmpty(xpathFilter))
+                               xpathQueryStr += "[" + xpathFilter + "]";
+
+                       // boolean doOrder = orderResultsBtn != null
+                       // && !(orderResultsBtn.isDisposed())
+                       // && orderResultsBtn.getSelection();
+                       // if (doOrder) {
+                       // xpathQueryStr += " order by jcr:title";
+                       // }
+
+                       long begin = System.currentTimeMillis();
+                       Query xpathQuery = XPathUtils.createQuery(session, xpathQueryStr);
+
+                       xpathQuery.setLimit(ConnectUiConstants.SEARCH_DEFAULT_LIMIT);
+                       QueryResult result = xpathQuery.execute();
+
+                       NodeIterator nit = result.getNodes();
+                       entityViewer.setInput(JcrUtils.nodeIteratorToList(nit));
+                       if (log.isDebugEnabled()) {
+                               long end = System.currentTimeMillis();
+                               log.debug("Quick Search - Found: " + nit.getSize() + " in " + (end - begin)
+                                               + " ms by executing XPath query (" + xpathQueryStr + ").");
+                       }
+               } catch (RepositoryException e) {
+                       throw new SuiteException("Unable to list entities", e);
+               }
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
+       public void setResourcesService(ResourcesService resourcesService) {
+               this.resourcesService = resourcesService;
+       }
+       
+       public void setActivitiesService(ActivitiesService activitiesService) {
+               this.activitiesService = activitiesService;
+       }
+       
+       public void setPeopleService(PeopleService peopleService) {
+               this.peopleService = peopleService;
+       }
+
+       public void setSystemWorkbenchService(SystemWorkbenchService systemWorkbenchService) {
+               this.systemWorkbenchService = systemWorkbenchService;
+       }
+}
diff --git a/org.argeo.suite.workbench.rap/theme/argeo-classic/icons/search.png b/org.argeo.suite.workbench.rap/theme/argeo-classic/icons/search.png
new file mode 100644 (file)
index 0000000..6588de8
Binary files /dev/null and b/org.argeo.suite.workbench.rap/theme/argeo-classic/icons/search.png differ