package com.psddev.cms.tool.widget;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.servlet.ServletException;
import com.google.common.collect.ImmutableSet;
import com.psddev.cms.db.Content;
import com.psddev.cms.db.ContentTemplate;
import com.psddev.cms.db.Directory;
import com.psddev.cms.db.Localization;
import com.psddev.cms.db.Site;
import com.psddev.cms.db.ToolRole;
import com.psddev.cms.db.ToolUi;
import com.psddev.cms.db.ToolUser;
import com.psddev.cms.tool.CmsTool;
import com.psddev.cms.tool.Dashboard;
import com.psddev.cms.tool.DefaultDashboardWidget;
import com.psddev.cms.tool.ObjectTypeOrContentTemplate;
import com.psddev.cms.tool.ToolPageContext;
import com.psddev.dari.db.Database;
import com.psddev.dari.db.Modification;
import com.psddev.dari.db.ObjectType;
import com.psddev.dari.db.Query;
import com.psddev.dari.db.Recordable;
import com.psddev.dari.db.Singleton;
import com.psddev.dari.db.State;
import com.psddev.dari.util.ObjectUtils;
@SuppressWarnings("deprecation")
public class CreateNewWidget extends DefaultDashboardWidget {
@Override
public int getColumnIndex() {
return 1;
}
@Override
public int getWidgetIndex() {
return 0;
}
@Override
public void writeHtml(ToolPageContext page, Dashboard dashboard) throws IOException, ServletException {
String redirect = page.param(String.class, "redirect");
CmsTool.CommonContentSettings settings = null;
ToolUser user = page.getUser();
List<String> includeFields = Arrays.asList("toolUserCreateNewSettings.editExistingContents");
if (user != null) {
ToolRole role = user.getRole();
if (role != null) {
settings = role.getRoleCommonContentSettings();
}
}
if (settings == null) {
Site site = page.getSite();
if (site != null) {
settings = site.getCommonContentSettings();
}
}
if (settings == null) {
settings = page.getCmsTool().getCommonContentSettings();
}
if (page.isFormPost()) {
try {
Set<Content> defaultContents = findCascadedEditExistingContents(page.getSite(), null, settings);
Set<Content> oldUserContents = user.as(ToolUserCreateNewSettings.class).getEditExistingContents();
oldUserContents.removeAll(defaultContents);
List<String> selectedContentIds = page.params(String.class, user.getId().toString() + "/toolUserCreateNewSettings.editExistingContents");
if (!ObjectUtils.isBlank(selectedContentIds)) {
Set<Content> selectedContents = new HashSet<>(
Query.from(Content.class).where("_id = ?", selectedContentIds).and(page.siteItemsPredicate()).selectAll());
if (!selectedContents.equals(defaultContents)) {
oldUserContents.removeIf(content -> page.getSite() == null || (page.getSite() != null && Site.Static.isObjectAccessible(page.getSite(), content)));
oldUserContents.addAll(selectedContents);
}
}
} catch (Exception ex) {
page.getErrors().add(ex);
}
}
Set<ObjectType> createNewTypes = settings.getCreateNewTypes();
List<TypeTemplate> typeTemplates = new ArrayList<TypeTemplate>();
Map<ObjectType, Integer> typeCounts = new HashMap<ObjectType, Integer>();
if (!createNewTypes.isEmpty()) {
for (ObjectType type : createNewTypes) {
TypeTemplate typeTemplate = new TypeTemplate(type, null);
if (typeTemplate.getCollapsedId().equals(redirect)) {
page.redirect("/content/edit.jsp", "typeId", type.getId());
return;
}
typeTemplates.add(typeTemplate);
typeCounts.put(type, 1);
}
} else {
for (com.psddev.cms.db.Template template : Query
.from(com.psddev.cms.db.Template.class)
.where(page.siteItemsPredicate())
.sortAscending("name")
.selectAll()) {
for (ObjectType type : template.getContentTypes()) {
if (type.getGroups().contains(Singleton.class.getName())) {
continue;
}
TypeTemplate typeTemplate = new TypeTemplate(type, template);
if (typeTemplate.getCollapsedId().equals(redirect)) {
page.redirect("/content/edit.jsp", "typeId", type.getId(), "templateId", template.getId());
return;
}
typeTemplates.add(typeTemplate);
typeCounts.put(type, typeCounts.containsKey(type) ? typeCounts.get(type) + 1 : 1);
}
}
Predicate<ObjectType> filter = page.createTypeDisplayPredicate(ImmutableSet.of("write"));
for (ObjectType type : Database.Static.getDefault().getEnvironment().getTypes()) {
if (filter.test(type)
&& type.getGroups().contains(Directory.Item.class.getName())
&& !type.getGroups().contains(Singleton.class.getName())
&& !typeCounts.containsKey(type)) {
for (ObjectTypeOrContentTemplate otct : page.getObjectTypeOrContentTemplates(Collections.singleton(type), true)) {
TypeTemplate typeTemplate = new TypeTemplate(type, otct.getTemplate());
if (typeTemplate.getCollapsedId().equals(redirect)) {
page.redirect("/content/edit.jsp",
"typeId", typeTemplate.template instanceof ContentTemplate
? typeTemplate.template.getState().getId()
: type.getId());
return;
}
typeTemplates.add(typeTemplate);
typeCounts.put(type, 1);
}
}
}
}
Collections.sort(typeTemplates);
List<TypeTemplate> favorites = new ArrayList<TypeTemplate>();
List<TypeTemplate> collapsed = new ArrayList<TypeTemplate>();
if (page.isFormPost()) {
Set<String> collapsedIds = new HashSet<String>();
for (TypeTemplate typeTemplate : typeTemplates) {
if (page.param(boolean.class, typeTemplate.getParameterName())) {
favorites.add(typeTemplate);
} else {
collapsed.add(typeTemplate);
collapsedIds.add(typeTemplate.getCollapsedId());
}
}
user.as(ToolUserCreateNewSettings.class).setCollapsedIds(collapsedIds);
user.save();
} else {
Set<String> collapsedIds = user.as(ToolUserCreateNewSettings.class).getCollapsedIds();
if (collapsedIds == null) {
favorites = typeTemplates;
} else {
for (TypeTemplate typeTemplate : typeTemplates) {
if (collapsedIds.contains(typeTemplate.getCollapsedId())) {
collapsed.add(typeTemplate);
} else {
favorites.add(typeTemplate);
}
}
}
}
String widgetId = page.createId();
page.writeStart("div", "class", "widget p-commonContent", "id", widgetId);
page.writeStart("h1", "class", "icon icon-file");
page.writeHtml(page.localize(CreateNewWidget.class, "title"));
page.writeEnd();
if (page.param(boolean.class, "customize")) {
page.writeStart("form",
"method", "post",
"action", page.url(null));
page.writeStart("h2");
page.writeHtml("\"Create New\" Types");
page.writeEnd();
page.writeStart("table", "class", "table-striped");
page.writeStart("thead");
page.writeStart("tr");
page.writeStart("th").writeEnd();
page.writeStart("th", "class", "p-commonContent-favorite");
page.writeHtml(page.localize(CreateNewWidget.class, "label.favorite"));
page.writeEnd();
page.writeEnd();
page.writeEnd();
page.writeStart("tbody");
for (TypeTemplate typeTemplate : typeTemplates) {
page.writeStart("tr");
page.writeStart("td").writeHtml(typeTemplate.getLabel()).writeEnd();
page.writeStart("td", "class", "p-commonContent-favorite checkboxContainer");
page.writeElement("input",
"type", "checkbox",
"id", page.getId(),
"name", typeTemplate.getParameterName(),
"value", "true",
"checked", collapsed.contains(typeTemplate) ? null : "checked");
page.writeEnd();
page.writeEnd();
}
page.writeEnd();
page.writeEnd();
page.writeStart("h2");
page.writeHtml("\"Edit Existing\" Contents");
page.writeEnd();
page.include("/WEB-INF/errors.jsp");
user.as(ToolUserCreateNewSettings.class).setEditExistingContents(findCascadedEditExistingContents(page.getSite(), user, settings));
page.writeSomeFormFields(user, false, includeFields, null);
page.writeStart("div", "class", "actions");
page.writeStart("button",
"class", "action action-save");
page.writeHtml(page.localize(CreateNewWidget.class, "action.save"));
page.writeEnd();
page.writeStart("a",
"class", "action action-cancel action-pullRight",
"href", page.url(null));
page.writeHtml(page.localize(CreateNewWidget.class, "action.cancel"));
page.writeEnd();
page.writeEnd();
page.writeEnd();
} else {
page.writeStart("div", "class", "widgetControls");
page.writeStart("a",
"class", "action action-customize",
"href", page.url("", "customize", "true"));
page.writeHtml(page.localize(CreateNewWidget.class, "action.customize"));
page.writeEnd();
page.writeEnd();
Set<UUID> automaticallySavedDraftIds = user.getAutomaticallySavedDraftIds();
List<Object> automaticallySavedDrafts = Query
.from(Object.class)
.where("_id = ?", automaticallySavedDraftIds)
.selectAll();
if (!automaticallySavedDrafts.isEmpty()) {
boolean removed = false;
for (Iterator<Object> i = automaticallySavedDrafts.iterator(); i.hasNext();) {
State draft = State.getInstance(i.next());
if (!draft.as(Content.ObjectModification.class).isDraft()) {
removed = true;
automaticallySavedDraftIds.remove(draft.getId());
i.remove();
}
}
if (removed) {
user.save();
}
if (!automaticallySavedDrafts.isEmpty()) {
page.writeStart("h2");
page.writeHtml(page.localize(CreateNewWidget.class, "subtitle.savedDrafts"));
page.writeEnd();
page.writeStart("ul", "class", "links");
for (Object draft : automaticallySavedDrafts) {
page.writeStart("li");
page.writeStart("a",
"target", "_top",
"href", page.objectUrl("/content/edit.jsp", draft));
page.writeTypeObjectLabel(draft);
page.writeEnd();
page.writeEnd();
}
page.writeEnd();
}
}
page.writeStart("div", "class", "p-commonContent-new", "style", page.cssString(
"-moz-box-sizing", "border-box",
"-webkit-box-sizing", "border-box",
"box-sizing", "border-box",
"float", "left",
"padding-right", "5px",
"width", "50%"));
page.writeStart("h2");
page.writeHtml(page.localize(CreateNewWidget.class, "subtitle.createNew"));
page.writeEnd();
page.writeStart("ul", "class", "links pageThumbnails");
for (TypeTemplate typeTemplate : favorites) {
ObjectType type = typeTemplate.getType();
Recordable template = typeTemplate.getTemplate();
State state = State.getInstance(Query.fromType(type).where("cms.template.default = ?", template).first());
String permalink = null;
if (state != null) {
permalink = state.as(Directory.ObjectModification.class).getPermalink();
}
page.writeStart("li", "data-preview-url", permalink);
page.writeStart("a",
"target", "_top",
"href", page.url("/content/edit.jsp",
"typeId", template instanceof ContentTemplate ? template.getState().getId() : type.getId(),
"templateId", template != null && !(template instanceof ContentTemplate) ? template.getState().getId() : null));
page.writeHtml(typeTemplate.getLabel());
page.writeEnd();
page.writeEnd();
}
page.writeEnd();
if (!collapsed.isEmpty()) {
page.writeStart("form",
"method", "get",
"action", page.url(null),
"target", "_top");
page.writeStart("select", "name", "redirect");
for (TypeTemplate typeTemplate : collapsed) {
page.writeStart("option", "value", typeTemplate.getCollapsedId());
page.writeHtml(typeTemplate.getLabel());
page.writeEnd();
}
page.writeEnd();
page.writeHtml(" ");
page.writeStart("button", "class", "action action-create");
page.writeHtml(page.localize(CreateNewWidget.class, "action.new"));
page.writeEnd();
page.writeEnd();
}
page.writeEnd();
Set<Content> editExistingContents = findCascadedEditExistingContents(page.getSite(), user, settings);
if (!editExistingContents.isEmpty()) {
page.writeStart("div", "class", "p-commonContent-existing", "style", page.cssString(
"-moz-box-sizing", "border-box",
"-webkit-box-sizing", "border-box",
"box-sizing", "border-box",
"float", "left",
"padding-left", "5px",
"width", "50%"));
page.writeStart("h2");
page.writeHtml(page.localize(CreateNewWidget.class, "subtitle.editExisting"));
page.writeEnd();
page.writeStart("ul", "class", "links pageThumbnails");
for (Iterator<Content> i = editExistingContents.stream().sorted().iterator(); i.hasNext();) {
Content content = i.next();
page.writeStart("li");
page.writeStart("a",
"target", "_top",
"href", page.objectUrl("/content/edit.jsp", content));
page.writeTypeObjectLabel(content);
page.writeEnd();
page.writeEnd();
}
page.writeEnd();
page.writeEnd();
}
page.writeStart("div", "style", page.cssString(
"clear", "both"));
page.writeEnd();
}
page.writeEnd();
}
private Set<Content> findCascadedEditExistingContents(Site site, ToolUser user, CmsTool.CommonContentSettings settings) {
Set<Content> editExistingContents = new HashSet<>();
if (user != null) {
Set<Content> existingContentList = user.as(ToolUserCreateNewSettings.class).getEditExistingContents();
if (site != null) {
existingContentList = existingContentList.stream()
.filter(content -> Site.Static.isObjectAccessible(site, content))
.collect(Collectors.toSet());
}
editExistingContents.addAll(existingContentList);
}
if (editExistingContents.isEmpty()) {
editExistingContents.addAll(settings.getEditExistingContents());
}
if (editExistingContents.isEmpty()) {
for (Object item : Query
.from(Object.class)
.where("_type = ?", Database.Static.getDefault().getEnvironment().getTypesByGroup(Singleton.class.getName()))
.selectAll()) {
if (item instanceof Content) {
editExistingContents.add((Content) item);
}
}
}
return editExistingContents;
}
@FieldInternalNamePrefix("toolUserCreateNewSettings.")
private static class ToolUserCreateNewSettings extends Modification<ToolUser> {
@ToolUi.Hidden
private Set<String> collapsedIds;
@ToolUi.Tab("Dashboard")
private Set<Content> editExistingContents;
public Set<String> getCollapsedIds() {
if (collapsedIds == null) {
collapsedIds = new LinkedHashSet<>();
}
return collapsedIds;
}
public void setCollapsedIds(Set<String> collapsedIds) {
this.collapsedIds = collapsedIds;
}
public Set<Content> getEditExistingContents() {
if (editExistingContents == null) {
editExistingContents = new LinkedHashSet<>();
}
return editExistingContents;
}
public void setEditExistingContents(Set<Content> editExistingContents) {
this.editExistingContents = editExistingContents;
}
}
private static class TypeTemplate implements Comparable<TypeTemplate> {
private final ObjectType type;
private final Recordable template;
public TypeTemplate(ObjectType type, Recordable template) {
this.type = type;
this.template = template;
}
public ObjectType getType() {
return type;
}
public Recordable getTemplate() {
return template;
}
public String getParameterName() {
StringBuilder name = new StringBuilder();
name.append("favorite.");
name.append(type.getId());
name.append('.');
if (template != null) {
name.append(template.getState().getId());
}
return name.toString();
}
public String getCollapsedId() {
StringBuilder id = new StringBuilder();
id.append(type.getId());
id.append(',');
if (template != null) {
id.append(template.getState().getId());
}
return id.toString();
}
public String getLabel() {
ObjectType type = getType();
Recordable template = getTemplate();
if (template instanceof ContentTemplate) {
return ((ContentTemplate) template).getName();
} else {
StringBuilder label = new StringBuilder();
label.append(Localization.currentUserText(type, "displayName"));
if (template != null) {
label.append(" - ");
label.append(ToolPageContext.Static.getObjectLabel(template));
}
return label.toString();
}
}
@Override
public int compareTo(TypeTemplate other) {
return getLabel().compareTo(other.getLabel());
}
@Override
public int hashCode() {
return ObjectUtils.hashCode(type, template);
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
} else if (other instanceof TypeTemplate) {
TypeTemplate o = (TypeTemplate) other;
return type.equals(o.getType()) && ObjectUtils.equals(template, o.getTemplate());
} else {
return false;
}
}
}
}