package com.psddev.cms.tool.content; import com.psddev.cms.db.Content; import com.psddev.cms.db.Directory; import com.psddev.cms.db.Localization; import com.psddev.cms.db.Site; import com.psddev.cms.db.ToolUser; import com.psddev.cms.db.Workflow; import com.psddev.cms.tool.CmsTool; import com.psddev.cms.tool.ContentEditWidgetPlacement; import com.psddev.cms.tool.ToolPageContext; import com.psddev.cms.tool.UpdatingContentEditWidget; import com.psddev.dari.db.Query; import com.psddev.dari.db.State; import com.psddev.dari.util.CompactMap; import com.psddev.dari.util.ObjectUtils; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; public class UrlsWidget extends UpdatingContentEditWidget { @Override public ContentEditWidgetPlacement getPlacement(ToolPageContext page, Object content) { return ContentEditWidgetPlacement.RIGHT; } @Override public double getPosition(ToolPageContext page, Object content, ContentEditWidgetPlacement placement) { return -30.0; } @Override public String getHeading(ToolPageContext page, Object content) { return Localization.currentUserText(getClass(), "title"); } @Override @SuppressWarnings("deprecation") public Collection<Class<? extends UpdatingContentEditWidget>> getUpdateDependencies() { return Collections.singleton(TemplateWidget.class); } @Override public void displayOrUpdate(ToolPageContext page, Object content, ContentEditWidgetPlacement placement) throws IOException { Object original = page.getRequest().getAttribute("original"); if (original == null) { original = content; } Site site = page.getSite(); State state = State.getInstance(original); String namePrefix = state.getId() + "/directory."; String automaticName = namePrefix + "automatic"; String automaticContainerId = page.createId(); String pathName = namePrefix + "path"; String removeName = namePrefix + "remove"; String typeName = namePrefix + "type"; String siteIdName = namePrefix + "siteId"; Directory.Data dirData = state.as(Directory.Data.class); Map<UUID, Site> sites = new CompactMap<>(); ToolUser user = page.getUser(); Set<Site> userSites = new HashSet<>(); if (user != null) { userSites.addAll(user.findOtherAccessibleSites()); userSites.add(user.getCurrentSite()); } for (Site s : Query .from(Site.class) .where("name != missing") .sortAscending("name") .selectAll()) { if (userSites.contains(s)) { sites.put(s.getId(), s); } } CmsTool cms = page.getCmsTool(); if (cms.isAlwaysGeneratePermalinks()) { if (placement == null) { dirData.setPathsMode(page.param(boolean.class, automaticName) ? null : Directory.PathsMode.MANUAL); dirData.clearPaths(); List<String> paths = page.params(String.class, pathName); List<UUID> siteIds = page.params(UUID.class, siteIdName); List<Directory.PathType> types = page.params(Directory.PathType.class, typeName); for (int i = 0, size = Math.min(paths.size(), types.size()); i < size; i ++) { if (!page.param(boolean.class, removeName + "." + i)) { dirData.addPath(i < siteIds.size() ? sites.get(siteIds.get(i)) : null, paths.get(i), types.get(i)); } } // Automatically generate URLs if requested. if (!Directory.PathsMode.MANUAL.equals(dirData.getPathsMode())) { Set<Directory.Path> oldPaths = new LinkedHashSet<>(dirData.getPaths()); for (Directory.Path path : State.getInstance(content).as(Directory.ObjectModification.class).createPaths(site)) { dirData.addPath(path.getSite(), path.getPath(), path.getType()); } Set<Directory.Path> newPaths = new LinkedHashSet<>(dirData.getPaths()); newPaths.removeAll(oldPaths); state.getExtras().put("cms.newPaths", newPaths); } return; } writeErrors(page, state); writeAutomaticContainer(page, automaticName, dirData, automaticContainerId); Set<Directory.Path> paths = dirData.getPaths(); if (!paths.isEmpty() && !state.isNew() && !state.as(Content.ObjectModification.class).isDraft() && (Directory.PathsMode.MANUAL.equals(dirData.getPathsMode()) || !page.getCmsTool().isSingleGeneratedPermalink() || State.getInstance(content).as(Directory.ObjectModification.class).createPaths(site).isEmpty())) { writePaths(page, paths, sites, pathName, removeName, siteIdName, typeName); } } else { boolean initialDraft = state.isNew() || state.as(Content.ObjectModification.class).isDraft() || state.as(Workflow.Data.class).getCurrentState() != null; if (placement == null) { dirData.setPathsMode(page.param(boolean.class, automaticName) ? null : Directory.PathsMode.MANUAL); Set<Directory.Path> viewOnlyPaths = !ObjectUtils.isBlank(dirData.getPaths()) ? dirData.getPaths() .stream() .filter(path -> (path.getSite() == null && !user.hasPermission("site/global")) || (path.getSite() != null && !user.hasPermission(path.getSite().getPermissionId()))) .collect(Collectors.toSet()) : null; dirData.clearPaths(); if (!ObjectUtils.isBlank(viewOnlyPaths)) { for (Directory.Path path : viewOnlyPaths) { dirData.addPath(path.getSite(), path.getPath(), path.getType()); } } List<String> paths = page.params(String.class, pathName); List<UUID> siteIds = page.params(UUID.class, siteIdName); List<Directory.PathType> types = page.params(Directory.PathType.class, typeName); for (int i = 0, size = Math.min(paths.size(), types.size()); i < size; i ++) { if (!page.param(boolean.class, removeName + "." + i)) { Site removeSite = i < siteIds.size() ? sites.get(siteIds.get(i)) : null; if ((removeSite != null && user.hasPermission(removeSite.getPermissionId())) || (removeSite == null && user.hasPermission("site/global"))) { dirData.addPath(i < siteIds.size() ? sites.get(siteIds.get(i)) : null, paths.get(i), types.get(i)); } } } // Automatically generate URLs if requested. if (initialDraft) { if (!Directory.PathsMode.MANUAL.equals(dirData.getPathsMode())) { Set<Directory.Path> oldPaths = new LinkedHashSet<>(dirData.getPaths()); Set<String> oldRawPaths = new LinkedHashSet<>(dirData.getRawPaths()); dirData.clearPaths(); for (Directory.Path path : State.getInstance(content).as(Directory.ObjectModification.class).createPaths(site)) { dirData.addPath(path.getSite(), path.getPath(), path.getType()); } Set<Directory.Path> newPaths = new LinkedHashSet<>(dirData.getPaths()); Set<String> newRawPaths = new LinkedHashSet<>(dirData.getRawPaths()); dirData.clearPaths(); Stream.concat(oldPaths.stream(), newPaths.stream()) .forEach(p -> dirData.addPath(p.getSite(), p.getPath(), p.getType())); state.getExtras().put("cms.newPaths", newPaths); dirData.setAutomaticRawPaths(newRawPaths); } else { dirData.setAutomaticRawPaths(null); } } else { dirData.setPathsMode(Directory.PathsMode.MANUAL); dirData.setAutomaticRawPaths(null); } return; } writeErrors(page, state); if (initialDraft) { writeAutomaticContainer(page, automaticName, dirData, automaticContainerId); } else if (!Directory.PathsMode.MANUAL.equals(dirData.getPathsMode())) { page.writeElement("input", "type", "hidden", "name", automaticName, "value", true); } Set<Directory.Path> paths = initialDraft ? dirData.getManualPaths() : dirData.getPaths(); if (!paths.isEmpty()) { writePaths(page, paths, sites, pathName, removeName, siteIdName, typeName); } } page.writeStart("div", "class", "widget-urlsRepeatable repeatableInputs"); { page.writeStart("ul"); { page.writeStart("script", "type", "text/template"); { page.writeStart("li", "class", "widget-urlsItem", "data-type", "URL"); { page.writeStart("textarea", "class", "widget-urlsItemLabel", "name", pathName); page.writeEnd(); if (!sites.isEmpty()) { page.writeStart("select", "name", siteIdName); { if (user != null && user.hasPermission("site/global")) { page.writeStart("option", "value", ""); page.writeHtml("Global"); page.writeEnd(); } for (Site s : sites.values()) { page.writeStart("option", "value", s.getId(), "selected", s.equals(site) ? "selected" : null); page.writeObjectLabel(s); page.writeEnd(); } } page.writeEnd(); page.writeHtml(" "); } page.writeStart("select", "name", typeName); { for (Directory.PathType pathType : Directory.PathType.values()) { page.writeStart("option", "value", pathType.name()); page.writeHtml(pathType); page.writeEnd(); } } page.writeEnd(); } page.writeEnd(); } page.writeEnd(); } page.writeEnd(); } page.writeEnd(); page.writeStart("script", "type", "text/javascript"); { page.writeRaw("(function($, window, undefined) {"); { page.writeRaw("var $automaticContainer = $('#").writeRaw(automaticContainerId).writeRaw("'),"); page.writeRaw("$form = $automaticContainer.closest('form');"); page.writeRaw("$form.bind('cms-updateContentState', function(event, data) {"); page.writeRaw("$automaticContainer.html(data._urlWidgetHtml || '');"); page.writeRaw("});"); } page.writeRaw("})(jQuery, window);"); } page.writeEnd(); } private void writeErrors(ToolPageContext page, State state) throws IOException { List<String> errors = state.getErrors(state.getField(Directory.PATHS_FIELD)); if (!ObjectUtils.isBlank(errors)) { page.writeStart("div", "class", "message message-error"); { for (String error : errors) { page.writeHtml(error); } } page.writeEnd(); } } private void writeAutomaticContainer(ToolPageContext page, String automaticName, Directory.Data dirData, String automaticContainerId) throws IOException { page.writeStart("div", "class", "widget-urlsAutomatic"); { page.writeStart("label"); { page.writeElement("input", "type", "checkbox", "name", automaticName, "value", "true", "checked", Directory.PathsMode.MANUAL.equals(dirData.getPathsMode()) ? null : "checked"); page.writeHtml(" Generate Permalink?"); } page.writeEnd(); page.writeStart("div", "id", automaticContainerId); page.writeEnd(); } page.writeEnd(); } private void writePaths(ToolPageContext page, Set<Directory.Path> paths, Map<UUID, Site> sites, String pathName, String removeName, String siteIdName, String typeName) throws IOException { ToolUser user = page.getUser(); int index = 0; page.writeStart("ul", "class", "widget-urls"); { for (Directory.Path path : paths) { Site pathSite = path.getSite(); String pathPath = path.getPath(); String pathDisplay = ObjectUtils.firstNonNull(Directory.extractExternalUrl(pathPath), pathPath); String href = pathSite != null ? pathSite.getPrimaryUrl() + pathPath : pathPath; while (href.endsWith("*")) { href = href.substring(0, href.length() - 1); } if (user != null && ((path.getSite() == null && !user.hasPermission("site/global")) || (path.getSite() != null && !user.hasPermission(path.getSite().getPermissionId())))) { page.writeStart("li", "class", "widget-urlsItem"); { page.writeStart("div", "class", "widget-urlsItemLabel"); { page.writeStart("a", "href", href, "target", "_blank"); page.writeHtml(pathDisplay); page.writeEnd(); } page.writeEnd(); page.writeStart("label"); { if (path.getSite() == null) { page.writeHtml("Global"); } else { page.writeObjectLabel(path.getSite()); } if (path.getType() != null) { page.writeHtml(": "); page.writeHtml(path.getType()); } } page.writeEnd(); } page.writeEnd(); } else { page.writeStart("li", "class", "widget-urlsItem"); { page.writeElement("input", "type", "hidden", "id", page.createId(), "name", pathName, "value", pathPath); page.writeStart("div", "class", "widget-urlsItemLabel"); { page.writeStart("a", "href", href, "target", "_blank"); page.writeHtml(pathDisplay); page.writeEnd(); page.writeStart("label", "class", "widget-urlsItemRemove"); { page.writeHtml(" "); page.writeElement("input", "type", "checkbox", "name", removeName + "." + index, "value", "true"); page.writeStart("span", "class", "widget-urlsItemRemoveText"); page.writeHtml("Remove"); page.writeEnd(); } page.writeEnd(); } page.writeEnd(); if (!sites.isEmpty()) { page.writeStart("select", "name", siteIdName); { if (user != null && user.hasPermission("site/global")) { page.writeStart("option", "value", ""); page.writeHtml("Global"); page.writeEnd(); } for (Site s : sites.values()) { page.writeStart("option", "selected", s.equals(path.getSite()) ? "selected" : null, "value", s.getId()); page.writeObjectLabel(s); page.writeEnd(); } } page.writeEnd(); page.writeHtml(" "); } page.writeStart("select", "name", typeName); { for (Directory.PathType pathType : Directory.PathType.values()) { page.writeStart("option", "selected", pathType.equals(path.getType()) ? "selected" : null, "value", pathType.name()); page.writeHtml(pathType); page.writeEnd(); } } page.writeEnd(); } page.writeEnd(); ++index; } } } page.writeEnd(); } }