/** * Licensed to Apereo under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright ownership. Apereo * licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use * this file except in compliance with the License. You may obtain a copy of the License at the * following location: * * <p>http://www.apache.org/licenses/LICENSE-2.0 * * <p>Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ package org.apereo.portal.layout.dlm.remoting; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.xml.xpath.XPathConstants; import org.apache.commons.lang3.Validate; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apereo.portal.AuthorizationException; import org.apereo.portal.layout.IUserLayoutStore; import org.apereo.portal.layout.dlm.ConfigurationLoader; import org.apereo.portal.layout.dlm.Evaluator; import org.apereo.portal.layout.dlm.FragmentDefinition; import org.apereo.portal.portlet.om.IPortletDefinition; import org.apereo.portal.portlet.registry.IPortletDefinitionRegistry; import org.apereo.portal.security.AdminEvaluator; import org.apereo.portal.security.IPerson; import org.apereo.portal.security.IPersonManager; import org.apereo.portal.xml.xpath.XPathOperations; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.ModelAndView; import org.w3c.dom.Document; import org.w3c.dom.NodeList; /** * Spring controller that returns a JSON representation of DLM fragments in response to requests by * portal administrators. * * <p>Optional Request parameter: * * <ul> * <li>sort : PRECEDENCE or NAME. Defaults to PRECEDENCE. Sort by precedence or name of fragment. * </ul> * * Implementation note: currently the UI client for this JSON service, in fragment-audit.jsp, does * not implement support for user selection of sort order. * */ @Controller @RequestMapping("/fragmentList") public class FragmentListController { private static final Sort DEFAULT_SORT = Sort.PRECEDENCE; private static final String CHANNEL_FNAME_XPATH = "//channel/@fname"; private ConfigurationLoader dlmConfig; private IPersonManager personManager; private IUserLayoutStore userLayoutStore; private IPortletDefinitionRegistry portletRegistry; private XPathOperations xpathOperations; private final Log log = LogFactory.getLog(getClass()); @Autowired public void setXpathOperations(XPathOperations xpathOperations) { this.xpathOperations = xpathOperations; } @Autowired public void setConfigurationLoader(ConfigurationLoader dlmConfig) { this.dlmConfig = dlmConfig; } @Autowired public void setPersonManager(IPersonManager personManager) { this.personManager = personManager; } @Autowired public void setUserLayoutStore(IUserLayoutStore userLayoutStore) { this.userLayoutStore = userLayoutStore; } @Autowired public void setPortletRegistry(IPortletDefinitionRegistry portletRegistry) { this.portletRegistry = portletRegistry; } /** * Returns a model of fragments --> List<FragmentBean> , sorted by precedence (default) or by * fragment name depending on sort parameter, to be rendered by the jsonView. * * @param req the servlet request, bound via SpringWebMVC to GET method invocations of this * controller. * @param sortParam PRECEDENCE, NAME, or null. * @return ModelAndView with a List of FragmentBeans to be rendered by the jsonView. * @throws ServletException on Exception in underlying attempt to get at the fragments * @throws AuthorizationException if request is for any user other than a Portal Administrator. * @throws IllegalArgumentException if sort parameter has an unrecognized value */ @RequestMapping(method = RequestMethod.GET) public ModelAndView listFragments( HttpServletRequest req, @RequestParam(value = "sort", required = false) String sortParam) throws ServletException { // Verify that the user is allowed to use this service IPerson user = personManager.getPerson(req); if (!AdminEvaluator.isAdmin(user)) { throw new AuthorizationException( "User " + user.getUserName() + " not an administrator."); } Map<String, Document> fragmentLayoutMap = null; if (userLayoutStore != null) { try { fragmentLayoutMap = userLayoutStore.getFragmentLayoutCopies(); } catch (Exception e) { String msg = "Failed to access fragment layouts"; log.error(msg, e); throw new ServletException(msg, e); } } List<FragmentBean> fragments = new ArrayList<FragmentBean>(); for (FragmentDefinition frag : dlmConfig.getFragments()) { Document layout = fragmentLayoutMap != null ? fragmentLayoutMap.get(frag.getOwnerId()) : null; List<String> portlets = null; if (layout != null) { portlets = new ArrayList<String>(); NodeList channelFNames = this.xpathOperations.evaluate( CHANNEL_FNAME_XPATH, layout, XPathConstants.NODESET); for (int i = 0; i < channelFNames.getLength(); i++) { String fname = channelFNames.item(i).getTextContent(); IPortletDefinition pDef = portletRegistry.getPortletDefinitionByFname(fname); if (null != pDef) { portlets.add(pDef.getTitle()); } } } fragments.add(FragmentBean.create(frag, portlets)); } // Determine & follow sorting preference... Sort sort = DEFAULT_SORT; if (sortParam != null) { sort = Sort.valueOf(sortParam); } Collections.sort(fragments, sort.getComparator()); return new ModelAndView("jsonView", "fragments", fragments); } /* * Nested Types */ private enum Sort { PRECEDENCE { public Comparator<FragmentBean> getComparator() { return new Comparator<FragmentBean>() { @Override public int compare(FragmentBean frag1, FragmentBean frag2) { // When sorting by precedence, use reverse order to // match the order in which the portal will sort them // as tabs. return frag2.getPrecedence().compareTo(frag1.getPrecedence()); } }; } }, NAME { public Comparator<FragmentBean> getComparator() { return new Comparator<FragmentBean>() { @Override public int compare(FragmentBean frag1, FragmentBean frag2) { return frag1.getName().compareTo(frag2.getName()); } }; } }; public abstract Comparator<FragmentBean> getComparator(); } /** Very simple class representing a DLM fragment. */ public static final class FragmentBean { // Instance Members. private final String name; private final String ownerId; private final Double precedence; private final List<String> audience; private final List<String> portlets; public static FragmentBean create(FragmentDefinition frag, List<String> portlets) { Validate.notNull(frag, "Cannot create a FragmentBean for a null fragment."); // NB: 'portlets' may be null return new FragmentBean( frag.getName(), frag.getOwnerId(), frag.getPrecedence(), frag.getEvaluators(), portlets); } public String getName() { return name; } public String getOwnerId() { return ownerId; } public Double getPrecedence() { return precedence; } public List<String> getAudience() { return audience; } public List<String> getPortlets() { return portlets; } private FragmentBean( String name, String ownerId, Double precedence, List<Evaluator> audience, List<String> portlets) { this.name = name; this.ownerId = ownerId; this.precedence = precedence; List<String> list = new ArrayList<String>(); for (Evaluator ev : audience) { list.add(ev.getSummary()); } this.audience = Collections.unmodifiableList(list); if (portlets != null) { this.portlets = Collections.unmodifiableList(portlets); } else { this.portlets = Collections.emptyList(); } } } }