X-Git-Url: https://git.argeo.org/?p=gpl%2Fargeo-suite.git;a=blobdiff_plain;f=org.argeo.suite.workbench.rap%2Fsrc%2Forg%2Fargeo%2Fsuite%2Fworkbench%2Fparts%2FDefaultDashboardEditor.java;h=d879d7e54c2017af63c0aaba321921b409dc0938;hp=cfbe2eef88acd9dff03934bce5cceca4baa32120;hb=9d3b4e769feed8314ef92f894a580016fa09813d;hpb=f2bf1e12e3aad9c2507e86e1d33fef2fd3e67489 diff --git a/org.argeo.suite.workbench.rap/src/org/argeo/suite/workbench/parts/DefaultDashboardEditor.java b/org.argeo.suite.workbench.rap/src/org/argeo/suite/workbench/parts/DefaultDashboardEditor.java index cfbe2ee..d879d7e 100644 --- a/org.argeo.suite.workbench.rap/src/org/argeo/suite/workbench/parts/DefaultDashboardEditor.java +++ b/org.argeo.suite.workbench.rap/src/org/argeo/suite/workbench/parts/DefaultDashboardEditor.java @@ -1,102 +1,333 @@ package org.argeo.suite.workbench.parts; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.List; + import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.RepositoryException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.argeo.connect.people.PeopleConstants; -import org.argeo.connect.people.PeopleTypes; -import org.argeo.connect.people.workbench.rap.PeopleRapUtils; +import org.argeo.activities.ActivitiesNames; +import org.argeo.activities.ActivitiesService; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.ui.workbench.util.CommandUtils; +import org.argeo.cms.util.CmsUtils; +import org.argeo.connect.ConnectNames; import org.argeo.connect.util.ConnectJcrUtils; +import org.argeo.connect.workbench.Refreshable; +import org.argeo.connect.workbench.commands.OpenEntityEditor; import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.jcr.JcrUtils; +import org.argeo.node.NodeUtils; +import org.argeo.suite.SuiteException; import org.argeo.suite.workbench.AsUiPlugin; +import org.argeo.tracker.TrackerNames; +import org.argeo.tracker.TrackerService; +import org.argeo.tracker.core.TrackerUtils; +import org.argeo.tracker.ui.TaskListLabelProvider; +import org.argeo.tracker.ui.TaskVirtualListComposite; +import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Color; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowLayout; import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Link; /** Argeo Suite Default Dashboard */ -public class DefaultDashboardEditor extends AbstractSuiteDashboard { +public class DefaultDashboardEditor extends AbstractSuiteDashboard implements Refreshable { final static Log log = LogFactory.getLog(DefaultDashboardEditor.class); public final static String ID = AsUiPlugin.PLUGIN_ID + ".defaultDashboardEditor"; - // Default gadget dimensions - private int wh = 300; - private int hh = 350; + private ActivitiesService activitiesService; + private TrackerService trackerService; + + private String datePattern = "dd MMM yyyy"; + + private Composite headerCmp; + private Composite taskListCmp; + private TaskVirtualListComposite tvlc; @Override public void createPartControl(Composite parent) { super.createPartControl(parent); + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + Composite bodyCmp = new Composite(parent, SWT.NO_FOCUS); + bodyCmp.setLayoutData(EclipseUiUtils.fillAll()); + bodyCmp.setLayout(new GridLayout()); - parent.setLayout(new GridLayout()); - // Main Layout - Composite body = getFormToolkit().createComposite(parent); - body.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); + // Header + try { + headerCmp = createHeaderPart(bodyCmp, NodeUtils.getUserHome(getSession())); + headerCmp.setLayoutData(EclipseUiUtils.fillWidth()); - GridLayout bodyLayout = new GridLayout(2, true); - bodyLayout.horizontalSpacing = 20; - bodyLayout.verticalSpacing = 20; - body.setLayout(bodyLayout); + } catch (RepositoryException e) { + throw new SuiteException("Cannot create dashboard overview", e); + } - // Project List - Composite projectsGadget = createGadgetCmp(body, wh, hh); - populateProjectsGadget(projectsGadget); + taskListCmp = new Composite(bodyCmp, SWT.NO_FOCUS); + taskListCmp.setLayoutData(EclipseUiUtils.fillAll()); - // Contacts - Composite contactGadget = createGadgetCmp(body, wh, hh); - populateContactsGadget(contactGadget); + populateTaskListCmp(); + } + + private void populateTaskListCmp() { + CmsUtils.clear(taskListCmp); + taskListCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); + // Composite innerCmp = new Composite(taskListCmp, SWT.NO_FOCUS); + // innerCmp.setLayoutData(EclipseUiUtils.fillAll()); + TaskListLabelProvider labelProvider = new TaskListLabelProvider(trackerService); + tvlc = new TaskVirtualListComposite(taskListCmp, SWT.NO_FOCUS, labelProvider, 54); + tvlc.setLayoutData(EclipseUiUtils.fillAll()); + forceRefresh(null); } - /** Links to the various projects */ - private void populateProjectsGadget(Composite parent) { - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - createGadgetTitleCmp(parent, "Projects"); - Composite bodyCmp = createGadgetBodyCmp(parent); - - // // TODO enhance this - // NodeIterator nit = AoUtils.listNodesOfType(getSession(), - // AoTypes.OFFICE_ACCOUNT, - // getAoService().getBasePath(AoTypes.OFFICE_ACCOUNT)); - // while (nit.hasNext()) { - // Node account = nit.nextNode(); - // PeopleRapUtils.createOpenEntityEditorLink(getAoWbService(), bodyCmp, - // ConnectJcrUtils.get(account, Property.JCR_TITLE), account); - // } - // - // PeopleWorkbenchService aoWbSrv = getAoWbService(); - // // Opens a lits of all projects - // - // PeopleRapUtils.createOpenSearchEditorLink(aoWbSrv, bodyCmp, "All - // projects", TrackerTypes.TRACKER_PROJECT, - // AoConstants.ACCOUNTS_BASE_PATH); + @Override + public void forceRefresh(Object object) { + NodeIterator nit = activitiesService.getMyTasks(getSession(), true); + tvlc.getTableViewer().setInput(JcrUtils.nodeIteratorToList(nit).toArray()); } - /** Links to the various contact search pages */ - private void populateContactsGadget(Composite parent) { - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - createGadgetTitleCmp(parent, "Contacts"); - Composite bodyCmp = createGadgetBodyCmp(parent); - - PeopleRapUtils.createOpenSearchEditorLink(getPeopleWorkbenchService(), bodyCmp, "Persons", - PeopleTypes.PEOPLE_PERSON, getPeopleService().getBasePath(PeopleTypes.PEOPLE_PERSON)); - - PeopleRapUtils.createOpenSearchEditorLink(getPeopleWorkbenchService(), bodyCmp, "Organisations", - PeopleTypes.PEOPLE_ORG, getPeopleService().getBasePath(PeopleTypes.PEOPLE_ORG)); - - Node tagParent = getPeopleService().getResourceService().getTagLikeResourceParent(getSession(), - PeopleTypes.PEOPLE_MAILING_LIST); - PeopleRapUtils.createOpenSearchEditorLink(getPeopleWorkbenchService(), bodyCmp, "Mailing lists", - PeopleTypes.PEOPLE_MAILING_LIST, ConnectJcrUtils.getPath(tagParent)); - - PeopleRapUtils.createOpenSearchEditorLink(getPeopleWorkbenchService(), bodyCmp, "Tasks", - PeopleTypes.PEOPLE_TASK, getPeopleService().getBasePath(null)); - - tagParent = getPeopleService().getResourceService().getTagLikeResourceParent(getSession(), - PeopleConstants.RESOURCE_TAG); - - PeopleRapUtils.createOpenSearchEditorLink(getPeopleWorkbenchService(), bodyCmp, "Tags", - PeopleTypes.PEOPLE_TAG_INSTANCE, ConnectJcrUtils.getPath(tagParent)); + private Composite createHeaderPart(Composite parent, Node context) throws RepositoryException { + Composite bodyCmp = new Composite(parent, SWT.NO_FOCUS); + bodyCmp.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, true))); + + Composite leftCmp = new Composite(bodyCmp, SWT.NO_FOCUS); + leftCmp.setLayout(new GridLayout()); + leftCmp.setLayoutData(EclipseUiUtils.fillWidth()); + Composite rightCmp = new Composite(bodyCmp, SWT.NO_FOCUS); + rightCmp.setLayout(new GridLayout()); + rightCmp.setLayoutData(EclipseUiUtils.fillWidth()); + + // Title + Label titleLbl = new Label(leftCmp, SWT.WRAP | SWT.LEAD); + CmsUtils.markup(titleLbl); + String titleStr = " Hello " + CurrentUser.getDisplayName() + " "; + titleLbl.setText(titleStr); + GridData gd = new GridData(SWT.TOP, SWT.BOTTOM, false, false); + gd.verticalIndent = 5; + gd.horizontalIndent = 10; + titleLbl.setLayoutData(gd); + + Calendar now = GregorianCalendar.getInstance(); + + NodeIterator nit = activitiesService.getMyTasks(getSession(), true); + if (nit.hasNext()) { + List overdueTasks = new ArrayList<>(); + while (nit.hasNext()) { + Node currNode = nit.nextNode(); + if (currNode.hasProperty(ActivitiesNames.ACTIVITIES_DUE_DATE) + && currNode.getProperty(ActivitiesNames.ACTIVITIES_DUE_DATE).getDate().before(now)) + overdueTasks.add(currNode); + } + if (!overdueTasks.isEmpty()) { + Composite overdueCmp = new Composite(leftCmp, SWT.NO_FOCUS); + long size = overdueTasks.size(); + String overdueStr = "You have " + size + " overdue task" + (size > 1 ? "s" : "") + ": "; + populateMuliValueClickableList(overdueCmp, overdueTasks.toArray(new Node[0]), new TaskLp(), overdueStr); + } + } + + nit = trackerService.getMyMilestones(getSession(), true); + List openMilestones = new ArrayList<>(); + + if (nit.hasNext()) { + List overdueMilestones = new ArrayList<>(); + while (nit.hasNext()) { + Node currNode = nit.nextNode(); + openMilestones.add(currNode); + if (currNode.hasProperty(TrackerNames.TRACKER_TARGET_DATE) + && currNode.getProperty(TrackerNames.TRACKER_TARGET_DATE).getDate().before(now)) + overdueMilestones.add(currNode); + } + if (!overdueMilestones.isEmpty()) { + Composite overdueCmp = new Composite(leftCmp, SWT.NO_FOCUS); + long size = overdueMilestones.size(); + String overdueStr = "You have " + size + " overdue milestone" + (size > 1 ? "s" : "") + ": "; + populateMuliValueClickableList(overdueCmp, overdueMilestones.toArray(new Node[0]), new MilestoneLp(), + overdueStr); + } + } + + // My projects + List openProjects = JcrUtils.nodeIteratorToList(trackerService.getMyProjects(getSession(), true)); + if (!openProjects.isEmpty()) { + Group myProjectsGp = new Group(rightCmp, SWT.NO_FOCUS); + myProjectsGp.setText("My open projects"); + myProjectsGp.setLayoutData(EclipseUiUtils.fillWidth()); + populateMuliValueClickableList(myProjectsGp, openProjects.toArray(new Node[0]), new ProjectLp(), null); + } + + // My Milestones + if (!openMilestones.isEmpty()) { + Group myMilestoneGp = new Group(rightCmp, SWT.NO_FOCUS); + myMilestoneGp.setText("My open milestones"); + myMilestoneGp.setLayoutData(EclipseUiUtils.fillWidth()); + populateMuliValueClickableList(myMilestoneGp, openMilestones.toArray(new Node[0]), new MilestoneLp(), null); + } + return bodyCmp; + } + + private class ProjectLp extends ColumnLabelProvider { + private static final long serialVersionUID = 7231233932794865555L; + + @Override + public String getText(Object element) { + Node project = (Node) element; + + String percent; + NodeIterator nit = TrackerUtils.getIssues(project, null, null, null, true); + long openNb = nit.getSize(); + + nit = TrackerUtils.getIssues(project, null, null, null, false); + long allNb = nit.getSize(); + + if (allNb < 1) + percent = "empty"; + else { + double result = (allNb - openNb) / allNb * 100; + percent = String.format("%.1f", result) + "% done"; + } + StringBuilder builder = new StringBuilder(); + builder.append("").append(ConnectJcrUtils.get(project, Property.JCR_TITLE)).append(""); + builder.append(" (").append(percent).append(")"); + + return builder.toString(); + } + } + private class MilestoneLp extends ColumnLabelProvider { + private static final long serialVersionUID = 7231233932794865555L; + + @Override + public String getText(Object element) { + Node milestone = (Node) element; + Node project = TrackerUtils.getRelatedProject(trackerService, milestone); + String dueDate = ConnectJcrUtils.getDateFormattedAsString(milestone, TrackerNames.TRACKER_TARGET_DATE, + datePattern); + + String percent; + String propName = TrackerNames.TRACKER_MILESTONE_UID; + String muid = ConnectJcrUtils.get(milestone, ConnectNames.CONNECT_UID); + NodeIterator nit = TrackerUtils.getIssues(project, null, propName, muid, true); + long openNb = nit.getSize(); + + nit = TrackerUtils.getIssues(project, null, propName, muid, false); + long allNb = nit.getSize(); + + if (allNb < 1) + percent = "empty"; + else { + double result = (allNb - openNb) / allNb * 100; + percent = String.format("%.1f", result) + "% done"; + } + StringBuilder builder = new StringBuilder(); + builder.append("").append(ConnectJcrUtils.get(milestone, Property.JCR_TITLE)).append(""); + builder.append(" ("); + if (EclipseUiUtils.notEmpty(dueDate)) + builder.append("due to ").append(dueDate).append(", "); + + builder.append(percent).append(")"); + return builder.toString(); + } + + @Override + public Color getForeground(Object element) { + Node milestone = (Node) element; + Calendar dueDate = ConnectJcrUtils.getDateValue(milestone, TrackerNames.TRACKER_TARGET_DATE); + if (dueDate != null && dueDate.before(Calendar.getInstance())) + return Display.getCurrent().getSystemColor(SWT.COLOR_RED); + return null; + } } + + private class TaskLp extends ColumnLabelProvider { + private static final long serialVersionUID = 7231233932794865555L; + + @Override + public String getText(Object element) { + Node task = (Node) element; + String dueDate = ConnectJcrUtils.getDateFormattedAsString(task, ActivitiesNames.ACTIVITIES_DUE_DATE, + datePattern); + + StringBuilder builder = new StringBuilder(); + builder.append("").append(ConnectJcrUtils.get(task, Property.JCR_TITLE)).append(""); + if (EclipseUiUtils.notEmpty(dueDate)) + builder.append(" (").append("due to ").append(dueDate).append(")"); + return builder.toString(); + } + + @Override + public Color getForeground(Object element) { + Node milestone = (Node) element; + Calendar dueDate = ConnectJcrUtils.getDateValue(milestone, TrackerNames.TRACKER_TARGET_DATE); + if (dueDate != null && dueDate.before(Calendar.getInstance())) + return Display.getCurrent().getSystemColor(SWT.COLOR_RED); + return null; + } + } + + @Override + public void setFocus() { + // refreshDocListGadget(); + } + + public void setActivitiesService(ActivitiesService activitiesService) { + this.activitiesService = activitiesService; + } + + public void setTrackerService(TrackerService trackerService) { + this.trackerService = trackerService; + } + + // LOCAL HELPERS + private void populateMuliValueClickableList(Composite parent, Node[] nodes, ColumnLabelProvider lp, + String listLabel) { + CmsUtils.clear(parent); + RowLayout rl = new RowLayout(SWT.HORIZONTAL); + rl.wrap = true; + rl.marginLeft = rl.marginTop = rl.marginBottom = 0; + rl.marginRight = 8; + parent.setLayout(rl); + + if (EclipseUiUtils.notEmpty(listLabel)) { + Link link = new Link(parent, SWT.NONE); + link.setText(listLabel); + link.setFont(EclipseUiUtils.getBoldFont(parent)); + } + + int i = 1; + for (Node node : nodes) { + Link link = new Link(parent, SWT.NONE); + CmsUtils.markup(link); + link.setText(lp.getText(node) + (i != nodes.length ? ", " : "")); + i++; +// Color fc = lp.getForeground(node); +// if (fc != null) +// link.setForeground(fc); + + link.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = 1L; + + @Override + public void widgetSelected(final SelectionEvent event) { + CommandUtils.callCommand(getSystemWorkbenchService().getOpenEntityEditorCmdId(), + OpenEntityEditor.PARAM_JCR_ID, ConnectJcrUtils.getIdentifier(node)); + } + }); + } + } + }