/** * 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.portlets.dynamicskin; import java.io.IOException; import java.text.MessageFormat; import javax.portlet.PortletPreferences; import javax.portlet.PortletRequest; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import javax.servlet.http.HttpServletRequest; import org.apereo.portal.portlet.om.IPortletEntity; import org.apereo.portal.portlet.om.IPortletWindow; import org.apereo.portal.portlet.om.IPortletWindowId; import org.apereo.portal.portlet.registry.IPortletWindowRegistry; import org.apereo.portal.portlets.dynamicskin.storage.DynamicSkinService; import org.apereo.portal.security.AuthorizationPrincipalHelper; import org.apereo.portal.security.IAuthorizationPrincipal; import org.apereo.portal.security.IPerson; import org.apereo.portal.security.IPersonManager; import org.apereo.portal.url.IPortalRequestUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.portlet.ModelAndView; import org.springframework.web.portlet.bind.annotation.RenderMapping; /** * The DynamicRespondrSkin portlet includes a CONFIG mode interface that allows an admin to set * various skin properties, together with a VIEW mode controller that renders a link to the compiled * skin (CSS file) and generates that file if necessary. * * @since 4.1.0 */ @Controller @RequestMapping("VIEW") public class DynamicRespondrSkinViewController { private static final MessageFormat DEFAULT_SKIN_CSS_PATH_FORMAT = new MessageFormat(DynamicRespondrSkinConstants.DEFAULT_RELATIVE_ROOT_FOLDER + "/{0}.css"); protected Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private DynamicSkinService service; @Autowired private IPortalRequestUtils portalRequestUtils; @Autowired private IPersonManager personManager; @Autowired private IPortletWindowRegistry portletWindowRegistry; /** * Display Skin CSS include based on skin's configuration values from portlet preferences.<br> * dynamic=false: load the pre-built css file from the skins directory and default skin name; * e.g. RELATIVE_ROOT/{defaultSkin}.css dynamic=true: Process the default skin less file if * needed at RELATIVE_ROOT/{defaultSkin}.less to create a customized skin css file * (RELATIVE_ROOT/skin-ID#.css to load. */ @RenderMapping public ModelAndView displaySkinCssHeader( RenderRequest request, RenderResponse response, Model model) throws IOException { // NOTE: RENDER_HEADERS phase may be called before or at the same time as the RENDER_MARKUP. The spec is // silent on this issue and uPortal does not guarantee order or timing of render execution, but does // guarantee order of render output processing (output of RENDER_HEADERS phase is included before // RENDER_MARKUP phase). uPortal inserts the HTML markup returned from RENDER_HEADERS execution into the HEAD // section of the page. if (PortletRequest.RENDER_HEADERS.equals( request.getAttribute(PortletRequest.RENDER_PART))) { PortletPreferences prefs = request.getPreferences(); Boolean enabled = Boolean.valueOf(prefs.getValue(DynamicRespondrSkinConstants.PREF_DYNAMIC, "false")); String skinName = prefs.getValue(DynamicRespondrSkinConstants.PREF_SKIN_NAME, DynamicRespondrSkinConstants.DEFAULT_SKIN_NAME); String cssUrl = enabled ? calculateDynamicSkinUrlPathToUse(request, skinName) : calculateDefaultSkinCssLocationInWebapp(skinName); model.addAttribute(DynamicRespondrSkinConstants.SKIN_CSS_URL_MODEL_ATTRIBUTE_NAME, cssUrl); return new ModelAndView("jsp/DynamicRespondrSkin/skinHeader"); } else { // We need to know if this user can CONFIG this skin boolean canAccessSkinConfig = false; // Default final HttpServletRequest httpr = portalRequestUtils.getCurrentPortalRequest(); final IPerson user = personManager.getPerson(httpr); final IAuthorizationPrincipal principal = AuthorizationPrincipalHelper.principalFromUser(user); final IPortletWindowId portletWindowId = portletWindowRegistry.getPortletWindowId(httpr, request.getWindowID()); final IPortletWindow portletWindow = portletWindowRegistry.getPortletWindow(httpr, portletWindowId); final IPortletEntity portletEntity = portletWindow.getPortletEntity(); if (principal.canConfigure(portletEntity.getPortletDefinitionId().toString())) { canAccessSkinConfig = true; } // RENDER_MARKUP return new ModelAndView( "jsp/DynamicRespondrSkin/skinBody", DynamicRespondrSkinConstants.CAN_ACCESS_SKIN_CONFIG_MODEL_NAME, canAccessSkinConfig); } } /** * Calculate the default skin URL path or the path to a skin CSS file that is specific to the set of * portlet preference values currently defined. * * @param request * @return * @throws IOException */ private String calculateDynamicSkinUrlPathToUse(PortletRequest request, String lessfileBaseName) throws IOException { final DynamicSkinInstanceData data = new DefaultDynamicSkinInstanceDataImpl(request); if (!service.skinCssFileExists(data)) { // Trigger the LESS compilation service.generateSkinCssFile(data); } return service.getSkinCssPath(data); } /** * Calculates the relative URL of the default skin CSS file. * @param skinName skin filename * @return Relative URL of the default skin CSS file for the user. */ private String calculateDefaultSkinCssLocationInWebapp(String skinName) { return DEFAULT_SKIN_CSS_PATH_FORMAT.format(new Object[] {skinName}); } }