/** * Copyright © 2014 Instituto Superior Técnico * * This file is part of FenixEdu CMS. * * FenixEdu CMS is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * FenixEdu CMS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with FenixEdu CMS. If not, see <http://www.gnu.org/licenses/>. */ package org.fenixedu.cms.ui; import static java.util.Optional.ofNullable; import static org.fenixedu.cms.domain.PermissionEvaluation.canDoThis; import static org.fenixedu.cms.domain.PermissionEvaluation.ensureCanDoThis; import static org.fenixedu.cms.domain.PermissionsArray.Permission.EDIT_ADVANCED_PAGES; import java.util.HashSet; import java.util.Optional; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Stream; import org.fenixedu.bennu.signals.DomainObjectEvent; import org.fenixedu.bennu.signals.Signal; import org.fenixedu.cms.domain.Menu; import org.fenixedu.cms.domain.MenuItem; import org.fenixedu.cms.domain.Page; import org.fenixedu.cms.domain.PermissionsArray.Permission; import org.fenixedu.cms.domain.Site; import org.fenixedu.cms.domain.component.StaticPost; import org.fenixedu.commons.i18n.LocalizedString; import org.springframework.stereotype.Service; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import pt.ist.fenixframework.Atomic; /** * Created by borgez on 03-08-2015. */ @Service public class AdminMenusService { private final Predicate<MenuItem> isStaticPageOrHasPermissions = menuItem -> menuItem.getPage() != null && (canDoThis(menuItem.getPage() .getSite(),EDIT_ADVANCED_PAGES) || menuItem.getPage().isStaticPage()); @Atomic public Menu createMenu(Site site, LocalizedString name) { return new Menu(site, name); } public JsonObject serializeMenu(Menu menu) { JsonObject menuJson = new JsonObject(); menuJson.addProperty("title", Optional.ofNullable(menu.getName()) .map(LocalizedString::getContent).orElse(menu.getSite().getName().getContent())); menuJson.add("name", menu.getName().json()); menuJson.addProperty("key", menu.getSlug()); menuJson.addProperty("root", true); menuJson.addProperty("folder", true); menuJson.addProperty("privileged", menu.getPrivileged()); JsonArray child = new JsonArray(); menu.getToplevelItemsSorted().map(this::serializeMenuItem).forEach(json -> child.add(json)); menuJson.add("children", child); return menuJson; } private JsonObject serializeMenuItem(MenuItem item) { JsonObject root = new JsonObject(); root.add("title", new JsonPrimitive(item.getName().isEmpty() ? "---" : item.getName().getContent())); root.add("name", item.getName().json()); root.addProperty("key", item.getExternalId()); root.addProperty("url", item.getUrl()); root.addProperty("page", ofNullable(item.getPage()).map(Page::getSlug).orElse(null)); root.addProperty("position", item.getPosition()); root.addProperty("isFolder", ofNullable(item.getFolder()).orElse(false)); if (item.getChildrenSet().size() > 0) { JsonArray children = new JsonArray(); item.getChildrenSorted().stream().map(this::serializeMenuItem).forEach(children::add); root.add("children", children); root.addProperty("folder", true); } if(item.getPage() != null) { root.addProperty("use", "page"); } else if(ofNullable(item.getFolder()).orElse(false)) { root.addProperty("use", "folder"); root.addProperty("folder", true); } else { root.addProperty("use", "url"); } return root; } public JsonObject serializePage(Page page) { JsonObject pageJson = new JsonObject(); pageJson.addProperty("slug", page.getSlug()); pageJson.addProperty("address", page.getAddress()); pageJson.addProperty("editUrl", page.getEditUrl()); pageJson.add("name", page.getName().json()); return pageJson; } Optional<MenuItem> menuItemForOid(Site site, String menuItemOid) { return site.getMenusSet().stream().map(menu->menu.menuItemForOid(menuItemOid)).filter(mi->mi!=null).findFirst(); } void processMenuChanges(Menu menu, JsonObject menuJson) { ensureCanDoThis(menu.getSite(), Permission.EDIT_MENU); if(menu.getPrivileged()) { ensureCanDoThis(menu.getSite(), Permission.EDIT_PRIVILEGED_MENU); } if(menuJson.get("privileged") != null) { boolean newIsPrivileged = menuJson.get("privileged").getAsBoolean(); if(newIsPrivileged != menu.getPrivileged()) { ensureCanDoThis(menu.getSite(), Permission.EDIT_PRIVILEGED_MENU); if(newIsPrivileged) { ensureCanDoThis(menu.getSite(), Permission.CREATE_PRIVILEGED_MENU); } else { ensureCanDoThis(menu.getSite(), Permission.DELETE_PRIVILEGED_MENU); } menu.setPrivileged(newIsPrivileged); } } LocalizedString newName = LocalizedString.fromJson(menuJson.get("name")); if(!menu.getName().equals(newName)) { menu.setName(newName); } allDeletedItems(menu, menuJson).forEach(MenuItem::delete); if(menuJson.get("children")!=null && menuJson.get("children").isJsonArray()) { JsonArray children = menuJson.get("children").getAsJsonArray(); for (int newPosition = 0; newPosition < children.size(); ++newPosition) { processMenuItemChanges(menu, null, children.get(newPosition).getAsJsonObject(), newPosition); } } Signal.emit(Menu.SIGNAL_EDITED, new DomainObjectEvent<>(menu)); } void processMenuItemChanges(Menu menu, MenuItem parent, JsonObject menuItemJson, int newPosition) { LocalizedString newName = LocalizedString.fromJson(menuItemJson.get("name")); String key = menuItemJson.get("key").getAsString(); MenuItem menuItem = menuItemForOid(menu.getSite(), key).orElseGet(()-> { ensureCanDoThis(menu.getSite(), Permission.CREATE_MENU_ITEM); return new MenuItem(menu); }); menuItem.setName(newName); if (parent != null) { if(parent.getMenu().getPrivileged()){ ensureCanDoThis(parent.getMenu().getSite(),Permission.EDIT_PRIVILEGED_MENU); } parent.putAt(menuItem, newPosition); } else { if(menu.getPrivileged()){ ensureCanDoThis(menu.getSite(),Permission.EDIT_PRIVILEGED_MENU); } menu.putAt(menuItem, newPosition); } switch (menuItemJson.get("use").getAsString()) { case "page": setMenuItemPage(menuItem, menuItemJson); break; case "url": setMenuItemUrl(menuItem, menuItemJson); break; default: setMenuItemFolder(menuItem, menuItemJson); break; } if (menuItemJson.get("children") != null && menuItemJson.get("children").isJsonArray()) { JsonArray children = menuItemJson.get("children").getAsJsonArray(); for (int newChildrenPosition = 0; newChildrenPosition < children.size(); ++newChildrenPosition) { processMenuItemChanges(menu, menuItem, children.get(newChildrenPosition).getAsJsonObject(), newChildrenPosition); } } Signal.emit(MenuItem.SIGNAL_EDITED, new DomainObjectEvent<>(menuItem)); } void deleteMenuItem(MenuItem menuItem){ Site site= menuItem.getMenu().getSite(); ensureCanDoThis(site, Permission.DELETE_MENU_ITEM); if(menuItem.getMenu().getPrivileged()) { ensureCanDoThis(site, Permission.EDIT_PRIVILEGED_MENU); } menuItem.delete(); } private void setMenuItemFolder(MenuItem menuItem, JsonObject menuItemJson) { if(!menuItem.getFolder()) { menuItem.setFolder(true); menuItem.setUrl(null); menuItem.setPage(null); } } private void setMenuItemUrl(MenuItem menuItem, JsonObject menuItemJson) { String newUrl = Optional.ofNullable(menuItemJson).map(json->json.get("url")) .filter(JsonElement::isJsonPrimitive).map(JsonElement::getAsString).orElse("#"); if(!newUrl.equals(menuItem.getUrl())) { menuItem.setFolder(false); menuItem.setPage(null); menuItem.setUrl(newUrl); } } private void setMenuItemPage(MenuItem menuItem, JsonObject menuItemJson) { Site site = menuItem.getMenu().getSite(); Optional.ofNullable(menuItemJson).map(json -> json.get("page")) .filter(JsonElement::isJsonPrimitive).map(JsonElement::getAsString) .map(pageSlug->site.pageForSlug(pageSlug)).filter(page -> page != menuItem.getPage()) .ifPresent(page -> { menuItem.setPage(page); menuItem.setFolder(false); menuItem.setUrl(null); }); } private Set<String> allJsonKeys(JsonObject menuItemJson) { Set<String> keys = new HashSet<>(); if(menuItemJson.get("children") != null && menuItemJson.get("children").isJsonArray()) { for(JsonElement el : menuItemJson.get("children").getAsJsonArray()) { keys.add(el.getAsJsonObject().get("key").getAsString()); keys.addAll(allJsonKeys(el.getAsJsonObject())); } } return keys; } public Stream<MenuItem> allDeletedItems(Menu menu, JsonObject rootJson) { Set<String> allJsonKeys = allJsonKeys(rootJson); return menu.getItemsSet().stream().filter(mi->!allJsonKeys.contains(mi.getExternalId())); } }