001/* 002 * Copyright 2023 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.impl; 017 018import java.util.Collections; 019import java.util.LinkedHashMap; 020import java.util.List; 021import java.util.Map; 022import java.util.Set; 023 024import org.apache.commons.collections.MapUtils; 025 026import org.ametys.cms.search.model.DefaultSearchModel; 027import org.ametys.cms.search.model.LanguageAwareCriterionDefinition; 028import org.ametys.cms.search.model.SearchModelCriterionDefinition; 029import org.ametys.cms.search.model.SearchModelCriterionDefinitionHelper; 030import org.ametys.cms.search.ui.model.SearchModelCriterionViewItem; 031import org.ametys.cms.search.ui.model.SearchUIModel; 032import org.ametys.runtime.i18n.I18nizableText; 033import org.ametys.runtime.model.DefinitionContext; 034import org.ametys.runtime.model.ElementDefinition; 035import org.ametys.runtime.model.ModelViewItem; 036import org.ametys.runtime.model.View; 037import org.ametys.runtime.model.ViewHelper; 038import org.ametys.runtime.model.ViewItem; 039import org.ametys.runtime.model.ViewItemAccessor; 040import org.ametys.runtime.model.ViewItemContainer; 041 042/** 043 * Default implementation of a {@link SearchUIModel}. 044 */ 045public class DefaultSearchUIModel extends DefaultSearchModel implements SearchUIModel 046{ 047 /** The default plugin name for URLs */ 048 protected static final String __DEFAULT_URL_PLUGIN = "cms"; 049 /** The default URL for search */ 050 protected static final String __DEFAULT_SEARCH_URL = "search/list.json"; 051 /** The default URL for CSV export */ 052 protected static final String __DEFAULT_EXPORT_CSV_URL = "search/export.csv"; 053 /** The URL for print results */ 054 protected static final String __DEFAULT_PRINT_URL = "search/print.html"; 055 056 /** The advanced criteria */ 057 private ViewItemContainer _advancedCriteria = new View(); 058 059 /** The page size **/ 060 private int _pageSize = -1; 061 062 /** The URL for search */ 063 private String _searchUrl = __DEFAULT_SEARCH_URL; 064 /** The plugin name for search */ 065 private String _searchUrlPlugin = __DEFAULT_URL_PLUGIN; 066 067 /** The URL for CSV export of results */ 068 private String _exportCSVUrl = __DEFAULT_EXPORT_CSV_URL; 069 /** The plugin name for CSV export of results */ 070 private String _exportCSVUrlPlugin = __DEFAULT_URL_PLUGIN; 071 072 /** The URL for print results */ 073 private String _printUrl = __DEFAULT_PRINT_URL; 074 /** The plugin name for print results */ 075 private String _printUrlPlugin = __DEFAULT_URL_PLUGIN; 076 077 /** The name of the view to use for summary of the content */ 078 private String _summaryView; 079 080 /** Determine is sort is allowed on multiple join */ 081 private boolean _sortOnMultipleJoin; 082 private I18nizableText _label; 083 private I18nizableText _description; 084 private Set<String> _tags = Set.of(); 085 086 /** 087 * Default constructor 088 */ 089 public DefaultSearchUIModel() 090 { 091 super(); 092 } 093 094 /** 095 * Constructor by copying an existing {@link SearchUIModel}. 096 * @param searchUIModelToCopy The {@link SearchUIModel} to copy 097 * @param contextualParameters The contextual parameters 098 */ 099 public DefaultSearchUIModel(SearchUIModel searchUIModelToCopy, Map<String, Object> contextualParameters) 100 { 101 super(searchUIModelToCopy, contextualParameters); 102 103 setLabel(searchUIModelToCopy.getLabel()); 104 setDescription(searchUIModelToCopy.getDescription()); 105 106 setTags(searchUIModelToCopy.getTags()); 107 108 ViewItemContainer advancedCriteriaToCopy = searchUIModelToCopy.getAdvancedCriteria(contextualParameters); 109 if (advancedCriteriaToCopy != null) 110 { 111 View advancedCriteria = new View(); 112 List<ViewItem> advancedCriteriaViewItems = ViewHelper.copyViewItems(advancedCriteriaToCopy.getViewItems()); 113 advancedCriteria.addViewItems(advancedCriteriaViewItems); 114 setAdvancedCriteria(advancedCriteria); 115 } 116 117 setPageSize(searchUIModelToCopy.getPageSize(contextualParameters)); 118 119 setSearchUrl(searchUIModelToCopy.getSearchUrl(contextualParameters)); 120 setSearchUrlPlugin(searchUIModelToCopy.getSearchUrlPlugin(contextualParameters)); 121 122 setExportCSVUrl(searchUIModelToCopy.getExportCSVUrl(contextualParameters)); 123 setExportCSVUrlPlugin(searchUIModelToCopy.getExportCSVUrlPlugin(contextualParameters)); 124 125 setPrintUrl(searchUIModelToCopy.getPrintUrl(contextualParameters)); 126 setPrintUrlPlugin(searchUIModelToCopy.getPrintUrlPlugin(contextualParameters)); 127 } 128 129 public String getName() 130 { 131 return getId(); 132 } 133 134 public I18nizableText getLabel() 135 { 136 return _label; 137 } 138 139 /** 140 * Set the label of the model 141 * @param label the label 142 */ 143 public void setLabel(I18nizableText label) 144 { 145 _label = label; 146 } 147 148 public I18nizableText getDescription() 149 { 150 return _description; 151 } 152 153 /** 154 * Set the description of the model 155 * @param description the description 156 */ 157 public void setDescription(I18nizableText description) 158 { 159 _description = description; 160 } 161 162 public ViewItemContainer getAdvancedCriteria(Map<String, Object> contextualParameters) 163 { 164 return _advancedCriteria; 165 } 166 167 /** 168 * Add criteria to the advanced criteria. The created criteria reference the given paths 169 * @param criterionDefinitionHelper the criterion definition helper 170 * @param contextualParameters the contextual parameters 171 * @param references the paths to the items the criteria will reference 172 */ 173 public void addAdvancedCriteria(SearchModelCriterionDefinitionHelper criterionDefinitionHelper, Map<String, Object> contextualParameters, String... references) 174 { 175 for (String reference : references) 176 { 177 SearchModelCriterionDefinition criterion = criterionDefinitionHelper.createReferencingCriterionDefinition(this, reference, getContentTypes(contextualParameters)); 178 addAdvancedCriterion(criterion, contextualParameters); 179 } 180 } 181 182 /** 183 * Set the advanced criteria 184 * @param criteria The advanced criteria to set 185 */ 186 public void setAdvancedCriteria(ViewItemContainer criteria) 187 { 188 _advancedCriteria = criteria; 189 } 190 191 public int getPageSize(Map<String, Object> contextualParameters) 192 { 193 return _pageSize; 194 } 195 196 /** 197 * Set the page size 198 * @param pageSize The page size to set. Set to negative or 0 for unlimited or default 199 */ 200 public void setPageSize(int pageSize) 201 { 202 _pageSize = pageSize; 203 } 204 205 public String getSearchUrl(Map<String, Object> contextualParameters) 206 { 207 return _searchUrl; 208 } 209 210 /** 211 * Set the URL for search 212 * @param searchUrl The URL to set 213 */ 214 public void setSearchUrl(String searchUrl) 215 { 216 _searchUrl = searchUrl; 217 } 218 219 public String getSearchUrlPlugin(Map<String, Object> contextualParameters) 220 { 221 return _searchUrlPlugin; 222 } 223 224 /** 225 * Set the plugin name for search 226 * @param searchUrlPlugin the plugin name to set 227 */ 228 public void setSearchUrlPlugin(String searchUrlPlugin) 229 { 230 _searchUrlPlugin = searchUrlPlugin; 231 } 232 233 public String getExportCSVUrl(Map<String, Object> contextualParameters) 234 { 235 return _exportCSVUrl; 236 } 237 238 /** 239 * Set the URL for CSV export of results 240 * @param exportCSVUrl The URL to set 241 */ 242 public void setExportCSVUrl(String exportCSVUrl) 243 { 244 _exportCSVUrl = exportCSVUrl; 245 } 246 247 public String getExportCSVUrlPlugin(Map<String, Object> contextualParameters) 248 { 249 return _exportCSVUrlPlugin; 250 } 251 252 /** 253 * Set the plugin name for CSV export of results 254 * @param exportCSVUrlPlugin the plugin name to set 255 */ 256 public void setExportCSVUrlPlugin(String exportCSVUrlPlugin) 257 { 258 _exportCSVUrlPlugin = exportCSVUrlPlugin; 259 } 260 261 public String getPrintUrl(Map<String, Object> contextualParameters) 262 { 263 return _printUrl; 264 } 265 266 /** 267 * Set the URL for print results 268 * @param printUrl The URL to set 269 */ 270 public void setPrintUrl(String printUrl) 271 { 272 _printUrl = printUrl; 273 } 274 275 public String getPrintUrlPlugin(Map<String, Object> contextualParameters) 276 { 277 return _printUrlPlugin; 278 } 279 280 /** 281 * Set the plugin name for print results 282 * @param printUrlPlugin the plugin name to set 283 */ 284 public void setPrintUrlPlugin(String printUrlPlugin) 285 { 286 _printUrlPlugin = printUrlPlugin; 287 } 288 289 public String getSummaryView() 290 { 291 return _summaryView; 292 } 293 294 /** 295 * Set The name of the view to use for summary of the content 296 * @param summaryView the name to set 297 */ 298 public void setSummaryView(String summaryView) 299 { 300 _summaryView = summaryView; 301 } 302 303 /** 304 * Indicates if sorting on join columns which contain at least one {@link ElementDefinition#isMultiple() multiple} {@link ElementDefinition definition} in its path (intermediate ones only) must be allowed. 305 * <br>If the final definition of the path is multiple, the column will not be sortable though. 306 * @return <code>true</code> if sorting on join columns which contains at least one multiple item in its path (intermediate ones only) must be allowed. 307 */ 308 protected boolean allowSortOnMultipleJoin() 309 { 310 return _sortOnMultipleJoin; 311 } 312 313 /** 314 * Set to <code>true</code> to allow sort on multiple join (default to <code>false</code>) 315 * @param sortOnMultipleJoin <code>true</code> to allow sort on multiple join, <code>false</code> otherwise 316 */ 317 public void setAllowSortOnMultipleJoin(boolean sortOnMultipleJoin) 318 { 319 _sortOnMultipleJoin = sortOnMultipleJoin; 320 } 321 322 @Override 323 public Map<String, Object> toJSON(Map<String, Object> contextualParameters) 324 { 325 Map<String, Object> jsonObject = super.toJSON(contextualParameters); 326 327 jsonObject.put("pageSize", getPageSize(contextualParameters)); 328 jsonObject.put("workspace", getWorkspace(contextualParameters)); 329 jsonObject.put("searchUrl", getSearchUrl(contextualParameters)); 330 jsonObject.put("searchUrlPlugin", getSearchUrlPlugin(contextualParameters)); 331 jsonObject.put("exportCSVUrl", getExportCSVUrl(contextualParameters)); 332 jsonObject.put("exportCSVUrlPlugin", getExportCSVUrlPlugin(contextualParameters)); 333 jsonObject.put("printUrl", getPrintUrl(contextualParameters)); 334 jsonObject.put("printUrlPlugin", getPrintUrlPlugin(contextualParameters)); 335 jsonObject.put("summaryView", getSummaryView()); 336 337 jsonObject.put("advanced-criteria", _getAdvancedCriteriaListInfo(getAdvancedCriteria(contextualParameters))); 338 339 return jsonObject; 340 } 341 342 /** 343 * Return information on a list of advanced {@link SearchModelCriterionViewItem}, serialized as a Map. 344 * @param criteria A map of advanced criteria. 345 * @return the detailed information serialized in a Map. 346 */ 347 protected Map<String, Object> _getAdvancedCriteriaListInfo(ViewItemAccessor criteria) 348 { 349 Map<String, Object> jsonObject = new LinkedHashMap<>(); 350 351 if (criteria != null) 352 { 353 // Make a copy of the criteria view 354 ViewItemAccessor criteriaCopy = new View(); 355 criteriaCopy.addViewItems(ViewHelper.copyViewItems(criteria.getViewItems())); 356 357 // Extract language criterion from the others 358 ModelViewItem languageCriterion = _getContentLanguageCriterionViewItem(criteriaCopy); 359 if (languageCriterion != null) 360 { 361 jsonObject.put("language", languageCriterion.toJSON(DefinitionContext.newInstance())); 362 363 // Remove language criterion that is already processed 364 ViewItemAccessor languageParent = languageCriterion.getParent(); 365 languageParent.removeViewItem(languageCriterion); 366 } 367 368 Map<String, Object> criteriaObject = ViewHelper.viewItemsToJSON(criteriaCopy.getViewItems(), DefinitionContext.newInstance()); 369 if (MapUtils.isNotEmpty(criteriaObject)) 370 { 371 jsonObject.put("criteria", criteriaObject); 372 } 373 } 374 375 return jsonObject; 376 } 377 378 private ModelViewItem _getContentLanguageCriterionViewItem(ViewItemAccessor criteria) 379 { 380 for (ViewItem viewItem : criteria.getViewItems()) 381 { 382 if (viewItem instanceof SearchModelCriterionViewItem criterionViewItem 383 && criterionViewItem.getDefinition() instanceof LanguageAwareCriterionDefinition) 384 { 385 return criterionViewItem; 386 } 387 else if (viewItem instanceof ViewItemContainer itemContainer) 388 { 389 ModelViewItem contentLanguageCriterion = _getContentLanguageCriterionViewItem(itemContainer); 390 if (contentLanguageCriterion != null) 391 { 392 return contentLanguageCriterion; 393 } 394 } 395 } 396 397 // No criterion on contentLanguage has been found 398 return null; 399 } 400 401 /** 402 * Set the tags of this search model 403 * @param tags the set of tags to set 404 */ 405 public void setTags(Set<String> tags) 406 { 407 _tags = tags; 408 } 409 410 @Override 411 public boolean hasTag(String tagName) 412 { 413 return _tags.contains(tagName); 414 } 415 416 @Override 417 public Set<String> getTags() 418 { 419 return Collections.unmodifiableSet(_tags); 420 } 421}