/* * JBoss, Home of Professional Open Source. * Copyright 2012, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This 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 2.1 of * the License, or (at your option) any later version. * * This software 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 this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.gatein.api.management; import org.gatein.api.common.i18n.LocalizedString; import org.gatein.api.navigation.Navigation; import org.gatein.api.navigation.Node; import org.gatein.api.navigation.NodePath; import org.gatein.api.navigation.NodeVisitor; import org.gatein.api.navigation.Nodes; import org.gatein.api.navigation.PublicationDate; import org.gatein.api.navigation.Visibility; import org.gatein.api.page.PageId; import org.gatein.api.site.SiteId; import org.gatein.api.site.SiteType; import org.gatein.management.api.PathAddress; import org.gatein.management.api.annotations.Managed; import org.gatein.management.api.annotations.ManagedContext; import org.gatein.management.api.annotations.ManagedOperation; import org.gatein.management.api.annotations.ManagedRole; import org.gatein.management.api.annotations.MappedAttribute; import org.gatein.management.api.annotations.MappedPath; import org.gatein.management.api.model.Model; import org.gatein.management.api.model.ModelList; import org.gatein.management.api.model.ModelNumber; import org.gatein.management.api.model.ModelObject; import org.gatein.management.api.model.ModelProvider; import org.gatein.management.api.model.ModelReference; import org.gatein.management.api.model.ModelString; import org.gatein.management.api.model.ModelValue; import org.gatein.management.api.operation.OperationContext; import org.gatein.management.api.operation.OperationNames; import java.util.Date; import java.util.Locale; import static org.gatein.api.management.GateInApiManagementResource.*; import static org.gatein.api.management.Utils.*; /** * @author <a href="mailto:nscavell@redhat.com">Nick Scavelli</a> */ @Managed @SuppressWarnings("unused") public class NavigationManagementResource { private final Navigation navigation; private final ModelProvider modelProvider; public NavigationManagementResource(Navigation navigation, ModelProvider modelProvider) { this.navigation = navigation; this.modelProvider = modelProvider; } @Managed public ModelObject getNavigation(@ManagedContext OperationContext context, @MappedAttribute("scope") String scopeAttribute, @MappedAttribute("showAll") String showAllAttribute) { NodeVisitor visitor = Nodes.visitChildren(); int scope = 0; if (scopeAttribute != null) { scope = Integer.parseInt(scopeAttribute); visitor = Nodes.visitNodes(scope); } Node node = getNode(NodePath.root(), true, visitor); boolean showAll = showAllAttribute != null && Boolean.parseBoolean(showAllAttribute); if (!showAll || !context.getExternalContext().isUserInRole("administrators")) { node = node.filter().showDefault(); } return populateNavigationModel(node, scope, context); } @Managed @ManagedOperation(name = OperationNames.UPDATE_RESOURCE, description = "Updates the navigation") public ModelObject updateNavigation(@ManagedContext OperationContext context, @ManagedContext ModelObject navModel) { ModelNumber priority = get(navModel, ModelNumber.class, "priority"); if (priority.isDefined()) { navigation.setPriority(priority.getInt()); } Node node = getNode(NodePath.root(), true, Nodes.visitChildren()); return populateNavigationModel(node, 0, context); } @Managed("{path: .*}") public ModelObject getNode(@MappedPath("path") String path, @MappedAttribute("scope") String scopeAttribute, @MappedAttribute("showAll") String showAllAttribute, @ManagedContext OperationContext context) { NodeVisitor visitor = Nodes.visitChildren(); int scope = 0; if (scopeAttribute != null) { scope = Integer.parseInt(scopeAttribute); visitor = Nodes.visitNodes(scope); } Node node = getNode(path, true, visitor); boolean showAll = showAllAttribute != null && Boolean.parseBoolean(showAllAttribute); if (showAll && context.getExternalContext().isUserInRole("administrators")) { node = node.filter().showDefault(); } else { node = node.filter().showDefault(); } // Populate the model ModelObject model = modelProvider.newModel(ModelObject.class); populateNode(node, scope, model, context.getAddress()); return model; } @Managed("{path: .*}") @ManagedRole("administrators") @ManagedOperation(name = OperationNames.REMOVE_RESOURCE, description = "Removes the navigation node") public void removeNode(@MappedPath("path") String path) { Node node = getNode(path, true); Node parent = node.getParent(); parent.removeChild(node.getName()); navigation.saveNode(parent); } @Managed("{path: .*}") @ManagedRole("administrators") @ManagedOperation(name = OperationNames.ADD_RESOURCE, description = "Adds the navigation node") public ModelObject addNode(@MappedPath("path") String path, @ManagedContext PathAddress address) { NodePath nodePath = NodePath.fromString(path); Node parent = getNode(nodePath.parent(), true, Nodes.visitChildren()); String name = nodePath.getLastSegment(); if (parent.hasChild(name)) { throw alreadyExists("Cannot add node", navigation.getSiteId(), nodePath); } // Add child and save Node child = parent.addChild(name); navigation.saveNode(parent); // Populate model ModelObject model = modelProvider.newModel(ModelObject.class); populateNode(child, 0, model, address); return model; } @Managed("{path: .*}") @ManagedRole("administrators") @ManagedOperation(name = OperationNames.UPDATE_RESOURCE, description = "Updates the navigation node") public ModelObject updateNode(@MappedPath("path") String path, @ManagedContext ModelObject nodeModel, @ManagedContext PathAddress address) { // Update node from model Node node = getNode(path, true); updateNodeFromModel(node, nodeModel); // Save node navigation.saveNode(node); // Populate model for update node ModelObject updateNodeModel = modelProvider.newModel(ModelObject.class); populateNode(node, 0, updateNodeModel, address); return updateNodeModel; } private Node getNode(String pathString, boolean require) { return getNode(pathString, require, Nodes.visitNone()); } private Node getNode(String pathString, boolean require, NodeVisitor visitor) { return getNode(NodePath.fromString(pathString), require, visitor); } private Node getNode(NodePath path, boolean require) { return getNode(path, require, Nodes.visitNone()); } private Node getNode(NodePath path, boolean require, NodeVisitor visitor) { Node node = navigation.getNode(path, visitor); if (node == null && require) throw notFound("Cannot retrieve node", navigation.getSiteId(), path); return node; } private ModelObject populateNavigationModel(Node rootNode, int scope, OperationContext context) { ModelObject model = modelProvider.newModel(ModelObject.class); PathAddress address = context.getAddress(); // Populate navigation fields model.set("priority", navigation.getPriority()); model.set("siteType", navigation.getSiteId().getType().getName()); model.set("siteName", navigation.getSiteId().getName()); ModelList nodesModel = model.get("nodes").setEmptyList(); if (rootNode.isChildrenLoaded()) { for (Node child : rootNode) { Model childModel = nodesModel.add(); PathAddress childAddress = address.append(child.getName()); if (scope > 0 || scope < 0) // Continue populating nodes in response { populateNode(child, scope - 1, childModel.setEmptyObject(), childAddress); } else { // Populate node reference which can be followed ModelReference nodeRef = childModel.set(childAddress); nodeRef.set("name", child.getName()); } } } return model; } private void populateNode(Node node, int scope, ModelObject model, PathAddress address) { model.set("name", node.getName()); set("uri", node.getURI(), model); model.set("isVisible", node.isVisible()); populateVisibility(node.getVisibility(), model.get("visibility", ModelObject.class)); model.set("iconName", node.getIconName()); // Display name model.set("displayName", node.getDisplayName()); populate("displayNames", node.getDisplayNames(), model); // Children nodes ModelList children = model.get("children", ModelList.class); if (node.isChildrenLoaded()) { for (Node child : node) { Model childModel = children.add(); PathAddress childAddress = address.append(child.getName()); if (scope > 0 || scope < 0) // Continue populating nodes in response { populateNode(child, scope - 1, childModel.setEmptyObject(), childAddress); } else { // Populate node reference which can be followed ModelReference nodeRef = childModel.set(childAddress); nodeRef.set("name", child.getName()); } } } // Page reference ModelReference pageRef = model.get("page").asValue(ModelReference.class); if (node.getPageId() != null) { PageId pageId = node.getPageId(); pageRef.set("pageName", pageId.getPageName()); pageRef.set("siteName", pageId.getSiteId().getName()); pageRef.set("siteType", pageId.getSiteId().getType().getName()); // Set the address for the ref PathAddress pageAddress = getPagesAddress(pageId.getSiteId()).append(pageId.getPageName()); pageRef.set(pageAddress); } } private void populateVisibility(Visibility visibility, ModelObject model) { if (visibility != null) { set("status", visibility.getStatus(), model); if (visibility.getPublicationDate() != null) { ModelObject pubDateModel = model.get("publication-date", ModelObject.class); Date start = visibility.getPublicationDate().getStart(); Date end = visibility.getPublicationDate().getEnd(); set("start", start, pubDateModel); set("end", end, pubDateModel); } } } private void updateNodeFromModel(Node node, ModelObject nodeModel) { // Update name if (nodeModel.has("name")) { node.setName(nonNullString(nodeModel, "name")); } // Update visibility if (nodeModel.has("visibility")) { Visibility visibility = getVisibility(nodeModel, node.getVisibility()); if (visibility != null) { node.setVisibility(visibility); } } // Update iconName if (nodeModel.has("iconName")) { String iconName = get(nodeModel, ModelString.class, "iconName").getValue(); node.setIconName(iconName); } // Update pageId if (nodeModel.has("page")) { node.setPageId(getPageId(nodeModel)); } //TODO: Support adding and not just overwriting. i.e. one locale/value pair is added to rest. if (nodeModel.has("displayNames")) { LocalizedString displayName = getDisplayNames(nodeModel); node.setDisplayNames(displayName); } else if (nodeModel.has("displayName")) { String displayName = get(nodeModel, ModelString.class, "displayName").getValue(); if (displayName == null) { throw invalidValue(null, "displayName"); } node.setDisplayName(displayName); } } private static LocalizedString getDisplayNames(ModelObject nodeModel) { ModelList list = get(nodeModel, ModelList.class, "displayNames"); if (!list.isDefined()) { throw invalidValue(null, "displayNames"); } LocalizedString displayName = null; int i=0; for (ModelValue mv : list) { ModelObject displayNameModel = mv.asValue(ModelObject.class); // Parse value (required && non-null) if (!displayNameModel.has("value")) { throw requiredField("displayNames["+i+"].value"); } String value = get(displayNameModel, ModelString.class, "value").getValue(); if (value == null) { throw invalidValue(value, "displayNames[" + i + "].value"); } // Parse lang (not-required but if defined must be non-null) if (displayNameModel.has("lang")) { ModelString langModel = get(displayNameModel, ModelString.class, "lang"); String lang = langModel.getValue(); if (lang == null) { throw invalidValue(lang, "displayNames[" + i + "].lang"); } Locale locale = getLocale(displayNameModel, "lang"); if (displayName == null) { displayName = new LocalizedString(locale, value); } else { displayName.setLocalizedValue(locale, value); } } else if (displayName == null) { displayName = new LocalizedString(value); } else { throw invalidData("Cannot have multiple non localized values for displayNames"); } } return displayName; } private static PageId getPageId(ModelObject nodeModel) { ModelObject pageModel = get(nodeModel, ModelObject.class, "page"); if (pageModel.isDefined()) { String pageName = nonNullString(nodeModel, "page", "pageName"); String siteName = nonNullString(nodeModel, "page", "siteName"); String siteTypeString = nonNullString(nodeModel, "page", "siteType"); SiteType siteType = SiteType.forName(siteTypeString); if (siteType == null) { throw invalidValue(siteTypeString, "page", "siteType"); } return new PageId(new SiteId(siteType, siteName), pageName); } else { return null; } } private static Visibility getVisibility(ModelObject nodeModel, Visibility original) { Visibility.Status status = getStatus(nodeModel); if (status == Visibility.Status.PUBLICATION) { ModelObject pubDateModel = get(nodeModel, ModelObject.class, "visibility", "publication-date"); // If status was set to PUBLICATION however no publication date was specified then throw exception if (!pubDateModel.isDefined()) { throw requiredFieldWhen("visibility status is " + status, "visibility", "publication-date"); } PublicationDate publicationDate = getPublicationDate(nodeModel, original.getPublicationDate()); return new Visibility(publicationDate); } else { return new Visibility(status); } } private static Visibility.Status getStatus(ModelObject nodeModel) { String statusString = nonNullString(nodeModel, "visibility", "status"); Visibility.Status status; try { status = Visibility.Status.valueOf(statusString.toUpperCase()); } catch (IllegalArgumentException e) { throw invalidValue(statusString, "visibility", "status"); } return status; } private static PublicationDate getPublicationDate(ModelObject nodeModel, PublicationDate previous) { ModelString startModel = get(nodeModel, ModelString.class, "visibility", "publication-date", "start"); Date start = (previous == null) ? null : previous.getStart(); if (startModel.isDefined()) { start = getDate(nodeModel, "visibility", "publication-date", "start"); } ModelString endModel = get(nodeModel, ModelString.class, "visibility", "publication-date", "end"); Date end = (previous == null) ? null : previous.getEnd(); if (endModel.isDefined()) { end = getDate(nodeModel, "visibility", "publication-date", "end"); } if (start != null && end != null) { return PublicationDate.between(start, end); } else if (start == null && end != null) { return PublicationDate.endingOn(end); } else if (start != null) { return PublicationDate.startingOn(start); } else { throw invalidData("Either 'start' or 'end' is required for visibility.publication-date"); } } }