001/* 002 * Copyright 2010 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.service; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023 024import org.apache.avalon.framework.activity.Disposable; 025import org.apache.avalon.framework.configuration.Configurable; 026import org.apache.avalon.framework.configuration.Configuration; 027import org.apache.avalon.framework.configuration.ConfigurationException; 028import org.apache.avalon.framework.context.Context; 029import org.apache.avalon.framework.context.ContextException; 030import org.apache.avalon.framework.context.Contextualizable; 031import org.apache.avalon.framework.service.ServiceException; 032import org.apache.avalon.framework.service.ServiceManager; 033import org.apache.avalon.framework.service.Serviceable; 034import org.apache.commons.lang3.LocaleUtils; 035import org.apache.solr.common.SolrInputDocument; 036 037import org.ametys.cms.data.type.indexing.IndexableElementTypeHelper; 038import org.ametys.cms.model.CMSDataContext; 039import org.ametys.core.ui.ClientSideElement.Script; 040import org.ametys.core.ui.ClientSideElement.ScriptFile; 041import org.ametys.plugins.core.ui.util.ConfigurationHelper; 042import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder; 043import org.ametys.plugins.repository.model.parsing.RepeaterDefinitionParser; 044import org.ametys.runtime.i18n.I18nizableText; 045import org.ametys.runtime.model.ElementDefinition; 046import org.ametys.runtime.model.Enumerator; 047import org.ametys.runtime.model.ItemParserHelper; 048import org.ametys.runtime.model.ItemParserHelper.ConfigurationAndPluginName; 049import org.ametys.runtime.model.ModelItem; 050import org.ametys.runtime.model.View; 051import org.ametys.runtime.model.type.ElementType; 052import org.ametys.runtime.parameter.Validator; 053import org.ametys.runtime.plugin.component.AbstractLogEnabled; 054import org.ametys.runtime.plugin.component.PluginAware; 055import org.ametys.runtime.plugin.component.ThreadSafeComponentManager; 056import org.ametys.web.data.type.ModelItemTypeExtensionPoint; 057import org.ametys.web.parameters.ViewAndParametersParser; 058import org.ametys.web.parameters.ViewAndParametersParser.ViewAndParameters; 059import org.ametys.web.repository.page.Page; 060import org.ametys.web.repository.page.ZoneItem; 061 062/** 063 * Class representing a business service. <br> 064 * A service is identified by an id and a Cocoon-URL.<br> 065 * This URL corresponds to a pipeline called by a page template.<br> 066 * URL must be relative to the sitemap of the plugin containing the service. 067 */ 068public class StaticService extends AbstractLogEnabled implements Service, Contextualizable, Configurable, PluginAware, Serviceable, Disposable 069{ 070 /** The plugin name */ 071 protected String _pluginName; 072 /** The feature name */ 073 protected String _featureName; 074 /** The service manager */ 075 protected ServiceManager _manager; 076 /** The context. */ 077 protected Context _context; 078 /** The script configured */ 079 protected Script _paramsScript; 080 081 /** The view of the service */ 082 protected View _view; 083 084 /** The map of model items */ 085 protected Map<String, ModelItem> _modelItems; 086 087 /** The view and parameters parser */ 088 protected ViewAndParametersParser _viewAndParametersParser; 089 090 /** The service parameter definition parser */ 091 protected ServiceParameterDefinitionParser _serviceParameterDefinitionParser; 092 093 /** The repeater definition parser */ 094 protected RepeaterDefinitionParser _repeaterDefinitionParser; 095 096 private String _id; 097 private I18nizableText _label; 098 private I18nizableText _description; 099 private I18nizableText _category; 100 101 private String _iconGlyph; 102 private String _iconDecorator; 103 private String _smallIcon; 104 private String _mediumIcon; 105 private String _largeIcon; 106 private Integer _creationBoxHeight; 107 private Integer _creationBoxWidth; 108 private List<ScriptFile> _cssFiles; 109 110 private boolean _isCacheable; 111 private boolean _isPrivate; 112 /** The right needed to create an instance of this service, blank or null if no right is needed. */ 113 private String _right; 114 115 private String _url; 116 117 private List<String> _parametersToIndex; 118 119 // ComponentManager for validators 120 private ThreadSafeComponentManager<Validator> _validatorManager; 121 122 // ComponentManager for enumerators 123 private ThreadSafeComponentManager<Enumerator> _enumeratorManager; 124 125 private ModelItemTypeExtensionPoint _serviceParameterTypeExtensionPoint; 126 127 128 @Override 129 public void service(ServiceManager smanager) throws ServiceException 130 { 131 _manager = smanager; 132 _serviceParameterTypeExtensionPoint = (ModelItemTypeExtensionPoint) smanager.lookup(ModelItemTypeExtensionPoint.ROLE_SERVICE_PARAM); 133 _viewAndParametersParser = (ViewAndParametersParser) smanager.lookup(ViewAndParametersParser.ROLE); 134 } 135 136 @Override 137 public void contextualize(Context context) throws ContextException 138 { 139 _context = context; 140 } 141 142 @Override 143 public void dispose() 144 { 145 _validatorManager.dispose(); 146 _validatorManager = null; 147 148 _enumeratorManager.dispose(); 149 _enumeratorManager = null; 150 } 151 152 @Override 153 public void configure(Configuration configuration) throws ConfigurationException 154 { 155 _validatorManager = new ThreadSafeComponentManager<>(); 156 _validatorManager.setLogger(getLogger()); 157 _validatorManager.contextualize(_context); 158 _validatorManager.service(_manager); 159 160 _enumeratorManager = new ThreadSafeComponentManager<>(); 161 _enumeratorManager.setLogger(getLogger()); 162 _enumeratorManager.contextualize(_context); 163 _enumeratorManager.service(_manager); 164 165 ConfigurationAndPluginName configurationAndPluginName = new ConfigurationAndPluginName(configuration, _pluginName); 166 _label = ItemParserHelper.parseI18nizableText(configurationAndPluginName, "label"); 167 _description = ItemParserHelper.parseI18nizableText(configurationAndPluginName, "description"); 168 _category = ItemParserHelper.parseI18nizableText(configurationAndPluginName, "category"); 169 170 _isCacheable = configuration.getChild("cacheable").getValueAsBoolean(false); 171 _isPrivate = configuration.getChild("private").getValueAsBoolean(false); 172 _right = configuration.getChild("right").getValue(null); 173 174 this._iconGlyph = configuration.getChild("thumbnail").getChild("glyph").getValue(null); 175 this._iconDecorator = configuration.getChild("thumbnail").getChild("decorator").getValue(null); 176 this._smallIcon = _configureThumbnail(configuration.getChild("thumbnail").getChild("small"), "/plugins/web/resources/img/service/servicepage_16.png"); 177 this._mediumIcon = _configureThumbnail(configuration.getChild("thumbnail").getChild("medium"), "/plugins/web/resources/img/service/servicepage_32.png"); 178 this._largeIcon = _configureThumbnail(configuration.getChild("thumbnail").getChild("marge"), "/plugins/web/resources/img/service/servicepage_50.png"); 179 this._creationBoxHeight = configureDialogBoxDimension(configuration, "height"); 180 this._creationBoxWidth = configureDialogBoxDimension(configuration, "width"); 181 182 _cssFiles = _configureImports(configuration.getChild("css")); 183 _paramsScript = _configureScript(configuration.getChild("parameters")); 184 185 _url = configuration.getChild("url").getValue(); 186 187 _serviceParameterDefinitionParser = new ServiceParameterDefinitionParser(_serviceParameterTypeExtensionPoint, _enumeratorManager, _validatorManager); 188 _repeaterDefinitionParser = new RepeaterDefinitionParser(_serviceParameterTypeExtensionPoint); 189 190 Configuration parametersConfiguration = configuration.getChild("parameters"); 191 configureParameters(parametersConfiguration); 192 193 try 194 { 195 _serviceParameterDefinitionParser.lookupComponents(); 196 } 197 catch (Exception e) 198 { 199 throw new ConfigurationException("Unable to lookup parameter local components", configuration, e); 200 } 201 202 configureIndexation(configuration.getChild("indexation")); 203 } 204 205 /** 206 * Configure the service parameters 207 * @param parametersConfiguration the parameters configuration 208 * @throws ConfigurationException if a configuration exception occurred 209 */ 210 protected void configureParameters(Configuration parametersConfiguration) throws ConfigurationException 211 { 212 ViewAndParameters viewAndParameters = _viewAndParametersParser.parseParameters(parametersConfiguration, _pluginName, "plugin." + _pluginName, this, _serviceParameterDefinitionParser, _repeaterDefinitionParser); 213 _view = viewAndParameters.getView(); 214 _modelItems = viewAndParameters.getParameters(); 215 } 216 217 /** 218 * Configure the indexation process.<br> 219 * This class only allow to index the value of some parameters (not repeaters). 220 * @param configuration the indexation configuration. 221 * @throws ConfigurationException if an error occurs. 222 */ 223 protected void configureIndexation(Configuration configuration) throws ConfigurationException 224 { 225 _parametersToIndex = new ArrayList<>(); 226 227 for (Configuration config : configuration.getChildren("parameter")) 228 { 229 String id = config.getValue(""); 230 ModelItem param = _modelItems.get(id); 231 232 if (param == null || !(param instanceof ElementDefinition)) 233 { 234 if (getLogger().isWarnEnabled()) 235 { 236 getLogger().warn("Invalid indexation configuration for service " + _id + " : there's no parameter '" + id + "'"); 237 } 238 } 239 else 240 { 241 _parametersToIndex.add(id); 242 } 243 } 244 } 245 246 @Override 247 public void setPluginInfo(String pluginName, String featureName, String id) 248 { 249 _pluginName = pluginName; 250 _featureName = featureName; 251 _id = id; 252 } 253 254 @Override 255 public String getPluginName() 256 { 257 return _pluginName; 258 } 259 260 @Override 261 public String getId() 262 { 263 return _id; 264 } 265 266 public String getName() 267 { 268 return _id; 269 } 270 271 @Override 272 public boolean isCacheable(Page currentPage, ZoneItem zoneItem) 273 { 274 return _isCacheable; 275 } 276 277 @Override 278 public I18nizableText getLabel() 279 { 280 return _label; 281 } 282 283 @Override 284 public I18nizableText getDescription() 285 { 286 return _description; 287 } 288 289 @Override 290 public I18nizableText getCategory() 291 { 292 return _category; 293 } 294 295 @Override 296 public String getIconGlyph() 297 { 298 return _iconGlyph; 299 } 300 301 @Override 302 public String getIconDecorator() 303 { 304 return _iconDecorator; 305 } 306 307 @Override 308 public String getSmallIcon() 309 { 310 return _smallIcon; 311 } 312 313 @Override 314 public String getMediumIcon() 315 { 316 return _mediumIcon; 317 } 318 319 @Override 320 public String getLargeIcon() 321 { 322 return _largeIcon; 323 } 324 325 @Override 326 public Integer getCreationBoxHeight() 327 { 328 return _creationBoxHeight; 329 } 330 331 @Override 332 public Integer getCreationBoxWidth() 333 { 334 return _creationBoxWidth; 335 } 336 337 @Override 338 public String getURL() 339 { 340 return "cocoon://_plugins/" + _pluginName + "/" + _url; 341 } 342 343 @Override 344 public Script getParametersScript() 345 { 346 return _paramsScript; 347 } 348 349 @Override 350 public List<ScriptFile> getCSSFiles() 351 { 352 return _cssFiles; 353 } 354 355 @Override 356 public boolean isPrivate() 357 { 358 return _isPrivate; 359 } 360 361 @Override 362 public String getRight() 363 { 364 return _right; 365 } 366 367 @SuppressWarnings("unchecked") 368 @Override 369 public void index(ZoneItem zoneItem, SolrInputDocument document) 370 { 371 for (String id : _parametersToIndex) 372 { 373 ModelAwareDataHolder dataHolder = zoneItem.getServiceParameters(); 374 if (dataHolder.hasValue(id)) 375 { 376 String language = zoneItem.getZone().getSitemapElement().getSitemapName(); 377 378 Object value = dataHolder.getValue(id); 379 380 ElementDefinition parameter = (ElementDefinition) _modelItems.get(id); 381 ElementType type = parameter.getType(); 382 383 CMSDataContext context = CMSDataContext.newInstance() 384 .withLocale(LocaleUtils.toLocale(language)) 385 .withModelItem(parameter); 386 if (parameter.isMultiple()) 387 { 388 for (Object singleValue : (Object[]) value) 389 { 390 IndexableElementTypeHelper.indexFulltextValue(document, type.toString(singleValue), context); 391 } 392 } 393 else 394 { 395 IndexableElementTypeHelper.indexFulltextValue(document, type.toString(value), context); 396 } 397 } 398 } 399 } 400 401 private String _configureThumbnail(Configuration valueConf, String defaultImage) 402 { 403 String value = valueConf.getValue(null); 404 if (value == null) 405 { 406 return defaultImage; 407 } 408 else 409 { 410 String pluginName = valueConf.getAttribute("plugin", this._pluginName); 411 return "/plugins/" + pluginName + "/resources/" + value; 412 } 413 } 414 415 /** 416 * Configure the dimensions of the dialog box 417 * @param configuration the global configuration 418 * @param dimensionName the name of the dimension to configure 419 * @return the value of the dimension 420 * @throws ConfigurationException if configuration is invalid 421 */ 422 protected Integer configureDialogBoxDimension(Configuration configuration, String dimensionName) throws ConfigurationException 423 { 424 Configuration dimensionConfiguration = configuration.getChild("dialogBox").getChild(dimensionName, false); 425 if (dimensionConfiguration != null) 426 { 427 return dimensionConfiguration.getValueAsInteger(); 428 } 429 else 430 { 431 return null; 432 } 433 } 434 435 /** 436 * Configure the script 437 * @param configuration the global configuration 438 * @return The script created 439 * @throws ConfigurationException if configuration is invalid 440 */ 441 protected Script _configureScript(Configuration configuration) throws ConfigurationException 442 { 443 List<ScriptFile> scriptsImports = _configureImports(configuration.getChild("scripts")); 444 List<ScriptFile> cssImports = _configureImports(configuration.getChild("css")); 445 String jsClassName = _configureClass(configuration.getChild("action")); 446 447 return new Script(this.getId(), jsClassName, scriptsImports, cssImports, new HashMap<>()); 448 } 449 450 451 /** 452 * Configure the js class name 453 * @param configuration The configuration on action tag 454 * @return The js class name 455 * @throws ConfigurationException If an error occurs 456 */ 457 protected String _configureClass(Configuration configuration) throws ConfigurationException 458 { 459 String jsClassName = configuration.getAttribute("class", ""); 460 if (getLogger().isDebugEnabled()) 461 { 462 getLogger().debug("Js class configured is '" + jsClassName + "'"); 463 } 464 return jsClassName; 465 } 466 467 /** 468 * Configure the import part 469 * @param configuration The imports configuration 470 * @return The set of the complete url of imported file 471 * @throws ConfigurationException If an error occurs 472 */ 473 protected List<ScriptFile> _configureImports(Configuration configuration) throws ConfigurationException 474 { 475 return ConfigurationHelper.parsePluginResourceList(configuration, getPluginName(), getLogger()); 476 } 477 478 public Collection<ModelItem> getModelItems() 479 { 480 return _modelItems.values(); 481 } 482 483 public Map<String, ModelItem> getParameters() 484 { 485 return _modelItems; 486 } 487 488 public View getView() 489 { 490 return _view; 491 } 492}