001/* 002 * Copyright 2017 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.plugins.odfweb.restrictions; 017 018import java.io.File; 019import java.io.FileInputStream; 020import java.io.InputStream; 021import java.util.ArrayList; 022import java.util.Collections; 023import java.util.HashMap; 024import java.util.List; 025import java.util.Map; 026import java.util.Set; 027 028import org.apache.avalon.framework.activity.Disposable; 029import org.apache.avalon.framework.component.Component; 030import org.apache.avalon.framework.configuration.Configuration; 031import org.apache.avalon.framework.configuration.ConfigurationException; 032import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; 033import org.apache.avalon.framework.context.Context; 034import org.apache.avalon.framework.context.ContextException; 035import org.apache.avalon.framework.context.Contextualizable; 036import org.apache.avalon.framework.service.ServiceException; 037import org.apache.avalon.framework.service.ServiceManager; 038import org.apache.avalon.framework.service.Serviceable; 039import org.apache.cocoon.Constants; 040import org.apache.cocoon.components.ContextHelper; 041import org.apache.cocoon.environment.Request; 042import org.apache.commons.lang.StringUtils; 043 044import org.ametys.cms.content.ContentHelper; 045import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 046import org.ametys.core.ui.Callable; 047import org.ametys.odf.orgunit.OrgUnit; 048import org.ametys.odf.orgunit.RootOrgUnitProvider; 049import org.ametys.plugins.odfweb.restrictions.rules.OdfAndRestrictionRule; 050import org.ametys.plugins.odfweb.restrictions.rules.OdfMetadataRestrictionRule; 051import org.ametys.plugins.odfweb.restrictions.rules.OdfNotRestrictionRule; 052import org.ametys.plugins.odfweb.restrictions.rules.OdfOrRestrictionRule; 053import org.ametys.plugins.odfweb.restrictions.rules.OdfOrgunitRestrictionRule; 054import org.ametys.plugins.odfweb.restrictions.rules.OdfRestrictionRule; 055import org.ametys.plugins.repository.AmetysObjectResolver; 056import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector; 057import org.ametys.runtime.i18n.I18nizableText; 058import org.ametys.runtime.plugin.component.AbstractLogEnabled; 059import org.ametys.web.repository.page.Page; 060import org.ametys.web.repository.site.Site; 061import org.ametys.web.repository.site.SiteManager; 062 063/** 064 * Component able to handle program restrictions related to the "odf-restrictions" site parameter. 065 */ 066public class OdfProgramRestrictionManager extends AbstractLogEnabled implements Component, Serviceable, Contextualizable, Disposable 067{ 068 /** The avalon role. */ 069 public static final String ROLE = OdfProgramRestrictionManager.class.getName(); 070 071 /** Site Manager */ 072 protected SiteManager _siteManager; 073 074 /** The content type extension point. */ 075 protected ContentTypeExtensionPoint _cTypeEP; 076 077 /** Ametys object resolver */ 078 protected AmetysObjectResolver _resolver; 079 080 /** Root orgunit provider */ 081 protected RootOrgUnitProvider _rootOrgUnitProvider; 082 083 /** Cocoon context */ 084 protected org.apache.cocoon.environment.Context _cocoonContext; 085 086 /** The available odf restrictions */ 087 protected Map<String, OdfProgramRestriction> _restrictions; 088 089 /** Content helper */ 090 protected ContentHelper _contentHelper; 091 092 private Context _context; 093 094 @Override 095 public void service(ServiceManager serviceManager) throws ServiceException 096 { 097 _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE); 098 _cTypeEP = (ContentTypeExtensionPoint) serviceManager.lookup(ContentTypeExtensionPoint.ROLE); 099 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 100 _rootOrgUnitProvider = (RootOrgUnitProvider) serviceManager.lookup(RootOrgUnitProvider.ROLE); 101 } 102 103 @Override 104 public void contextualize(Context context) throws ContextException 105 { 106 _context = context; 107 _cocoonContext = (org.apache.cocoon.environment.Context) context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT); 108 } 109 110 @Override 111 public void dispose() 112 { 113 _restrictions = null; 114 } 115 116 /** 117 * Retrieves the available restrictions 118 * @return The map of restriction, where keys are restriction ids. 119 */ 120 public Map<String, OdfProgramRestriction> getRestrictions() 121 { 122 // Lazy initialize restrictions because orgunit restrictions cannot be 123 // resolved during the initialization of the component 124 if (_restrictions == null) 125 { 126 _readRestrictionConfigurationFile(); 127 } 128 129 return _restrictions; 130 } 131 132 /** 133 * Get the ODF restriction for the given ODF root page 134 * @param odfRootPage The ODF root page 135 * @return The restriction or <code>null</code> 136 */ 137 public OdfProgramRestriction getRestriction(Page odfRootPage) 138 { 139 Site site = odfRootPage.getSite(); 140 String restrictionId = site.getValue("odf-restrictions"); 141 if (StringUtils.isNotEmpty(restrictionId)) 142 { 143 return getRestrictions().get(restrictionId); 144 } 145 146 return null; 147 } 148 149 /** 150 * Indicate is there is any restriction for this root page 151 * @param rootPage odf root page 152 * @return true if it is the case 153 */ 154 public boolean hasRestrictions(Page rootPage) 155 { 156 Site site = rootPage.getSite(); 157 String restrictionId = site.getValue("odf-restrictions"); 158 return StringUtils.isNotEmpty(restrictionId) ? getRestrictions().containsKey(restrictionId) : false; 159 } 160 161 /** 162 * Indicate is there is any restriction related to orgunit for this root page 163 * @param rootPage odf root page 164 * @return true if it is the case 165 */ 166 public boolean hasOrgunitRestrictions(Page rootPage) 167 { 168 Site site = rootPage.getSite(); 169 String restrictionId = site.getValue("odf-restrictions"); 170 if (StringUtils.isNotEmpty(restrictionId)) 171 { 172 OdfProgramRestriction odfProgramRestriction = getRestrictions().get(restrictionId); 173 return odfProgramRestriction.hasOrgunitRestrictions(); 174 } 175 176 return false; 177 } 178 179 /** 180 * Get the id of the restriction root orgunit 181 * @param siteName the site name 182 * @return the id of the restriction root orgunit 183 */ 184 @Callable 185 public String getRestrictionRootOrgUnitId(String siteName) 186 { 187 String odfRootId = null; 188 189 Site site = _siteManager.getSite(siteName); 190 String restrictionId = site.getValue("odf-restrictions"); 191 if (StringUtils.isNotEmpty(restrictionId)) 192 { 193 OdfProgramRestriction odfProgramRestriction = getRestrictions().get(restrictionId); 194 if (odfProgramRestriction != null && odfProgramRestriction.hasOrgunitRestrictions()) 195 { 196 odfRootId = odfProgramRestriction.getId(); 197 } 198 } 199 200 if (StringUtils.isBlank(odfRootId)) 201 { 202 odfRootId = _rootOrgUnitProvider.getRootId(); 203 } 204 205 return odfRootId; 206 } 207 208 /** 209 * Configured the restrictions from the XML configuration file 210 */ 211 protected void _readRestrictionConfigurationFile() 212 { 213 _restrictions = new HashMap<>(); 214 215 File restrictionFile = new File (_cocoonContext.getRealPath("/WEB-INF/param/odf-restrictions.xml")); 216 217 if (restrictionFile.exists()) 218 { 219 try (InputStream is = new FileInputStream(restrictionFile);) 220 { 221 Configuration cfg = new DefaultConfigurationBuilder().build(is); 222 223 Configuration[] restrictionConfs = cfg.getChildren("restriction"); 224 for (Configuration restrictionConf : restrictionConfs) 225 { 226 String id = restrictionConf.getAttribute("id"); 227 I18nizableText label = _configureI18nizableText(restrictionConf, "label", "", "application"); 228 List<OdfRestrictionRule> rules = _configureRestrictionRules(restrictionConf.getChild("rules")); 229 230 _restrictions.put(id, new OdfProgramRestriction(id, label, rules)); 231 } 232 233 Configuration orgunitConf = cfg.getChild("orgunits", false); 234 if (orgunitConf != null) 235 { 236 _addDefaultOrgunitRestrictions(); 237 } 238 } 239 catch (Exception e) 240 { 241 getLogger().error("Cannot read the configuration file located at /WEB-INF/param/odf-restrictions.xml. Reverting to default.", e); 242 _restrictions.clear(); 243 _addDefaultOrgunitRestrictions(); 244 } 245 } 246 else 247 { 248 _addDefaultOrgunitRestrictions(); 249 } 250 } 251 252 private I18nizableText _configureI18nizableText(Configuration config, String name, String defaultValue, String defaultCatalog) 253 { 254 Configuration textConfig = config.getChild(name); 255 boolean i18nSupported = textConfig.getAttributeAsBoolean("i18n", false); 256 String text = textConfig.getValue(defaultValue); 257 258 if (i18nSupported) 259 { 260 String catalogue = textConfig.getAttribute("catalogue", defaultCatalog); 261 return new I18nizableText(catalogue, text); 262 } 263 else 264 { 265 return new I18nizableText(text); 266 } 267 } 268 269 /** 270 * Create a list of rules from configuration nodes 271 * @param rulesConf Array of configuration node that represents the set of rules 272 * @return list of rules 273 * @throws ConfigurationException if a configuration error is encountered 274 */ 275 protected List<OdfRestrictionRule> _configureRestrictionRules(Configuration rulesConf) throws ConfigurationException 276 { 277 List<OdfRestrictionRule> rules = new ArrayList<>(); 278 279 for (Configuration ruleConf : rulesConf.getChildren()) 280 { 281 rules.add(_configureRestrictionRule(ruleConf)); 282 } 283 284 return rules; 285 } 286 287 /** 288 * Configure a restriction rule from a configuration node 289 * @param ruleConf The configuration node representing the rule 290 * @return The odf restriction rule 291 * @throws ConfigurationException if a configuration error is encountered 292 */ 293 protected OdfRestrictionRule _configureRestrictionRule(Configuration ruleConf) throws ConfigurationException 294 { 295 String name = ruleConf.getName(); 296 297 if ("metadata".equals(name)) 298 { 299 String metadataPath = ruleConf.getAttribute("name"); 300 String metadataValue = ruleConf.getAttribute("value"); 301 return new OdfMetadataRestrictionRule(_contentHelper, metadataPath, metadataValue); 302 } 303 else if ("orgunit".equals(name)) 304 { 305 String orgunitId = ruleConf.getAttribute("id", null); 306 307 if (StringUtils.isEmpty(orgunitId)) 308 { 309 throw new ConfigurationException("Expecting 'id' attribute for orgunit restriction rule."); 310 } 311 312 return new OdfOrgunitRestrictionRule(_rootOrgUnitProvider, orgunitId); 313 } 314 else if ("and".equals(name)) 315 { 316 List<OdfRestrictionRule> childRules = _configureRestrictionRules(ruleConf); 317 return new OdfAndRestrictionRule(childRules); 318 } 319 else if ("or".equals(name)) 320 { 321 List<OdfRestrictionRule> childRules = _configureRestrictionRules(ruleConf); 322 return new OdfOrRestrictionRule(childRules); 323 } 324 else if ("not".equals(name)) 325 { 326 List<OdfRestrictionRule> childRules = _configureRestrictionRules(ruleConf); 327 return new OdfNotRestrictionRule(childRules); 328 } 329 330 throw new ConfigurationException("Unknow node name in restriction configuration : " + name); 331 } 332 333 private void _addDefaultOrgunitRestrictions() 334 { 335 Request request = ContextHelper.getRequest(_context); 336 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 337 338 try 339 { 340 // Force default workspace 341 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, null); 342 343 String rootOrgunitId = _rootOrgUnitProvider.getRootId(); 344 Set<String> orgunitIds = _rootOrgUnitProvider.getChildOrgUnitIds(rootOrgunitId, true); 345 orgunitIds.add(rootOrgunitId); 346 347 for (String id : orgunitIds) 348 { 349 if (!StringUtils.equals(id, rootOrgunitId)) 350 { 351 OrgUnit orgunit = _resolver.resolveById(id); 352 353 OdfRestrictionRule rule = new OdfOrgunitRestrictionRule(_rootOrgUnitProvider, id); 354 OdfProgramRestriction restriction = new OdfProgramRestriction(id, new I18nizableText(orgunit.getTitle()), Collections.singletonList(rule)); 355 356 _restrictions.put(id, restriction); 357 } 358 } 359 } 360 finally 361 { 362 // Restore workspace 363 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 364 } 365 } 366}