001/* 002 * Copyright 2023 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.web.repository.page.virtual; 017 018import java.io.InputStream; 019import java.util.Collection; 020import java.util.LinkedHashMap; 021import java.util.Map; 022 023import org.apache.avalon.framework.configuration.Configurable; 024import org.apache.avalon.framework.configuration.Configuration; 025import org.apache.avalon.framework.configuration.ConfigurationException; 026import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; 027import org.apache.avalon.framework.context.Context; 028import org.apache.avalon.framework.context.ContextException; 029import org.apache.avalon.framework.context.Contextualizable; 030import org.apache.avalon.framework.service.ServiceException; 031import org.apache.avalon.framework.service.ServiceManager; 032import org.apache.avalon.framework.service.Serviceable; 033import org.apache.cocoon.components.ContextHelper; 034import org.apache.cocoon.environment.Request; 035import org.apache.excalibur.source.Source; 036 037import org.ametys.core.util.filereloader.FileReloader; 038import org.ametys.core.util.filereloader.FileReloaderUtils; 039import org.ametys.runtime.plugin.component.AbstractLogEnabled; 040import org.ametys.runtime.plugin.component.PluginAware; 041import org.ametys.web.WebConstants; 042import org.ametys.web.repository.page.Page; 043import org.ametys.web.repository.site.Site; 044import org.ametys.web.repository.site.SiteManager; 045 046/** 047 * This class represents the configuration of a virtual page based on an XML configuration 048 */ 049public class VirtualPageConfiguration extends AbstractLogEnabled implements Configurable, Serviceable, Contextualizable, PluginAware 050{ 051 /** The file reloader utils */ 052 protected FileReloaderUtils _fileReloaderUtils; 053 /** The site manager */ 054 protected SiteManager _siteManager; 055 /** The context */ 056 protected Context _context; 057 058 private String _id; 059 private String _template; 060 private Map<String, VirtualZoneConfiguration> _zonesConfiguration; 061 private String _path; 062 063 @Override 064 public void service(ServiceManager manager) throws ServiceException 065 { 066 _fileReloaderUtils = (FileReloaderUtils) manager.lookup(FileReloaderUtils.ROLE); 067 _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE); 068 } 069 070 @Override 071 public void contextualize(Context context) throws ContextException 072 { 073 _context = context; 074 } 075 076 public void setPluginInfo(String pluginName, String featureName, String id) 077 { 078 _id = id; 079 } 080 081 public void configure(Configuration configuration) throws ConfigurationException 082 { 083 _path = configuration.getChild("path").getValue(); 084 } 085 086 private void _configure(Configuration configuration) throws ConfigurationException 087 { 088 _template = configuration.getAttribute("template", "page"); 089 090 _zonesConfiguration = new LinkedHashMap<>(); 091 Configuration[] zonesConfigurations = configuration.getChildren("zone"); 092 for (Configuration zoneConfiguration : zonesConfigurations) 093 { 094 VirtualZoneConfiguration zone = new VirtualZoneConfiguration(zoneConfiguration); 095 096 _zonesConfiguration.put(zone.getId(), zone); 097 } 098 } 099 100 /** 101 * Get the path to override the configuration with a skin configuration 102 * @return The path 103 */ 104 public String getPath() 105 { 106 return _path; 107 } 108 109 /** 110 * Get the id 111 * @return The id 112 */ 113 public String getId() 114 { 115 return _id; 116 } 117 118 /** 119 * Get the template's id 120 * @param rootPage The root page 121 * @return The template's id 122 */ 123 public String getTemplate(Page rootPage) 124 { 125 loadSkinConfigurationIfExists(rootPage); 126 return _template; 127 } 128 129 /** 130 * Get the zones configurations 131 * @param rootPage The root page 132 * @return The collection of VirtualPageZoneConfiguration 133 */ 134 public Collection<VirtualZoneConfiguration> getZonesConfigurations(Page rootPage) 135 { 136 loadSkinConfigurationIfExists(rootPage); 137 return _zonesConfiguration.values(); 138 } 139 140 /** 141 * Check if the virtual page has a zone, by its id 142 * @param id The zone id 143 * @param rootPage The root page 144 * @return true if the page has a zone of the id given, false otherwise 145 */ 146 public boolean hasZoneConfiguration(String id, Page rootPage) 147 { 148 loadSkinConfigurationIfExists(rootPage); 149 return _zonesConfiguration.containsKey(id); 150 } 151 152 /** 153 * Get the zone configuration for a zone id 154 * @param id The zone id 155 * @param rootPage The root page 156 * @return The VirtualPageZoneConfiguration, null if none is found 157 */ 158 public VirtualZoneConfiguration getZoneConfiguration(String id, Page rootPage) 159 { 160 loadSkinConfigurationIfExists(rootPage); 161 return _zonesConfiguration.get(id); 162 } 163 164 /** 165 * Load the configuration from skin if one is found 166 * @param rootPage The root page 167 */ 168 private void loadSkinConfigurationIfExists(Page rootPage) 169 { 170 Site site = rootPage.getSite(); 171 String siteName = site.getName(); 172 String skinId = site.getSkinId(); 173 174 Request request = ContextHelper.getRequest(_context); 175 Site oldSite = (Site) request.getAttribute(WebConstants.REQUEST_ATTR_SITE); 176 String oldSiteName = (String) request.getAttribute(WebConstants.REQUEST_ATTR_SITE_NAME); 177 String oldSkinId = (String) request.getAttribute(WebConstants.REQUEST_ATTR_SKIN_ID); 178 179 try 180 { 181 request.setAttribute(WebConstants.REQUEST_ATTR_SITE, site); 182 request.setAttribute(WebConstants.REQUEST_ATTR_SITE_NAME, siteName); 183 request.setAttribute(WebConstants.REQUEST_ATTR_SKIN_ID, skinId); 184 185 String filePath = getPath(); 186 try 187 { 188 _fileReloaderUtils.updateFile(filePath, true, new VirtualPageConfigurationFileReloader(skinId, filePath, this)); 189 } 190 catch (Exception e) 191 { 192 throw new IllegalStateException("No configurations found at path '" + filePath + "' in the skin for virtual page of id " + _id, e); 193 } 194 } 195 finally 196 { 197 request.setAttribute(WebConstants.REQUEST_ATTR_SITE, oldSite); 198 request.setAttribute(WebConstants.REQUEST_ATTR_SITE_NAME, oldSiteName); 199 request.setAttribute(WebConstants.REQUEST_ATTR_SKIN_ID, oldSkinId); 200 } 201 } 202 203 /** 204 * Class representing a virtual page conf file reloader 205 */ 206 public static class VirtualPageConfigurationFileReloader implements FileReloader 207 { 208 private String _skinId; 209 private String _filePath; 210 private VirtualPageConfiguration _component; 211 212 /** 213 * Constructor 214 * @param skinId the skin id 215 * @param filePath the file path 216 * @param component the abstract parent component 217 */ 218 public VirtualPageConfigurationFileReloader (String skinId, String filePath, VirtualPageConfiguration component) 219 { 220 _skinId = skinId; 221 _filePath = filePath; 222 _component = component; 223 } 224 225 /** 226 * Get the skinId 227 * @return the skinId 228 */ 229 public String getSkinId() 230 { 231 return _skinId; 232 } 233 234 /** 235 * Get the file path 236 * @return the file path 237 */ 238 public String getFilePath() 239 { 240 return _filePath; 241 } 242 243 /** 244 * Get the parent component 245 * @return the parent component 246 */ 247 public VirtualPageConfiguration getComponent() 248 { 249 return _component; 250 } 251 252 @Override 253 public void updateFile(String sourceUrl, Source source, InputStream is) throws Exception 254 { 255 if (is != null) 256 { 257 Configuration cfg = new DefaultConfigurationBuilder().build(is, source.getURI()); 258 getComponent()._configure(cfg); 259 } 260 else 261 { 262 throw new UnsupportedOperationException("Components extending AbstractVirtualPageConfiguration should always have a default configuration file"); 263 } 264 } 265 266 @Override 267 public String getId(String sourceUrl) 268 { 269 return VirtualPageConfigurationFileReloader.class.getName() + "#" + getFilePath() + "#" + getSkinId(); 270 } 271 } 272}