package com.psddev.cms.tool.widget; import java.io.IOException; import java.util.Locale; import java.util.Map; import java.util.TreeMap; import java.util.UUID; import java.util.stream.Collectors; import javax.servlet.ServletException; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.psddev.cms.db.Content; import com.psddev.cms.db.Draft; import com.psddev.cms.db.ToolRole; import com.psddev.cms.db.ToolUi; import com.psddev.cms.db.ToolUser; import com.psddev.cms.db.Workflow; import com.psddev.cms.db.WorkflowState; import com.psddev.cms.tool.Dashboard; import com.psddev.cms.tool.DefaultDashboardWidget; import com.psddev.cms.tool.QueryRestriction; import com.psddev.cms.tool.ToolPageContext; import com.psddev.dari.db.ObjectType; import com.psddev.dari.db.Query; import com.psddev.dari.db.QueryFilter; import com.psddev.dari.db.State; import com.psddev.dari.util.JspUtils; import com.psddev.dari.util.PaginatedResult; public class UnpublishedDraftsWidget extends DefaultDashboardWidget { private static final int[] LIMITS = { 10, 20, 50 }; @Override public int getColumnIndex() { return 1; } @Override public int getWidgetIndex() { return 3; } @Override public void writeHtml(ToolPageContext page, Dashboard dashboard) throws IOException, ServletException { Query<Workflow> workflowQuery = Query.from(Workflow.class); if (page.getSite() != null) { workflowQuery.where("sites is missing or sites = ?", page.getSite()); } Map<String, String> workflowStateLabels = new TreeMap<>(); workflowStateLabels.put("draft", "Initial Draft"); for (Workflow w : workflowQuery.iterable(0)) { for (WorkflowState s : w.getStates()) { workflowStateLabels.put("ws." + s.getName(), s.getDisplayName()); } } String state = page.pageParam(String.class, "state", null); final ObjectType type = ObjectType.getInstance(page.pageParam(UUID.class, "typeId", null)); Query<?> draftsQuery; if ("draft".equals(state)) { draftsQuery = Query .from(Object.class) .where("_type = ? or cms.content.draft = true", Draft.class); } else if (state != null && state.startsWith("ws.")) { draftsQuery = Query .from(Object.class) .where("cms.workflow.currentState = ?", state.substring(3)); } else { draftsQuery = Query .from(Object.class) .where("_type = ? or cms.content.draft = true or cms.workflow.currentState != missing", Draft.class); } final UserType userType = page.pageParam(UserType.class, "userType", UserType.ANYONE); String userParameter = userType + ".value"; final Object user = Query.from(Object.class).where("_id = ?", page.pageParam(UUID.class, userParameter, null)).first(); QueryFilter<Object> queryFilter = null; if (type != null || userType != UserType.ANYONE) { queryFilter = item -> { State itemState = State.getInstance(item); boolean typeOk = true; boolean userOk = true; if (type != null) { ObjectType itemType = item instanceof Draft ? ((Draft) item).getObjectType() : itemState.getType(); typeOk = itemType != null && itemType.getGroups().contains(type.getInternalName()); } if (userType != UserType.ANYONE) { ToolUser updateUser = itemState.as(Content.ObjectModification.class).getUpdateUser(); if (userType == UserType.ME) { userOk = page.getUser().equals(updateUser); } else if (user instanceof ToolUser) { userOk = user.equals(updateUser); } else if (user instanceof ToolRole && updateUser != null) { userOk = user.equals(updateUser.getRole()); } } return typeOk && userOk; }; } QueryRestriction.updateQueryUsingAll(draftsQuery, page); int limit = page.pageParam(int.class, "limit", 20); PaginatedResult<?> drafts = draftsQuery .and("* matches *") .and(Content.UPDATE_DATE_FIELD + " != missing") .and(page.siteItemsPredicate()) .sortDescending(Content.UPDATE_DATE_FIELD) .selectFiltered(page.param(long.class, "offset"), limit, queryFilter); page.writeStart("div", "class", "widget widget-unpublishedDrafts"); page.writeStart("h1", "class", "icon icon-object-draft"); page.writeHtml(page.localize(UnpublishedDraftsWidget.class, "title")); page.writeEnd(); page.writeStart("div", "class", "widget-filters"); for (Class<? extends QueryRestriction> c : QueryRestriction.classIterable()) { page.writeQueryRestrictionForm(c); } page.writeStart("form", "method", "get", "action", page.url(null)); if (workflowStateLabels.size() > 1) { page.writeStart("select", "data-bsp-autosubmit", "", "name", "state"); page.writeStart("option", "value", ""); page.writeHtml(page.localize( UnpublishedDraftsWidget.class, "option.anyStatuses")); page.writeEnd(); for (Map.Entry<String, String> entry : workflowStateLabels.entrySet()) { String key = entry.getKey(); page.writeStart("option", "selected", key.equals(state) ? "selected" : null, "value", key); page.writeHtml(entry.getValue()); page.writeEnd(); } page.writeEnd(); } page.writeTypeSelect( ObjectType.getInstance(Content.class).as(ToolUi.class).findDisplayTypes() .stream() .filter(page.createTypeDisplayPredicate(ImmutableSet.of("read"))) .collect(Collectors.toList()), type, page.localize(UnpublishedDraftsWidget.class, "label.allTypes"), "name", "typeId", "data-bsp-autosubmit", "", "data-searchable", true); page.writeStart("select", "data-bsp-autosubmit", "", "name", "userType"); for (UserType t : UserType.values()) { if (t != UserType.ROLE || Query.from(ToolRole.class).first() != null) { page.writeStart("option", "selected", t.equals(userType) ? "selected" : null, "value", t.name()); page.writeHtml(page.localize(UnpublishedDraftsWidget.class, t.getResourceKey())); page.writeEnd(); } } page.writeEnd(); Query<?> userQuery; if (userType == UserType.ROLE) { userQuery = Query.from(ToolRole.class).sortAscending("name"); } else if (userType == UserType.USER) { userQuery = Query.from(ToolUser.class).sortAscending("name"); } else { userQuery = null; } if (userQuery != null) { if (userQuery.hasMoreThan(250)) { State userState = State.getInstance(user); page.writeElement("input", "type", "text", "class", "objectId", "data-bsp-autosubmit", "", "data-editable", false, "data-label", userState != null ? userState.getLabel() : null, "data-typeIds", ObjectType.getInstance(ToolRole.class).getId(), "name", userParameter, "value", userState != null ? userState.getId() : null); } else { page.writeStart("select", "name", userParameter, "data-bsp-autosubmit", "", "data-searchable", "true"); page.writeStart("option", "value", "").writeEnd(); for (Object v : userQuery.selectAll()) { State userState = State.getInstance(v); page.writeStart("option", "value", userState.getId(), "selected", v.equals(user) ? "selected" : null); page.writeHtml(userState.getLabel()); page.writeEnd(); } page.writeEnd(); } } page.writeEnd(); page.writeEnd(); if (drafts.getItems().isEmpty()) { String label = state != null ? workflowStateLabels.get(state) : null; page.writeStart("div", "class", "message message-info"); page.writeHtml(page.localize( UnpublishedDraftsWidget.class, ImmutableMap.of( "label", label != null ? label.toLowerCase(Locale.ENGLISH) : page.localize(UnpublishedDraftsWidget.class, "label.matching")), "message.noDrafts")); page.writeEnd(); } else { page.writeStart("ul", "class", "pagination"); if (drafts.hasPrevious()) { page.writeStart("li", "class", "first"); page.writeStart("a", "href", page.url("", "offset", drafts.getFirstOffset())); page.writeHtml(page.localize(UnpublishedDraftsWidget.class, "pagination.newest")); page.writeEnd(); page.writeEnd(); page.writeStart("li", "class", "previous"); page.writeStart("a", "href", page.url("", "offset", drafts.getPreviousOffset())); page.writeHtml(page.localize( UnpublishedDraftsWidget.class, ImmutableMap.of("count", drafts.getLimit()), "pagination.newerCount")); page.writeEnd(); page.writeEnd(); } page.writeStart("li"); page.writeStart("form", "data-bsp-autosubmit", "", "method", "get", "action", page.url(null)); page.writeStart("select", "name", "limit"); for (int l : LIMITS) { page.writeStart("option", "value", l, "selected", limit == l ? "selected" : null); page.writeHtml(page.localize( UnpublishedDraftsWidget.class, ImmutableMap.of("count", l), "option.showCount")); page.writeEnd(); } page.writeEnd(); page.writeEnd(); page.writeEnd(); if (drafts.hasNext()) { page.writeStart("li", "class", "next"); page.writeStart("a", "href", page.url("", "offset", drafts.getNextOffset())); page.writeHtml(page.localize( UnpublishedDraftsWidget.class, ImmutableMap.of("count", drafts.getLimit()), "pagination.olderCount")); page.writeEnd(); page.writeEnd(); } page.writeEnd(); page.writeStart("table", "class", "links table-striped pageThumbnails"); page.writeStart("tbody"); for (Object item : drafts.getItems()) { if (item instanceof Draft) { Draft draft = (Draft) item; item = draft.recreate(); if (item == null) { continue; } State itemState = State.getInstance(item); if (!itemState.isVisible() && draft.getDifferences().isEmpty()) { continue; } UUID draftId = draft.getId(); page.writeStart("tr", "data-preview-url", JspUtils.getAbsolutePath(page.getRequest(), "/_preview", "_cms.db.previewId", draftId)); page.writeStart("td"); page.writeHtml(page.getTypeLabel(item)); page.writeEnd(); page.writeStart("td"); page.writeStart("a", "target", "_top", "href", page.url("/content/edit.jsp", "id", itemState.getId(), "draftId", draftId)); page.writeObjectLabel(draft); page.writeEnd(); page.writeEnd(); page.writeStart("td"); page.writeObjectLabel(draft.as(Content.ObjectModification.class).getUpdateUser()); page.writeEnd(); page.writeEnd(); } else { State itemState = State.getInstance(item); UUID itemId = itemState.getId(); page.writeStart("tr", "data-preview-url", JspUtils.getAbsolutePath(page.getRequest(), "/_preview", "_cms.db.previewId", itemId)); page.writeStart("td"); page.writeHtml(page.getTypeLabel(item)); page.writeEnd(); page.writeStart("td"); page.writeStart("a", "href", page.url("/content/edit.jsp", "id", itemId), "target", "_top"); page.writeObjectLabel(itemState); page.writeEnd(); page.writeEnd(); page.writeStart("td"); page.writeObjectLabel(itemState.as(Content.ObjectModification.class).getUpdateUser()); page.writeEnd(); page.writeEnd(); } } page.writeEnd(); page.writeEnd(); } page.writeEnd(); } private enum UserType { ANYONE("label.anyone"), ME("label.me"), ROLE("label.role"), USER("label.user"); private String resourceKey; private UserType(String resourceKey) { this.resourceKey = resourceKey; } public String getResourceKey() { return resourceKey; } } }