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 SearchModelCriterionDefinition referenceCriterion = (SearchModelCriterionDefinition) titleCriterionViewItem.getDefinition(); 133 134 // Default value(s) 135 List<String> referenceAttributeValues = (List<String>) contextualParameters.get("referenceValues"); 136 List<Pair<String, Object>> defaultValues = new ArrayList<>(); 137 for (String referenceAttributeValue : referenceAttributeValues) 138 { 139 defaultValues.add(new ImmutablePair<>(null, referenceAttributeValue)); 140 } 141 referenceCriterion.setParsedDefaultValues(defaultValues); 142 143 // AND query on multiple entries 144 referenceCriterion.setMultipleOperandOperator(LogicalOperator.AND); 145 146 // Hidden 147 referenceCriterionViewItem.setHidden(true); 148 } 149 } 150 } 151 152 setCriteria(criteria); 153 return super.getCriteria(contextualParameters); 154 } 155 156 @Override 157 public ViewItemContainer getAdvancedCriteria(Map<String, Object> contextualParameters) 158 { 159 return null; 160 } 161 162 @Override 163 public ViewItemContainer getFacetedCriteria(Map<String, Object> contextualParameters) 164 { 165 ViewItemContainer criteria = new View(); 166 167 if (contextualParameters.containsKey(__CONTENT_ID_CONTEXTUAL_PARAM_NAME)) 168 { 169 for (String propertyId : new String[] {"contentTypes", "workflowStep", "contributor", "creator"}) 170 { 171 SearchModelCriterionViewItem criterionViewItem = _searchModelCriterionViewItemHelper.createReferencingCriterionViewItem(this, propertyId); 172 criteria.addViewItem(criterionViewItem); 173 } 174 } 175 176 setFacetedCriteria(criteria); 177 return super.getFacetedCriteria(contextualParameters); 178 } 179 180 @Override 181 public ViewItemContainer getResultItems(Map<String, Object> contextualParameters) 182 { 183 ViewItemContainer resultItems = new View(); 184 185 if (contextualParameters.containsKey(__CONTENT_ID_CONTEXTUAL_PARAM_NAME)) 186 { 187 String contentId = (String) contextualParameters.get(__CONTENT_ID_CONTEXTUAL_PARAM_NAME); 188 Content content = _resolver.resolveById(contentId); 189 190 // Add a column for the title 191 ModelItem titleDefinition = content.getDefinition(Content.ATTRIBUTE_TITLE); 192 SearchUIColumn titleColumn = SearchUIColumnHelper.createModelItemColumn(titleDefinition); 193 titleColumn.setAllowSortOnMultipleJoin(allowSortOnMultipleJoin()); 194 resultItems.addViewItem(titleColumn); 195 196 // Add a column for the referenced field 197 if (contextualParameters.containsKey(__REFERENCE_FIELD_CONTEXTUAL_PARAM_NAME)) 198 { 199 Collection<ContentType> contentTypes = _contentHelper.getContentTypes(content); 200 201 String referenceModelItemPath = ((String) contextualParameters.get(__REFERENCE_FIELD_CONTEXTUAL_PARAM_NAME)).replaceAll("\\.", "/"); 202 int lastIndexOfItemPathSeparator = referenceModelItemPath.lastIndexOf(ModelItem.ITEM_PATH_SEPARATOR); 203 String referencedModelItemName = lastIndexOfItemPathSeparator > -1 ? referenceModelItemPath.substring(lastIndexOfItemPathSeparator + ModelItem.ITEM_PATH_SEPARATOR.length()) : referenceModelItemPath; 204 205 // Get or create parent of the referenced field 206 ViewItemAccessor parent = resultItems; 207 if (lastIndexOfItemPathSeparator > -1) 208 { 209 String parentPath = referenceModelItemPath.substring(0, lastIndexOfItemPathSeparator); 210 parent = (ViewItemAccessor) ViewHelper.addViewItem(parentPath, resultItems, contentTypes.toArray(new ContentType[contentTypes.size()])); 211 } 212 213 // Create the column for the referenced model item 214 ModelItem referencedModelItem = _systemPropertyExtensionPoint.hasExtension(referencedModelItemName) 215 ? _systemPropertyExtensionPoint.getExtension(referencedModelItemName) 216 : ModelHelper.getModelItem(referencedModelItemName, contentTypes); 217 SearchUIColumn referencedItemColumn = SearchUIColumnHelper.createModelItemColumn(referencedModelItem); 218 referencedItemColumn.setAllowSortOnMultipleJoin(allowSortOnMultipleJoin()); 219 220 // Add the column to its parent 221 parent.addViewItem(referencedItemColumn); 222 } 223 224 resultItems.addViewItem(SearchUIColumnHelper.createModelItemColumn(_systemPropertyExtensionPoint.getExtension("contributor"))); 225 resultItems.addViewItem(SearchUIColumnHelper.createModelItemColumn(_systemPropertyExtensionPoint.getExtension("lastModified"))); 226 resultItems.addViewItem(SearchUIColumnHelper.createModelItemColumn(_systemPropertyExtensionPoint.getExtension("contentTypes"))); 227 resultItems.addViewItem(SearchUIColumnHelper.createModelItemColumn(_systemPropertyExtensionPoint.getExtension("contentLanguage"))); 228 resultItems.addViewItem(SearchUIColumnHelper.createModelItemColumn(_systemPropertyExtensionPoint.getExtension("workflowStep"))); 229 } 230 231 setResultItems(resultItems); 232 return super.getResultItems(contextualParameters); 233 } 234}