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