001/* 002 * Copyright 2013 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.cms.search.ui.model; 017 018import java.util.Collection; 019import java.util.Collections; 020import java.util.HashSet; 021import java.util.LinkedHashMap; 022import java.util.Map; 023import java.util.Map.Entry; 024import java.util.Set; 025 026import org.apache.avalon.framework.configuration.DefaultConfiguration; 027import org.apache.avalon.framework.context.Context; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.commons.lang3.StringUtils; 030import org.slf4j.Logger; 031 032import org.ametys.cms.contenttype.ContentTypeEnumerator; 033import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 034import org.ametys.cms.contenttype.MetadataType; 035import org.ametys.cms.search.query.Query; 036import org.ametys.cms.search.query.Query.Operator; 037import org.ametys.cms.search.ui.model.impl.AbstractSearchUICriterion; 038import org.ametys.cms.search.ui.model.impl.SystemSearchUICriterion; 039import org.ametys.runtime.i18n.I18nizableText; 040import org.ametys.runtime.parameter.Enumerator; 041import org.ametys.runtime.parameter.Validator; 042import org.ametys.runtime.plugin.component.ThreadSafeComponentManager; 043 044/** 045 * Dynamic search model wrapping a search model with a restriction on content types 046 * 047 */ 048public class DynamicWrappedSearchUIModel implements SearchUIModel 049{ 050 private ThreadSafeComponentManager<Enumerator> _enumeratorManager; 051 052 private Logger _logger; 053 private SearchUIModel _wrappedModel; 054 private Set<String> _restrictedContentTypes; 055 private Set<String> _restrictedMixins; 056 057 /** 058 * Constructor 059 * @param model The wrapped search model 060 * @param restrictedContentTypes The restricted content types 061 * @param cTypeEP The content type extension point 062 * @param logger The logger 063 * @param context The context 064 * @param manager The service manager 065 */ 066 public DynamicWrappedSearchUIModel(SearchUIModel model, Collection<String> restrictedContentTypes, ContentTypeExtensionPoint cTypeEP, Logger logger, Context context, ServiceManager manager) 067 { 068 _wrappedModel = model; 069 070 _restrictedContentTypes = new HashSet<>(); 071 _restrictedMixins = new HashSet<>(); 072 073 for (String cTypeId : restrictedContentTypes) 074 { 075 boolean mixin = cTypeEP.getExtension(cTypeId).isMixin(); 076 if (mixin) 077 { 078 _restrictedMixins.add(cTypeId); 079 } 080 else 081 { 082 _restrictedContentTypes.add(cTypeId); 083 } 084 085 for (String subTypeId : cTypeEP.getSubTypes(cTypeId)) 086 { 087 mixin = cTypeEP.getExtension(subTypeId).isMixin(); 088 if (mixin) 089 { 090 _restrictedMixins.add(subTypeId); 091 } 092 else 093 { 094 _restrictedContentTypes.add(subTypeId); 095 } 096 } 097 } 098 099 try 100 { 101 _logger = logger; 102 _enumeratorManager = new ThreadSafeComponentManager<>(); 103 _enumeratorManager.setLogger(logger); 104 _enumeratorManager.contextualize(context); 105 _enumeratorManager.service(manager); 106 107 _addContentTypeEnumeratorComponent(); 108 _addContentTypeAndMixinEnumeratorComponent(); 109 _addMixinEnumeratorComponent(); 110 111 _enumeratorManager.initialize(); 112 } 113 catch (Exception e) 114 { 115 _logger.error("Unable to instantiate enumerator for class: " + ContentTypeEnumerator.class.getName(), e); 116 } 117 } 118 119 private void _addContentTypeEnumeratorComponent() 120 { 121 DefaultConfiguration conf = new DefaultConfiguration("criteria"); 122 123 DefaultConfiguration enumConf = new DefaultConfiguration("enumeration"); 124 125 DefaultConfiguration customEnumerator = new DefaultConfiguration("custom-enumerator"); 126 customEnumerator.setAttribute("class", ContentTypeEnumerator.class.getName()); 127 128 if (!_restrictedContentTypes.isEmpty()) 129 { 130 DefaultConfiguration cTypeConf = new DefaultConfiguration("strict-content-types"); 131 cTypeConf.setValue(StringUtils.join(_restrictedContentTypes, ",")); 132 customEnumerator.addChild(cTypeConf); 133 134 DefaultConfiguration allOptConf = new DefaultConfiguration("all-option"); 135 allOptConf.setValue("concat"); 136 customEnumerator.addChild(allOptConf); 137 138 DefaultConfiguration excludeConf = new DefaultConfiguration("exclude-mixin"); 139 excludeConf.setValue(true); 140 customEnumerator.addChild(excludeConf); 141 142 enumConf.addChild(customEnumerator); 143 conf.addChild(enumConf); 144 145 _enumeratorManager.addComponent("cms", null, ContentTypeEnumerator.class.getName(), ContentTypeEnumerator.class, conf); 146 } 147 } 148 149 private void _addContentTypeAndMixinEnumeratorComponent() 150 { 151 DefaultConfiguration conf = new DefaultConfiguration("criteria"); 152 153 DefaultConfiguration enumConf = new DefaultConfiguration("enumeration"); 154 155 DefaultConfiguration customEnumerator = new DefaultConfiguration("custom-enumerator"); 156 customEnumerator.setAttribute("class", ContentTypeEnumerator.class.getName()); 157 158 if (_restrictedContentTypes.size() > 0) 159 { 160 DefaultConfiguration cTypeConf = new DefaultConfiguration("strict-content-types"); 161 cTypeConf.setValue(StringUtils.join(_restrictedContentTypes, ",")); 162 customEnumerator.addChild(cTypeConf); 163 164 DefaultConfiguration allOptConf = new DefaultConfiguration("all-option"); 165 allOptConf.setValue("concat"); 166 customEnumerator.addChild(allOptConf); 167 168 enumConf.addChild(customEnumerator); 169 conf.addChild(enumConf); 170 171 _enumeratorManager.addComponent("cms", null, ContentTypeEnumerator.class.getName() + ".ContentTypeAndMixin", ContentTypeEnumerator.class, conf); 172 } 173 } 174 175 private void _addMixinEnumeratorComponent() 176 { 177 DefaultConfiguration conf = new DefaultConfiguration("criteria"); 178 179 DefaultConfiguration enumConf = new DefaultConfiguration("enumeration"); 180 181 DefaultConfiguration customEnumerator = new DefaultConfiguration("custom-enumerator"); 182 customEnumerator.setAttribute("class", ContentTypeEnumerator.class.getName()); 183 184 if (_restrictedMixins.size() > 0) 185 { 186 DefaultConfiguration cTypeConf = new DefaultConfiguration("strict-content-types"); 187 cTypeConf.setValue(StringUtils.join(_restrictedMixins, ",")); 188 customEnumerator.addChild(cTypeConf); 189 190 DefaultConfiguration allOptConf = new DefaultConfiguration("all-option"); 191 allOptConf.setValue("concat"); 192 customEnumerator.addChild(allOptConf); 193 194 DefaultConfiguration excludeConf = new DefaultConfiguration("exclude-mixin"); 195 excludeConf.setValue(false); 196 customEnumerator.addChild(excludeConf); 197 198 enumConf.addChild(customEnumerator); 199 conf.addChild(enumConf); 200 201 _enumeratorManager.addComponent("cms", null, ContentTypeEnumerator.class.getName() + ".Mixin", ContentTypeEnumerator.class, conf); 202 } 203 } 204 205 @Override 206 public Set<String> getContentTypes(Map<String, Object> contextualParameters) 207 { 208 return _restrictedContentTypes; 209 } 210 211 @Override 212 public Set<String> getExcludedContentTypes(Map<String, Object> contextualParameters) 213 { 214 return Collections.<String>emptySet(); 215 } 216 217 @Override 218 public String getSearchUrl(Map<String, Object> contextualParameters) 219 { 220 return _wrappedModel.getSearchUrl(contextualParameters); 221 } 222 223 @Override 224 public String getSearchUrlPlugin(Map<String, Object> contextualParameters) 225 { 226 return _wrappedModel.getSearchUrlPlugin(contextualParameters); 227 } 228 229 @Override 230 public String getExportCSVUrl(Map<String, Object> contextualParameters) 231 { 232 return _wrappedModel.getExportCSVUrl(contextualParameters); 233 } 234 235 @Override 236 public String getExportCSVUrlPlugin(Map<String, Object> contextualParameters) 237 { 238 return _wrappedModel.getExportCSVUrlPlugin(contextualParameters); 239 } 240 241 @Override 242 public String getExportXMLUrl(Map<String, Object> contextualParameters) 243 { 244 return _wrappedModel.getExportXMLUrl(contextualParameters); 245 } 246 247 @Override 248 public String getExportXMLUrlPlugin(Map<String, Object> contextualParameters) 249 { 250 return _wrappedModel.getExportXMLUrlPlugin(contextualParameters); 251 } 252 253 @Override 254 public String getPrintUrl(Map<String, Object> contextualParameters) 255 { 256 return _wrappedModel.getPrintUrl(contextualParameters); 257 } 258 259 @Override 260 public String getPrintUrlPlugin(Map<String, Object> contextualParameters) 261 { 262 return _wrappedModel.getPrintUrlPlugin(contextualParameters); 263 } 264 265 @Override 266 public Map<String, SearchUICriterion> getCriteria(Map<String, Object> contextualParameters) 267 { 268 Map<String, SearchUICriterion> criteria = _wrappedModel.getCriteria(contextualParameters); 269 270 Map<String, SearchUICriterion> wrappedCriteria = new LinkedHashMap<>(criteria.size()); 271 272 for (Entry<String, SearchUICriterion> entry : criteria.entrySet()) 273 { 274 SearchUICriterion sc = entry.getValue(); 275 wrappedCriteria.put(entry.getKey(), wrap(sc)); 276 } 277 278 return wrappedCriteria; 279 } 280 281 @Override 282 public SearchUICriterion getCriterion(String id, Map<String, Object> contextualParameters) 283 { 284 SearchUICriterion criteria = _wrappedModel.getCriterion(id, contextualParameters); 285 286 return wrap(criteria); 287 } 288 289 @Override 290 public Map<String, SearchUICriterion> getFacetedCriteria(Map<String, Object> contextualParameters) 291 { 292 return _wrappedModel.getFacetedCriteria(contextualParameters); 293 } 294 295 @Override 296 public Map<String, SearchUICriterion> getAdvancedCriteria(Map<String, Object> contextualParameters) 297 { 298 return _wrappedModel.getAdvancedCriteria(contextualParameters); 299 } 300 301 @Override 302 public Map<String, SearchUIColumn> getResultFields(Map<String, Object> contextualParameters) 303 { 304 return _wrappedModel.getResultFields(contextualParameters); 305 } 306 307 @Override 308 public SearchUIColumn getResultField(String id, Map<String, Object> contextualParameters) 309 { 310 return _wrappedModel.getResultField(id, contextualParameters); 311 } 312 313 @Override 314 public int getPageSize(Map<String, Object> contextualParameters) 315 { 316 return _wrappedModel.getPageSize(contextualParameters); 317 } 318 319 @Override 320 public String getWorkspace(Map<String, Object> contextualParameters) 321 { 322 return _wrappedModel.getWorkspace(contextualParameters); 323 } 324 325 /** 326 * Wrap an existing search criteria. 327 * @param criteria the search criteria to wrap. 328 * @return the wrapped search criteria. 329 */ 330 protected SearchUICriterion wrap(SearchUICriterion criteria) 331 { 332 try 333 { 334 if (criteria instanceof SystemSearchUICriterion && !_restrictedContentTypes.isEmpty()) 335 { 336 SystemSearchUICriterion systemCriterion = (SystemSearchUICriterion) criteria; 337 338 // Wrap "contentTypes" and "contentTypeOrMixin" system criteria, replacing their standard enumerator with the local one. 339 Enumerator enumerator; 340 switch (systemCriterion.getSystemPropertyId()) 341 { 342 case "contentTypes": 343 enumerator = _enumeratorManager.lookup(ContentTypeEnumerator.class.getName()); 344 return new WrappedSearchUICriterion(systemCriterion, enumerator); 345 case "contentTypeOrMixin": 346 enumerator = _enumeratorManager.lookup(ContentTypeEnumerator.class.getName() + ".ContentTypeAndMixin"); 347 return new WrappedSearchUICriterion(systemCriterion, enumerator); 348 case "mixins": 349 enumerator = _enumeratorManager.lookup(ContentTypeEnumerator.class.getName() + ".Mixin"); 350 return new WrappedSearchUICriterion(systemCriterion, enumerator); 351 default: 352 break; 353 } 354 } 355 356 return criteria; 357 } 358 catch (Exception e) 359 { 360 _logger.error("Unable to lookup enumerator role.", e); 361 throw new RuntimeException("Unable to lookup enumerator role.", e); 362 } 363 } 364 365 /** 366 * Search criteria wrapper. 367 */ 368 protected class WrappedSearchUICriterion extends AbstractSearchUICriterion 369 { 370 371 /** The wrapped search criteria. */ 372 protected SearchUICriterion _wrappedCriterion; 373 374 /** 375 * Build a search criteria wrapper. 376 * @param wrappedCriteria wrapped search criteria. 377 * @param enumerator the new enumerator. 378 */ 379 public WrappedSearchUICriterion(SearchUICriterion wrappedCriteria, Enumerator enumerator) 380 { 381 _wrappedCriterion = wrappedCriteria; 382 setEnumerator(enumerator); 383 } 384 385 @Override 386 public Enumerator getEnumerator() 387 { 388 return super.getEnumerator(); 389 } 390 391 @Override 392 public String getId() 393 { 394 return _wrappedCriterion.getId(); 395 } 396 397 @Override 398 public I18nizableText getLabel() 399 { 400 return _wrappedCriterion.getLabel(); 401 } 402 403 @Override 404 public I18nizableText getDescription() 405 { 406 return _wrappedCriterion.getDescription(); 407 } 408 409 @Override 410 public String getFieldId() 411 { 412 return _wrappedCriterion.getFieldId(); 413 } 414 415 @Override 416 public String getInitClassName() 417 { 418 return _wrappedCriterion.getInitClassName(); 419 } 420 421 @Override 422 public String getSubmitClassName() 423 { 424 return _wrappedCriterion.getSubmitClassName(); 425 } 426 427 @Override 428 public String getChangeClassName() 429 { 430 return _wrappedCriterion.getChangeClassName(); 431 } 432 433 @Override 434 public boolean isHidden() 435 { 436 return _wrappedCriterion.isHidden(); 437 } 438 439 @Override 440 public MetadataType getType() 441 { 442 return _wrappedCriterion.getType(); 443 } 444 445 @Override 446 public boolean isMultiple() 447 { 448 return _wrappedCriterion.isMultiple(); 449 } 450 451 @Override 452 public String getWidget() 453 { 454 return _wrappedCriterion.getWidget(); 455 } 456 457// @Override 458// public Object getValue() 459// { 460// return _wrappedCriterion.getValue(); 461// } 462 463 @Override 464 public Map<String, I18nizableText> getWidgetParameters() 465 { 466 return _wrappedCriterion.getWidgetParameters(); 467 } 468 469 @Override 470 public Validator getValidator() 471 { 472 return _wrappedCriterion.getValidator(); 473 } 474 475 @Override 476 public Object getDefaultValue() 477 { 478 return _wrappedCriterion.getDefaultValue(); 479 } 480 481 @Override 482 public String getContentTypeId() 483 { 484 return _wrappedCriterion.getContentTypeId(); 485 } 486 487 @Override 488 public Operator getOperator() 489 { 490 return _wrappedCriterion.getOperator(); 491 } 492 493 @Override 494 public Query getQuery(Object value, Operator customOperator, Map<String, Object> allValues, String language, Map<String, Object> contextualParameters) 495 { 496 return _wrappedCriterion.getQuery(value, customOperator, allValues, language, contextualParameters); 497 } 498 499 } 500 501}