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