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("strictContentTypes"); 131 cTypeConf.setValue(StringUtils.join(_restrictedContentTypes, ",")); 132 customEnumerator.addChild(cTypeConf); 133 134 DefaultConfiguration excludeConf = new DefaultConfiguration("excludeMixin"); 135 excludeConf.setValue(true); 136 customEnumerator.addChild(excludeConf); 137 138 enumConf.addChild(customEnumerator); 139 conf.addChild(enumConf); 140 141 _enumeratorManager.addComponent("cms", null, ContentTypeEnumerator.class.getName(), ContentTypeEnumerator.class, conf); 142 } 143 } 144 145 private void _addContentTypeAndMixinEnumeratorComponent() 146 { 147 DefaultConfiguration conf = new DefaultConfiguration("criteria"); 148 149 DefaultConfiguration enumConf = new DefaultConfiguration("enumeration"); 150 151 DefaultConfiguration customEnumerator = new DefaultConfiguration("custom-enumerator"); 152 customEnumerator.setAttribute("class", ContentTypeEnumerator.class.getName()); 153 154 if (_restrictedContentTypes.size() > 0) 155 { 156 DefaultConfiguration cTypeConf = new DefaultConfiguration("strictContentTypes"); 157 cTypeConf.setValue(StringUtils.join(_restrictedContentTypes, ",")); 158 customEnumerator.addChild(cTypeConf); 159 160 enumConf.addChild(customEnumerator); 161 conf.addChild(enumConf); 162 163 _enumeratorManager.addComponent("cms", null, ContentTypeEnumerator.class.getName() + ".ContentTypeAndMixin", ContentTypeEnumerator.class, conf); 164 } 165 } 166 167 private void _addMixinEnumeratorComponent() 168 { 169 DefaultConfiguration conf = new DefaultConfiguration("criteria"); 170 171 DefaultConfiguration enumConf = new DefaultConfiguration("enumeration"); 172 173 DefaultConfiguration customEnumerator = new DefaultConfiguration("custom-enumerator"); 174 customEnumerator.setAttribute("class", ContentTypeEnumerator.class.getName()); 175 176 if (_restrictedMixins.size() > 0) 177 { 178 DefaultConfiguration cTypeConf = new DefaultConfiguration("strictContentTypes"); 179 cTypeConf.setValue(StringUtils.join(_restrictedMixins, ",")); 180 customEnumerator.addChild(cTypeConf); 181 182 DefaultConfiguration excludeConf = new DefaultConfiguration("excludeMixin"); 183 excludeConf.setValue(false); 184 customEnumerator.addChild(excludeConf); 185 186 enumConf.addChild(customEnumerator); 187 conf.addChild(enumConf); 188 189 _enumeratorManager.addComponent("cms", null, ContentTypeEnumerator.class.getName() + ".Mixin", ContentTypeEnumerator.class, conf); 190 } 191 } 192 193 @Override 194 public Set<String> getContentTypes(Map<String, Object> contextualParameters) 195 { 196 return _restrictedContentTypes; 197 } 198 199 @Override 200 public Set<String> getExcludedContentTypes(Map<String, Object> contextualParameters) 201 { 202 return Collections.<String>emptySet(); 203 } 204 205 @Override 206 public String getSearchUrl(Map<String, Object> contextualParameters) 207 { 208 return _wrappedModel.getSearchUrl(contextualParameters); 209 } 210 211 @Override 212 public String getSearchUrlPlugin(Map<String, Object> contextualParameters) 213 { 214 return _wrappedModel.getSearchUrlPlugin(contextualParameters); 215 } 216 217 @Override 218 public String getExportCSVUrl(Map<String, Object> contextualParameters) 219 { 220 return _wrappedModel.getExportCSVUrl(contextualParameters); 221 } 222 223 @Override 224 public String getExportCSVUrlPlugin(Map<String, Object> contextualParameters) 225 { 226 return _wrappedModel.getExportCSVUrlPlugin(contextualParameters); 227 } 228 229 @Override 230 public String getExportDOCUrl(Map<String, Object> contextualParameters) 231 { 232 return _wrappedModel.getExportDOCUrl(contextualParameters); 233 } 234 235 @Override 236 public String getExportDOCUrlPlugin(Map<String, Object> contextualParameters) 237 { 238 return _wrappedModel.getExportDOCUrlPlugin(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 String getSummaryView() 267 { 268 return _wrappedModel.getSummaryView(); 269 } 270 271 @Override 272 public Map<String, SearchUICriterion> getCriteria(Map<String, Object> contextualParameters) 273 { 274 Map<String, SearchUICriterion> criteria = _wrappedModel.getCriteria(contextualParameters); 275 276 Map<String, SearchUICriterion> wrappedCriteria = new LinkedHashMap<>(criteria.size()); 277 278 for (Entry<String, SearchUICriterion> entry : criteria.entrySet()) 279 { 280 SearchUICriterion sc = entry.getValue(); 281 wrappedCriteria.put(entry.getKey(), wrap(sc)); 282 } 283 284 return wrappedCriteria; 285 } 286 287 @Override 288 public SearchUICriterion getCriterion(String id, Map<String, Object> contextualParameters) 289 { 290 SearchUICriterion criteria = _wrappedModel.getCriterion(id, contextualParameters); 291 292 return wrap(criteria); 293 } 294 295 @Override 296 public Map<String, SearchUICriterion> getFacetedCriteria(Map<String, Object> contextualParameters) 297 { 298 return _wrappedModel.getFacetedCriteria(contextualParameters); 299 } 300 301 @Override 302 public Map<String, SearchUICriterion> getAdvancedCriteria(Map<String, Object> contextualParameters) 303 { 304 return _wrappedModel.getAdvancedCriteria(contextualParameters); 305 } 306 307 @Override 308 public Map<String, SearchUIColumn> getResultFields(Map<String, Object> contextualParameters) 309 { 310 return _wrappedModel.getResultFields(contextualParameters); 311 } 312 313 @Override 314 public SearchUIColumn getResultField(String id, Map<String, Object> contextualParameters) 315 { 316 return _wrappedModel.getResultField(id, contextualParameters); 317 } 318 319 @Override 320 public int getPageSize(Map<String, Object> contextualParameters) 321 { 322 return _wrappedModel.getPageSize(contextualParameters); 323 } 324 325 @Override 326 public String getWorkspace(Map<String, Object> contextualParameters) 327 { 328 return _wrappedModel.getWorkspace(contextualParameters); 329 } 330 331 /** 332 * Wrap an existing search criteria. 333 * @param criteria the search criteria to wrap. 334 * @return the wrapped search criteria. 335 */ 336 protected SearchUICriterion wrap(SearchUICriterion criteria) 337 { 338 try 339 { 340 if (criteria instanceof SystemSearchUICriterion && !_restrictedContentTypes.isEmpty()) 341 { 342 SystemSearchUICriterion systemCriterion = (SystemSearchUICriterion) criteria; 343 344 // Wrap "contentTypes" and "contentTypeOrMixin" system criteria, replacing their standard enumerator with the local one. 345 Enumerator enumerator; 346 switch (systemCriterion.getSystemPropertyId()) 347 { 348 case "contentTypes": 349 enumerator = _enumeratorManager.lookup(ContentTypeEnumerator.class.getName()); 350 return new WrappedSearchUICriterion(systemCriterion, enumerator); 351 case "contentTypeOrMixin": 352 enumerator = _enumeratorManager.lookup(ContentTypeEnumerator.class.getName() + ".ContentTypeAndMixin"); 353 return new WrappedSearchUICriterion(systemCriterion, enumerator); 354 case "mixins": 355 enumerator = _enumeratorManager.lookup(ContentTypeEnumerator.class.getName() + ".Mixin"); 356 return new WrappedSearchUICriterion(systemCriterion, enumerator); 357 default: 358 break; 359 } 360 } 361 362 return criteria; 363 } 364 catch (Exception e) 365 { 366 _logger.error("Unable to lookup enumerator role.", e); 367 throw new RuntimeException("Unable to lookup enumerator role.", e); 368 } 369 } 370 371 /** 372 * Search criteria wrapper. 373 */ 374 protected class WrappedSearchUICriterion extends AbstractSearchUICriterion 375 { 376 377 /** The wrapped search criteria. */ 378 protected SearchUICriterion _wrappedCriterion; 379 380 /** 381 * Build a search criteria wrapper. 382 * @param wrappedCriteria wrapped search criteria. 383 * @param enumerator the new enumerator. 384 */ 385 public WrappedSearchUICriterion(SearchUICriterion wrappedCriteria, Enumerator enumerator) 386 { 387 _wrappedCriterion = wrappedCriteria; 388 setEnumerator(enumerator); 389 } 390 391 @Override 392 public Enumerator getEnumerator() 393 { 394 return super.getEnumerator(); 395 } 396 397 @Override 398 public String getId() 399 { 400 return _wrappedCriterion.getId(); 401 } 402 403 @Override 404 public I18nizableText getLabel() 405 { 406 return _wrappedCriterion.getLabel(); 407 } 408 409 @Override 410 public I18nizableText getDescription() 411 { 412 return _wrappedCriterion.getDescription(); 413 } 414 415 @Override 416 public String getFieldId() 417 { 418 return _wrappedCriterion.getFieldId(); 419 } 420 421 @Override 422 public String getInitClassName() 423 { 424 return _wrappedCriterion.getInitClassName(); 425 } 426 427 @Override 428 public String getSubmitClassName() 429 { 430 return _wrappedCriterion.getSubmitClassName(); 431 } 432 433 @Override 434 public String getChangeClassName() 435 { 436 return _wrappedCriterion.getChangeClassName(); 437 } 438 439 @Override 440 public boolean isHidden() 441 { 442 return _wrappedCriterion.isHidden(); 443 } 444 445 @Override 446 public MetadataType getType() 447 { 448 return _wrappedCriterion.getType(); 449 } 450 451 @Override 452 public boolean isMultiple() 453 { 454 return _wrappedCriterion.isMultiple(); 455 } 456 457 @Override 458 public String getWidget() 459 { 460 return _wrappedCriterion.getWidget(); 461 } 462 463// @Override 464// public Object getValue() 465// { 466// return _wrappedCriterion.getValue(); 467// } 468 469 @Override 470 public Map<String, I18nizableText> getWidgetParameters() 471 { 472 return _wrappedCriterion.getWidgetParameters(); 473 } 474 475 @Override 476 public Validator getValidator() 477 { 478 return _wrappedCriterion.getValidator(); 479 } 480 481 @Override 482 public Object getDefaultValue() 483 { 484 return _wrappedCriterion.getDefaultValue(); 485 } 486 487 @Override 488 public String getContentTypeId() 489 { 490 return _wrappedCriterion.getContentTypeId(); 491 } 492 493 @Override 494 public Operator getOperator() 495 { 496 return _wrappedCriterion.getOperator(); 497 } 498 499 @Override 500 public Query getQuery(Object value, Operator customOperator, Map<String, Object> allValues, String language, Map<String, Object> contextualParameters) 501 { 502 return _wrappedCriterion.getQuery(value, customOperator, allValues, language, contextualParameters); 503 } 504 505 } 506 507}