package com.psddev.cms.tool.widget; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; import javax.servlet.ServletException; import com.psddev.dari.db.Database; import com.psddev.dari.util.JspUtils; import org.joda.time.DateTime; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.psddev.cms.db.Content; import com.psddev.cms.db.Directory; import com.psddev.cms.db.Renderer; import com.psddev.cms.db.ToolRole; import com.psddev.cms.db.ToolUi; import com.psddev.cms.db.ToolUser; import com.psddev.cms.tool.Dashboard; import com.psddev.cms.tool.DefaultDashboardWidget; import com.psddev.cms.tool.QueryRestriction; import com.psddev.cms.tool.Search; 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.ObjectUtils; import com.psddev.dari.util.PaginatedResult; public class RecentActivityWidget extends DefaultDashboardWidget { private static final int[] LIMITS = { 10, 20, 50 }; @Override public int getColumnIndex() { return 0; } @Override public int getWidgetIndex() { return 0; } @Override @SuppressWarnings("deprecation") public void writeHtml(ToolPageContext page, Dashboard dashboard) throws IOException, ServletException { ObjectType itemType = Query.findById(ObjectType.class, page.pageParam(UUID.class, "itemType", null)); Type type = page.pageParam(Type.class, "type", Type.ANYONE); String valueParameter = type + ".value"; Object valueObject = Query.from(Object.class).where("_id = ?", page.pageParam(UUID.class, valueParameter, null)).first(); long offset = page.param(long.class, "offset"); int limit = page.pageParam(Integer.class, "limit", 20); if (type == null) { type = Type.ANYONE; } PaginatedResult<?> result; List<String> visibilities = page.pageParams(String.class, Search.VISIBILITIES_PARAMETER, new ArrayList<>()); if (valueObject == null && (type == Type.ROLE || type == Type.USER)) { result = null; } else { Query<?> contentQuery = (itemType != null && !visibilities.contains("d") ? Query.fromType(itemType) : Query.fromGroup(Content.SEARCHABLE_GROUP)) .where(page.siteItemsSearchPredicate()) .and(Content.UPDATE_DATE_FIELD + " != missing") .sortDescending(Content.UPDATE_DATE_FIELD); switch (type) { case ROLE : contentQuery.and(Content.UPDATE_USER_FIELD + " = ?", Query.from(ToolUser.class).where("role = ?", valueObject)); break; case USER : contentQuery.and(Content.UPDATE_USER_FIELD + " = ?", valueObject); break; case ME : contentQuery.and(Content.UPDATE_USER_FIELD + " = ?", page.getUser()); break; default : break; } contentQuery.and(Search.getVisibilitiesPredicate(itemType, visibilities, null, false)); if (itemType == null) { contentQuery.and(page.userTypesPredicate()); } QueryRestriction.updateQueryUsingAll(contentQuery, page); result = contentQuery.and("_any matches *").select(offset, limit); } page.writeStart("div", "class", "widget"); page.writeStart("h1", "class", "icon icon-list"); page.writeHtml(page.localize(RecentActivityWidget.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)); page.writeTypeSelect( Database.Static.getDefault().getEnvironment().getTypesByGroup(Content.SEARCHABLE_GROUP).stream() .flatMap(t -> t.as(ToolUi.class).findDisplayTypes().stream()) .distinct() .filter(page.createTypeDisplayPredicate(ImmutableSet.of("read"))) .collect(Collectors.toList()), itemType, page.localize(RecentActivityWidget.class, "label.allTypes"), "data-bsp-autosubmit", "", "name", "itemType", "data-searchable", "true"); page.writeStart("select", "data-bsp-autosubmit", "", "name", "type"); for (Type t : Type.values()) { if (t != Type.ROLE || Query.from(ToolRole.class).first() != null) { page.writeStart("option", "selected", t.equals(type) ? "selected" : null, "value", t.name()); page.writeHtml(page.localize(null, t.getResourceKey())); page.writeEnd(); } } page.writeEnd(); Query<?> valueQuery; if (type == Type.ROLE) { valueQuery = Query.from(ToolRole.class).sortAscending("name"); } else if (type == Type.USER) { valueQuery = Query.from(ToolUser.class).sortAscending("name"); } else { valueQuery = null; } if (valueQuery != null) { if (valueQuery.hasMoreThan(250)) { State valueState = State.getInstance(valueObject); page.writeElement("input", "data-bsp-autosubmit", "", "type", "text", "class", "objectId", "data-editable", false, "data-label", valueState != null ? valueState.getLabel() : null, "data-typeIds", ObjectType.getInstance(valueQuery.getGroup()).getId(), "name", valueParameter, "value", valueState != null ? valueState.getId() : null); } else { page.writeStart("select", "data-bsp-autosubmit", "", "name", valueParameter, "data-searchable", "true"); page.writeStart("option", "value", "").writeEnd(); for (Object v : valueQuery.selectAll()) { State state = State.getInstance(v); page.writeStart("option", "value", state.getId(), "selected", v.equals(valueObject) ? "selected" : null); page.writeHtml(state.getLabel()); page.writeEnd(); } page.writeEnd(); } } page.writeTag("input", "type", "hidden", "name", Search.VISIBILITIES_PARAMETER, "value", ""); // extra hidden field so ToolPageContext#pageParams() knows we are intentionally submitting nothing page.writeMultipleVisibilitySelect(itemType, visibilities, "name", Search.VISIBILITIES_PARAMETER, "data-bsp-autosubmit", ""); page.writeEnd(); page.writeEnd(); if (result == null) { page.writeStart("div", "class", "message message-warning"); page.writeStart("p"); page.writeHtml(page.localize( RecentActivityWidget.class, ImmutableMap.of("type", page.localize(RecentActivityWidget.class, type.getResourceKey())), "warn.typeSelect")); page.writeEnd(); page.writeEnd(); } else if (!result.hasPages()) { page.writeStart("div", "class", "message message-info"); page.writeStart("p"); page.writeHtml(page.localize(RecentActivityWidget.class, "message.noActivity")); page.writeEnd(); page.writeEnd(); } else { page.writeStart("ul", "class", "pagination"); if (result.hasPrevious()) { page.writeStart("li", "class", "first"); page.writeStart("a", "href", page.url("", "offset", result.getFirstOffset())); page.writeHtml(page.localize( RecentActivityWidget.class, "pagination.newest")); page.writeEnd(); page.writeEnd(); page.writeStart("li", "class", "previous"); page.writeStart("a", "href", page.url("", "offset", result.getPreviousOffset())); page.writeHtml(page.localize( ImmutableMap.of("count", limit), "pagination.newerCount")); page.writeEnd(); page.writeEnd(); } if (result.getOffset() > 0 || result.hasNext() || result.getItems().size() > LIMITS[0]) { 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( RecentActivityWidget.class, ImmutableMap.of("count", l), "option.showCount")); page.writeEnd(); } page.writeEnd(); page.writeEnd(); page.writeEnd(); } if (result.hasNext()) { page.writeStart("li", "class", "next"); page.writeStart("a", "href", page.url("", "offset", result.getNextOffset())); page.writeHtml(page.localize( RecentActivityWidget.class, ImmutableMap.of("count", limit), "pagination.olderCount")); page.writeEnd(); page.writeEnd(); } page.writeEnd(); page.writeStart("table", "class", "links table-striped pageThumbnails").writeStart("tbody"); String lastUpdateDate = null; for (Object content : result.getItems()) { State contentState = State.getInstance(content); String permalink = contentState.as(Directory.ObjectModification.class).getPermalink(); Content.ObjectModification contentData = contentState.as(Content.ObjectModification.class); DateTime updateDateTime = page.toUserDateTime(contentData.getUpdateDate()); String updateDate = page.formatUserDate(updateDateTime); ToolUser updateUser = contentData.getUpdateUser(); Integer embedWidth = null; if (ObjectUtils.isBlank(permalink)) { ObjectType contentStateType = contentState.getType(); if (contentStateType != null) { Renderer.TypeModification rendererData = contentStateType.as(Renderer.TypeModification.class); int previewWidth = rendererData.getEmbedPreviewWidth(); if (previewWidth > 0 && page.isEmbeddable(content)) { permalink = JspUtils.getAbsolutePath(page.getRequest(), "/_preview", "_embed", "true", "_cms.db.previewId", contentState.getId()); embedWidth = previewWidth; } } } page.writeStart("tr", "data-preview-url", permalink, "data-preview-embed-width", embedWidth); page.writeStart("td", "class", "date"); if (!updateDate.equals(lastUpdateDate)) { page.writeHtml(updateDate); lastUpdateDate = updateDate; } page.writeEnd(); page.writeStart("td", "class", "time"); page.writeHtml(page.formatUserTime(updateDateTime)); page.writeEnd(); page.writeStart("td"); page.writeTypeLabel(content); page.writeEnd(); page.writeStart("td"); page.writeStart("a", "href", page.objectUrl("/content/edit.jsp", content), "target", "_top"); page.writeObjectLabel(content); page.writeEnd(); page.writeEnd(); page.writeStart("td"); page.writeObjectLabel(updateUser); page.writeEnd(); if (page.getCmsTool().isEnableViewers()) { page.writeStart("td"); page.writeStart("div", "class", "EditFieldUpdateViewers", "data-rtc-content-id", contentState.getId().toString()); { page.writeStart("div", "data-rtc-edit-field-update-viewers", ""); page.writeEnd(); } page.writeEnd(); page.writeEnd(); } page.writeEnd(); } page.writeEnd().writeEnd(); } page.writeEnd(); } private enum Type { ANYONE("label.anyone"), ME("label.me"), ROLE("label.role"), USER("label.user"); private String resourceKey; Type(String resourceKey) { this.resourceKey = resourceKey; } public String getResourceKey() { return resourceKey; } } }