package com.psddev.cms.tool.widget; import java.io.IOException; import java.util.List; import java.util.UUID; import javax.servlet.ServletException; import com.google.common.collect.ImmutableMap; import com.psddev.cms.db.ToolRole; import com.psddev.cms.db.ToolUser; import com.psddev.cms.db.WorkStream; import com.psddev.cms.tool.Dashboard; import com.psddev.cms.tool.DefaultDashboardWidget; import com.psddev.cms.tool.ToolPageContext; import com.psddev.dari.db.ObjectType; import com.psddev.dari.db.Query; import com.psddev.dari.db.State; import com.psddev.dari.util.PaginatedResult; public class WorkStreamsWidget extends DefaultDashboardWidget { private static final int[] LIMITS = { 10, 20, 50 }; private static final String TOOL_ENTITY_TYPE_PARAMETER = "toolEntityType"; private static final String TOOL_ENTITY_VALUE_PARAMETER = "toolEntity"; private static final String OFFSET_PARAMETER = "offset"; private static final String LIMIT_PARAMETER = "limit"; private static final String STOP_PARAMETER = "stop"; @Override public int getColumnIndex() { return 0; } @Override public int getWidgetIndex() { return 1; } @Override public void writeHtml(ToolPageContext page, Dashboard dashboard) throws IOException, ServletException { int limit = page.pageParam(Integer.class, LIMIT_PARAMETER, LIMITS[0]); UUID stop = page.param(UUID.class, STOP_PARAMETER); ToolUser user = page.getUser(); // Actions if (stop != null) { WorkStream toStop = Query.from(WorkStream.class).where("id = ?", stop).first(); if (toStop != null) { toStop.stop(user); } page.redirect(null); return; } PaginatedResult<WorkStream> results = getResults(page); page.writeHeader(); page.writeStart("div", "class", "widget p-workStreams"); page.writeStart("h1", "class", "icon icon-object-workStream"); page.writeHtml(page.localize(WorkStreamsWidget.class, "title")); page.writeEnd(); writeFilters(page); if (!results.hasPages()) { //workStreams.isEmpty()) { page.writeStart("div", "class", "message message-info"); page.writeHtml(page.localize(WorkStreamsWidget.class, "message.noWorkStreams")); page.writeEnd(); } else { writePaginationHtml(page, results, limit); // Results List<WorkStream> workStreams = results.getItems(); for (WorkStream workStream : workStreams) { List<ToolUser> users = workStream.getUsers(); long skipped = workStream.countSkipped(user); long complete = workStream.countComplete(); long incomplete = workStream.countIncomplete() - skipped; long total = complete + incomplete + skipped; boolean working = workStream.isWorking(user); page.writeStart("div", "class", "block " + (working ? "p-workStreams-working" : "p-workStreams-notWorking"), "style", page.cssString( "padding-right", working ? "165px" : "75px", "position", "relative")); //TODO: LOCALIZE if (users.isEmpty()) { page.writeHtml("No users"); } else { page.writeStart("a", "href", page.url("/workStreamUsers", "id", workStream.getId()), "target", "workStream"); page.writeHtml(users.size()); page.writeHtml(" users"); page.writeEnd(); } page.writeHtml(" working on "); page.writeStart("a", "href", page.objectUrl("/content/editWorkStream", workStream, "reload", true), "target", "workStream"); page.writeObjectLabel(workStream); page.writeEnd(); if (working) { page.writeStart("a", "class", "button p-workStreams-continue", "href", page.url("/content/edit.jsp", "workStreamId", workStream.getId(), "_", System.currentTimeMillis()), "target", "_top", "style", page.cssString( "bottom", 0, "position", "absolute", "right", "70px", "text-align", "center", "width", "90px")); page.writeHtml(page.localize(WorkStreamsWidget.class, "action.continue")); page.writeEnd(); page.writeStart("a", "class", "button p-workStreams-stop", "href", page.url("", "stop", workStream.getId()), "style", page.cssString( "bottom", 0, "position", "absolute", "right", 0, "text-align", "center", "width", "65px")); page.writeHtml(page.localize(WorkStreamsWidget.class, "action.stop")); page.writeEnd(); } else { page.writeStart("a", "class", "button p-workStreams-start", "href", page.url("/content/edit.jsp", "workStreamId", workStream.getId(), "_", System.currentTimeMillis()), "target", "_top", "style", page.cssString( "bottom", 0, "position", "absolute", "right", 0, "text-align", "center", "width", "70px")); page.writeHtml(page.localize(WorkStreamsWidget.class, "action.start")); page.writeEnd(); } page.writeStart("div", "class", "progress"); page.writeStart("div", "class", "progressBar", "style", "width:" + ((total - incomplete) * 100.0 / total) + "%"); page.writeEnd(); //TODO: LOCALIZE page.writeStart("strong"); page.writeHtml(incomplete); page.writeEnd(); page.writeHtml(" of "); page.writeStart("strong"); page.writeHtml(total); page.writeEnd(); page.writeHtml(" left "); if (complete > 0L || skipped > 0L) { page.writeHtml("("); } if (complete > 0L) { page.writeStart("strong"); page.writeHtml(complete); page.writeEnd(); page.writeHtml(" complete"); if (skipped > 0L) { page.writeHtml(", "); } } if (skipped > 0L) { page.writeStart("strong"); page.writeHtml(skipped); page.writeEnd(); page.writeHtml(" skipped"); } if (complete > 0L || skipped > 0L) { page.writeHtml(")"); } page.writeEnd(); page.writeEnd(); } } page.writeEnd(); page.writeFooter(); } private PaginatedResult<WorkStream> getResults(ToolPageContext page) { Query<WorkStream> query = Query.from(WorkStream.class).where(page.siteItemsPredicate()); ToolEntityType entityType = page.pageParam(ToolEntityType.class, TOOL_ENTITY_TYPE_PARAMETER, ToolEntityType.ANYONE); UUID entityId = null; if (entityType == ToolEntityType.USER || entityType == ToolEntityType.ROLE) { entityId = page.pageParam(UUID.class, TOOL_ENTITY_VALUE_PARAMETER, null); } else if (entityType == ToolEntityType.ME) { entityId = page.getUser().getId(); } if (entityId != null) { query.and("assignedEntities = ?", entityId); } return query.select(page.param(long.class, OFFSET_PARAMETER), page.paramOrDefault(int.class, LIMIT_PARAMETER, LIMITS[0])); } private void writeFilters(ToolPageContext page) throws IOException { page.writeStart("div", "class", "widget-filters"); page.writeStart("form", "method", "get", "action", page.url(null)); page.writeStart("select", "data-bsp-autosubmit", "", "name", TOOL_ENTITY_TYPE_PARAMETER, "data-searchable", "true"); ToolEntityType userType = page.pageParam(ToolEntityType.class, TOOL_ENTITY_TYPE_PARAMETER, ToolEntityType.ANYONE); for (ToolEntityType t : ToolEntityType.values()) { if (t != ToolEntityType.ROLE || Query.from(ToolRole.class).first() != null) { page.writeStart("option", "selected", t.equals(userType) ? "selected" : null, "value", t.name()); page.writeHtml(page.localize(null, t.getResourceKey())); page.writeEnd(); } } page.writeEnd(); // TODO: move somewhere reusable (duplicated in other widgets) ObjectType toolEntityType; Query<?> toolEntityQuery; if (userType == ToolEntityType.ROLE) { toolEntityType = ObjectType.getInstance(ToolRole.class); toolEntityQuery = Query.from(ToolRole.class).sortAscending("name"); } else if (userType == ToolEntityType.USER) { toolEntityType = ObjectType.getInstance(ToolUser.class); toolEntityQuery = Query.from(ToolUser.class).sortAscending("name"); } else { toolEntityType = null; toolEntityQuery = null; } if (toolEntityQuery != null) { Object toolEntity = Query.from(Object.class).where("_id = ?", page.pageParam(UUID.class, TOOL_ENTITY_VALUE_PARAMETER, null)).first(); if (toolEntityQuery.hasMoreThan(250)) { State toolEntityState = State.getInstance(toolEntity); page.writeElement("input", "type", "text", "class", "objectId", "data-bsp-autosubmit", "", "data-editable", false, "data-label", toolEntityState != null ? toolEntityState.getLabel() : null, "data-typeIds", toolEntityType != null ? toolEntityType.getId() : null, "name", TOOL_ENTITY_VALUE_PARAMETER, "value", toolEntityState != null ? toolEntityState.getId() : null); } else { page.writeStart("select", "name", TOOL_ENTITY_VALUE_PARAMETER, "data-bsp-autosubmit", "", "data-searchable", "true"); page.writeStart("option", "value", "").writeEnd(); for (Object v : toolEntityQuery.selectAll()) { State userState = State.getInstance(v); page.writeStart("option", "value", userState.getId(), "selected", v.equals(toolEntity) ? "selected" : null); page.writeHtml(userState.getLabel()); page.writeEnd(); } page.writeEnd(); } } page.writeEnd(); page.writeEnd(); } private void writePaginationHtml(ToolPageContext page, PaginatedResult<WorkStream> results, int limit) throws IOException { // Pagination page.writeStart("ul", "class", "pagination"); if (results.hasPrevious()) { page.writeStart("li", "class", "first"); page.writeStart("a", "href", page.url("", OFFSET_PARAMETER, results.getFirstOffset())); page.writeHtml(page.localize(WorkStreamsWidget.class, "pagination.newest")); page.writeEnd(); page.writeEnd(); page.writeStart("li", "class", "previous"); page.writeStart("a", "href", page.url("", OFFSET_PARAMETER, results.getPreviousOffset())); page.writeHtml(page.localize(ImmutableMap.of("count", limit), "pagination.newerCount")); page.writeEnd(); page.writeEnd(); } if (results.getOffset() > 0 || results.hasNext() || results.getItems().size() > LIMITS[0]) { page.writeStart("li"); page.writeStart("form", "data-bsp-autosubmit", "", "method", "get", "action", page.url(null)); page.writeStart("select", "name", LIMIT_PARAMETER); for (int l : LIMITS) { page.writeStart("option", "value", l, "selected", limit == l ? "selected" : null); page.writeHtml(page.localize(WorkStreamsWidget.class, ImmutableMap.of("count", l), "option.showCount")); page.writeEnd(); } page.writeEnd(); page.writeEnd(); page.writeEnd(); } if (results.hasNext()) { page.writeStart("li", "class", "next"); page.writeStart("a", "href", page.url("", "offset", results.getNextOffset())); page.writeHtml(page.localize(WorkStreamsWidget.class, ImmutableMap.of("count", limit), "pagination.olderCount")); page.writeEnd(); page.writeEnd(); } page.writeEnd(); } private enum ToolEntityType { ANYONE("label.anyone"), ME("label.me"), ROLE("label.role"), USER("label.user"); private String resourceKey; ToolEntityType(String resourceKey) { this.resourceKey = resourceKey; } public String getResourceKey() { return resourceKey; } } }