001/* 002 * Copyright 2018 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.content.referencetable.search; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.Collections; 021import java.util.List; 022import java.util.Map; 023import java.util.Set; 024 025import org.apache.avalon.framework.service.ServiceException; 026import org.apache.avalon.framework.service.ServiceManager; 027import org.apache.avalon.framework.service.Serviceable; 028import org.apache.commons.lang3.tuple.ImmutablePair; 029import org.apache.commons.lang3.tuple.Pair; 030 031import org.ametys.cms.content.ContentHelper; 032import org.ametys.cms.contenttype.ContentType; 033import org.ametys.cms.repository.Content; 034import org.ametys.cms.search.model.SearchModelCriterionDefinition; 035import org.ametys.cms.search.model.SystemPropertyExtensionPoint; 036import org.ametys.cms.search.query.Query.LogicalOperator; 037import org.ametys.cms.search.query.Query.Operator; 038import org.ametys.cms.search.ui.model.SearchUIColumn; 039import org.ametys.cms.search.ui.model.SearchUIColumnHelper; 040import org.ametys.cms.search.ui.model.SearchModelCriterionViewItem; 041import org.ametys.cms.search.ui.model.SearchUIModel; 042import org.ametys.cms.search.ui.model.SearchModelCriterionViewItemHelper; 043import org.ametys.cms.search.ui.model.impl.DefaultSearchUIModel; 044import org.ametys.plugins.repository.AmetysObjectResolver; 045import org.ametys.runtime.model.ModelHelper; 046import org.ametys.runtime.model.ModelItem; 047import org.ametys.runtime.model.View; 048import org.ametys.runtime.model.ViewHelper; 049import org.ametys.runtime.model.ViewItemAccessor; 050import org.ametys.runtime.model.ViewItemContainer; 051 052/** 053 * Implementation of {@link SearchUIModel} for contents referencing the same content's values for a given attribute. 054 */ 055public class ReferencingContentsWithSameValuesSearchUIModel extends DefaultSearchUIModel implements Serviceable 056{ 057 private static final String __CONTENT_ID_CONTEXTUAL_PARAM_NAME = "contentId"; 058 private static final String __REFERENCE_FIELD_CONTEXTUAL_PARAM_NAME = "referenceField"; 059 060 private AmetysObjectResolver _resolver; 061 private ContentHelper _contentHelper; 062 private SystemPropertyExtensionPoint _systemPropertyExtensionPoint; 063 private SearchModelCriterionViewItemHelper _searchModelCriterionViewItemHelper; 064 065 public void service(ServiceManager manager) throws ServiceException 066 { 067 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 068 _contentHelper = (ContentHelper) manager.lookup(ContentHelper.ROLE); 069 _systemPropertyExtensionPoint = (SystemPropertyExtensionPoint) manager.lookup(SystemPropertyExtensionPoint.ROLE); 070 _searchModelCriterionViewItemHelper = (SearchModelCriterionViewItemHelper) manager.lookup(SearchModelCriterionViewItemHelper.ROLE); 071 } 072 073 @Override 074 public Set<String> getContentTypes(Map<String, Object> contextualParameters) 075 { 076 if (contextualParameters != null && contextualParameters.containsKey(__CONTENT_ID_CONTEXTUAL_PARAM_NAME) && contextualParameters.containsKey(__REFERENCE_FIELD_CONTEXTUAL_PARAM_NAME)) 077 { 078 // Get the content type which has declared the attribute 079 String contentId = (String) contextualParameters.get(__CONTENT_ID_CONTEXTUAL_PARAM_NAME); 080 Content content = _resolver.resolveById(contentId); 081 082 String referenceAttributePath = (String) contextualParameters.get(__REFERENCE_FIELD_CONTEXTUAL_PARAM_NAME); 083 if (content.hasDefinition(referenceAttributePath)) 084 { 085 ModelItem modelItem = content.getDefinition(referenceAttributePath); 086 return Set.of(modelItem.getModel().getId()); 087 } 088 } 089 090 return Set.of(); 091 } 092 093 @Override 094 public Set<String> getExcludedContentTypes(Map<String, Object> contextualParameters) 095 { 096 return Collections.emptySet(); 097 } 098 099 @SuppressWarnings("unchecked") 100 @Override 101 public ViewItemContainer getCriteria(Map<String, Object> contextualParameters) 102 { 103 ViewItemContainer criteria = new View(); 104 105 if (contextualParameters.containsKey(__CONTENT_ID_CONTEXTUAL_PARAM_NAME)) 106 { 107 String contentId = (String) contextualParameters.get(__CONTENT_ID_CONTEXTUAL_PARAM_NAME); 108 Content content = _resolver.resolveById(contentId); 109 Collection<ContentType> contentTypes = content.getModel() 110 .stream() 111 .map(ContentType.class::cast) 112 .toList(); 113 114 SearchModelCriterionViewItem titleCriterionViewItem = _searchModelCriterionViewItemHelper.createReferencingCriterionViewItem(this, Content.ATTRIBUTE_TITLE, contentTypes); 115 SearchModelCriterionDefinition titleCriterion = (SearchModelCriterionDefinition) titleCriterionViewItem.getDefinition(); 116 titleCriterion.setOperator(Operator.SEARCH); 117 criteria.addViewItem(titleCriterionViewItem); 118 119 SearchModelCriterionViewItem fullCriterionViewItem = _searchModelCriterionViewItemHelper.createReferencingCriterionViewItem(this, "full"); 120 criteria.addViewItem(fullCriterionViewItem); 121 122 SearchModelCriterionViewItem workflowStepCriterionViewItem = _searchModelCriterionViewItemHelper.createReferencingCriterionViewItem(this, "workflowStep"); 123 criteria.addViewItem(workflowStepCriterionViewItem); 124 125 if (contextualParameters.containsKey(__REFERENCE_FIELD_CONTEXTUAL_PARAM_NAME) && contextualParameters.containsKey("referenceValues")) 126 { 127 String referencePath = ((String) contextualParameters.get(__REFERENCE_FIELD_CONTEXTUAL_PARAM_NAME)).replaceAll("\\.", "/"); 128 129 SearchModelCriterionViewItem referenceCriterionViewItem = _searchModelCriterionViewItemHelper.createReferencingCriterionViewItem(this, referencePath, contentTypes); 130 if (referenceCriterionViewItem != null) 131 { 132 criteria.addViewItem(referenceCriterionViewItem); 133 SearchModelCriterionDefinition referenceCriterion = (SearchModelCriterionDefinition) referenceCriterionViewItem.getDefinition(); 134 referenceCriterion.setMultiple(true); 135 136 // Default value(s) 137 List<String> referenceAttributeValues = (List<String>) contextualParameters.get("referenceValues"); 138 List<Pair<String, Object>> defaultValues = new ArrayList<>(); 139 for (String referenceAttributeValue : referenceAttributeValues) 140 { 141 defaultValues.add(new ImmutablePair<>(null, referenceAttributeValue)); 142 } 143 referenceCriterion.setParsedDefaultValues(defaultValues); 144 145 // AND query on multiple entries 146 referenceCriterion.setMultipleOperandOperator(LogicalOperator.AND); 147 148 // Hidden 149 referenceCriterionViewItem.setHidden(true); 150 } 151 } 152 } 153 154 setCriteria(criteria); 155 return super.getCriteria(contextualParameters); 156 } 157 158 @Override 159 public ViewItemContainer getAdvancedCriteria(Map<String, Object> contextualParameters) 160 { 161 return null; 162 } 163 164 @Override 165 public ViewItemContainer getFacetedCriteria(Map<String, Object> contextualParameters) 166 { 167 ViewItemContainer criteria = new View(); 168 169 if (contextualParameters.containsKey(__CONTENT_ID_CONTEXTUAL_PARAM_NAME)) 170 { 171 for (String propertyId : new String[] {"contentTypes", "workflowStep", "contributor", "creator"}) 172 { 173 SearchModelCriterionViewItem criterionViewItem = _searchModelCriterionViewItemHelper.createReferencingCriterionViewItem(this, propertyId); 174 criteria.addViewItem(criterionViewItem); 175 } 176 } 177 178 setFacetedCriteria(criteria); 179 return super.getFacetedCriteria(contextualParameters); 180 } 181 182 @Override 183 public ViewItemContainer getResultItems(Map<String, Object> contextualParameters) 184 { 185 ViewItemContainer resultItems = new View(); 186 187 if (contextualParameters.containsKey(__CONTENT_ID_CONTEXTUAL_PARAM_NAME)) 188 { 189 String contentId = (String) contextualParameters.get(__CONTENT_ID_CONTEXTUAL_PARAM_NAME); 190 Content content = _resolver.resolveById(contentId); 191 192 // Add a column for the title 193 ModelItem titleDefinition = content.getDefinition(Content.ATTRIBUTE_TITLE); 194 SearchUIColumn titleColumn = SearchUIColumnHelper.createModelItemColumn(titleDefinition); 195 titleColumn.setAllowSortOnMultipleJoin(allowSortOnMultipleJoin()); 196 resultItems.addViewItem(titleColumn); 197 198 // Add a column for the referenced field 199 if (contextualParameters.containsKey(__REFERENCE_FIELD_CONTEXTUAL_PARAM_NAME)) 200 { 201 Collection<ContentType> contentTypes = _contentHelper.getContentTypes(content); 202 203 String referenceModelItemPath = ((String) contextualParameters.get(__REFERENCE_FIELD_CONTEXTUAL_PARAM_NAME)).replaceAll("\\.", "/"); 204 int lastIndexOfItemPathSeparator = referenceModelItemPath.lastIndexOf(ModelItem.ITEM_PATH_SEPARATOR); 205 String referencedModelItemName = lastIndexOfItemPathSeparator > -1 ? referenceModelItemPath.substring(lastIndexOfItemPathSeparator + ModelItem.ITEM_PATH_SEPARATOR.length()) : referenceModelItemPath; 206 207 // Get or create parent of the referenced field 208 ViewItemAccessor parent = resultItems; 209 if (lastIndexOfItemPathSeparator > -1) 210 { 211 String parentPath = referenceModelItemPath.substring(0, lastIndexOfItemPathSeparator); 212 parent = (ViewItemAccessor) ViewHelper.addViewItem(parentPath, resultItems, contentTypes.toArray(new ContentType[contentTypes.size()])); 213 } 214 215 // Create the column for the referenced model item 216 ModelItem referencedModelItem = _systemPropertyExtensionPoint.hasExtension(referencedModelItemName) 217 ? _systemPropertyExtensionPoint.getExtension(referencedModelItemName) 218 : ModelHelper.getModelItem(referencedModelItemName, contentTypes); 219 SearchUIColumn referencedItemColumn = SearchUIColumnHelper.createModelItemColumn(referencedModelItem); 220 referencedItemColumn.setAllowSortOnMultipleJoin(allowSortOnMultipleJoin()); 221 222 // Add the column to its parent 223 parent.addViewItem(referencedItemColumn); 224 } 225 226 resultItems.addViewItem(SearchUIColumnHelper.createModelItemColumn(_systemPropertyExtensionPoint.getExtension("contributor"))); 227 resultItems.addViewItem(SearchUIColumnHelper.createModelItemColumn(_systemPropertyExtensionPoint.getExtension("lastModified"))); 228 resultItems.addViewItem(SearchUIColumnHelper.createModelItemColumn(_systemPropertyExtensionPoint.getExtension("contentTypes"))); 229 resultItems.addViewItem(SearchUIColumnHelper.createModelItemColumn(_systemPropertyExtensionPoint.getExtension("contentLanguage"))); 230 resultItems.addViewItem(SearchUIColumnHelper.createModelItemColumn(_systemPropertyExtensionPoint.getExtension("workflowStep"))); 231 } 232 233 setResultItems(resultItems); 234 return super.getResultItems(contextualParameters); 235 } 236}