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=d24ad972cdb6f560e78a73f4d9ec7074bc3e5f20;hp=cc45bb2557c4de4463cefd43b9c520cb6c14c426;hb=f29206b576a5fbc9715e89377ed8f7c04f623ef1;hpb=e7ec976e33b8893b93b953c69c816286d7a118c7 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 cc45bb2..d24ad97 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,105 +1,407 @@ 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.connect.ui.workbench.Refreshable; +import org.argeo.activities.ActivitiesNames; +import org.argeo.activities.ActivitiesService; +import org.argeo.activities.workbench.parts.TaskViewerContextMenu; +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.ConnectWorkbenchUtils; +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.workbench.AsUiPlugin; +import org.argeo.suite.workbench.SuiteWorkbenchException; +import org.argeo.tracker.TrackerNames; +import org.argeo.tracker.TrackerService; +import org.argeo.tracker.TrackerTypes; +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.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.TableViewer; import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Point; 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; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorSite; +import org.eclipse.ui.PartInitException; /** Argeo Suite Default Dashboard */ 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; - private Composite projectsGadget; + @Override + public void init(IEditorSite site, IEditorInput input) throws PartInitException { + super.init(site, input); + } @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)); - - GridLayout bodyLayout = new GridLayout(2, true); - bodyLayout.horizontalSpacing = 20; - bodyLayout.verticalSpacing = 20; - body.setLayout(bodyLayout); - - // Last updated doc List - projectsGadget = createGadgetCmp(body, wh, hh); - // refreshDocListGadget(projectsGadget); + headerCmp = new Composite(bodyCmp, SWT.NO_FOCUS); + headerCmp.setLayoutData(EclipseUiUtils.fillWidth()); - // Contacts - Composite contactGadget = createGadgetCmp(body, wh, hh); - populateContactsGadget(contactGadget); + taskListCmp = new Composite(bodyCmp, SWT.NO_FOCUS); + taskListCmp.setLayoutData(EclipseUiUtils.fillAll()); + forceRefresh(null); } @Override public void forceRefresh(Object object) { - refreshDocListGadget(); + CmsUtils.clear(headerCmp); + populateHeaderPart(headerCmp, NodeUtils.getUserHome(getSession())); + + CmsUtils.clear(taskListCmp); + populateTaskListCmp(taskListCmp); + + headerCmp.getParent().layout(true, true); } - @Override - public void setFocus() { - refreshDocListGadget(); + private void populateTaskListCmp(Composite parent) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + NodeIterator nit = activitiesService.getMyTasks(getSession(), true); + if (!nit.hasNext()) { + Composite noTaskCmp = new Composite(parent, SWT.NO_FOCUS); + noTaskCmp.setLayoutData(EclipseUiUtils.fillAll()); + noTaskCmp.setLayout(new GridLayout()); + + Label noTaskLbl = new Label(noTaskCmp, SWT.CENTER); + noTaskLbl.setText(" You have no pending Task. "); + CmsUtils.markup(noTaskLbl); + noTaskLbl.setLayoutData(new GridData(SWT.CENTER, SWT.BOTTOM, true, true)); + + final Link createTaskLk = new Link(noTaskCmp, SWT.CENTER); + createTaskLk.setText(" Create a task "); + createTaskLk.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, true)); + + createTaskLk.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -9028457805156989935L; + + @Override + public void widgetSelected(SelectionEvent e) { + String mainMixin = TrackerTypes.TRACKER_TASK; + String pathCreated = ConnectWorkbenchUtils.createAndConfigureEntity(createTaskLk.getShell(), getSession(), + getSystemAppService(), getSystemWorkbenchService(), mainMixin); + if (EclipseUiUtils.notEmpty(pathCreated)) + forceRefresh(null); + } + }); + + } else { + TaskListLabelProvider labelProvider = new TaskListLabelProvider(trackerService); + tvlc = new TaskVirtualListComposite(parent, SWT.NO_FOCUS, labelProvider, 54); + tvlc.setLayoutData(EclipseUiUtils.fillAll()); + final TableViewer viewer = tvlc.getTableViewer(); + viewer.setInput(JcrUtils.nodeIteratorToList(nit).toArray()); + final TaskViewerContextMenu contextMenu = new TaskViewerContextMenu(viewer, getSession(), + activitiesService) { + @Override + public boolean performAction(String actionId) { + boolean hasChanged = super.performAction(actionId); + if (hasChanged) { + viewer.getTable().setFocus(); + forceRefresh(null); + // NodeIterator nit = + // activitiesService.getMyTasks(getSession(), true); + // viewer.setInput(JcrUtils.nodeIteratorToList(nit).toArray()); + } + return hasChanged; + } + }; + viewer.getTable().addMouseListener(new MouseAdapter() { + private static final long serialVersionUID = 6737579410648595940L; + + @Override + public void mouseDown(MouseEvent e) { + if (e.button == 3) { + // contextMenu.setCurrFolderPath(currDisplayedFolder); + contextMenu.show(viewer.getTable(), new Point(e.x, e.y), + (IStructuredSelection) viewer.getSelection()); + } + } + }); + + } } - /** Links to the various last updated docs */ - private void refreshDocListGadget() { - EclipseUiUtils.clear(projectsGadget); - projectsGadget.setLayout(EclipseUiUtils.noSpaceGridLayout()); - createGadgetTitleCmp(projectsGadget, "Last updated documents"); - Composite bodyCmp = createGadgetBodyCmp(projectsGadget); - - NodeIterator nit = getDocumentsService().getLastUpdatedDocuments(getSession()); - while (nit.hasNext()) { - Node file = nit.nextNode(); - createOpenEntityEditorLink(getAppWorkbenchService(), bodyCmp, ConnectJcrUtils.getName(file), file); + private boolean isOverdue(Node node, String propName) { + try { + Calendar now = GregorianCalendar.getInstance(); + return node.hasProperty(propName) && node.getProperty(propName).getDate().before(now); + } catch (RepositoryException e) { + throw new SuiteWorkbenchException("Cannot check overdue status with property " + propName + " on " + node, e); } - projectsGadget.layout(true, true); } - /** Links to the various contact search pages */ - private void populateContactsGadget(Composite parent) { - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - createGadgetTitleCmp(parent, "Contacts"); - Composite bodyCmp = createGadgetBodyCmp(parent); + private void populateHeaderPart(Composite bodyCmp, Node context) { + bodyCmp.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, true))); - PeopleRapUtils.createOpenSearchEditorLink(getAppWorkbenchService(), bodyCmp, "Persons", - PeopleTypes.PEOPLE_PERSON, getPeopleService().getBasePath(PeopleTypes.PEOPLE_PERSON)); + 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()); - PeopleRapUtils.createOpenSearchEditorLink(getAppWorkbenchService(), bodyCmp, "Organisations", - PeopleTypes.PEOPLE_ORG, getPeopleService().getBasePath(PeopleTypes.PEOPLE_ORG)); + // 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); - Node tagParent = getPeopleService().getResourceService().getTagLikeResourceParent(getSession(), - PeopleTypes.PEOPLE_MAILING_LIST); - PeopleRapUtils.createOpenSearchEditorLink(getAppWorkbenchService(), bodyCmp, "Mailing lists", - PeopleTypes.PEOPLE_MAILING_LIST, ConnectJcrUtils.getPath(tagParent)); - PeopleRapUtils.createOpenSearchEditorLink(getAppWorkbenchService(), bodyCmp, "Tasks", PeopleTypes.PEOPLE_TASK, - getPeopleService().getBasePath(null)); + NodeIterator nit = activitiesService.getMyTasks(getSession(), true); + if (nit.hasNext()) { + List overdueTasks = new ArrayList<>(); + while (nit.hasNext()) { + Node currNode = nit.nextNode(); + if (isOverdue(currNode, ActivitiesNames.ACTIVITIES_DUE_DATE)) + 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); + } + } - tagParent = getPeopleService().getResourceService().getTagLikeResourceParent(getSession(), - PeopleConstants.RESOURCE_TAG); + nit = trackerService.getMyMilestones(getSession(), true); + List openMilestones = new ArrayList<>(); - PeopleRapUtils.createOpenSearchEditorLink(getAppWorkbenchService(), bodyCmp, "Tags", - PeopleTypes.PEOPLE_TAG_INSTANCE, ConnectJcrUtils.getPath(tagParent)); + if (nit.hasNext()) { + List overdueMilestones = new ArrayList<>(); + while (nit.hasNext()) { + Node currNode = nit.nextNode(); + openMilestones.add(currNode); + if (isOverdue(currNode, TrackerNames.TRACKER_TARGET_DATE)) + 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); + } + } + + 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 num = allNb - openNb; + double result = num / 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 num = allNb - openNb; + double result = num / 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 | SWT.WRAP); + 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)); + } + }); + } + } + }