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.Collections;
020import java.util.List;
021import java.util.Map;
022import java.util.Set;
023
024import org.apache.avalon.framework.component.ComponentException;
025import org.apache.avalon.framework.configuration.Configuration;
026import org.apache.avalon.framework.configuration.ConfigurationException;
027import org.apache.avalon.framework.configuration.DefaultConfiguration;
028import org.apache.avalon.framework.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030
031import org.ametys.cms.repository.Content;
032import org.ametys.cms.search.query.Query.Operator;
033import org.ametys.cms.search.ui.model.AbstractSearchUIModel;
034import org.ametys.cms.search.ui.model.SearchUIColumn;
035import org.ametys.cms.search.ui.model.SearchUICriterion;
036import org.ametys.cms.search.ui.model.SearchUIModel;
037import org.ametys.cms.search.ui.model.impl.IndexingFieldSearchUICriterion;
038import org.ametys.cms.search.ui.model.impl.MetadataSearchUIColumn;
039import org.ametys.cms.search.ui.model.impl.SystemSearchUIColumn;
040import org.ametys.cms.search.ui.model.impl.SystemSearchUICriterion;
041import org.ametys.plugins.repository.AmetysObjectResolver;
042import org.ametys.runtime.model.ModelItem;
043import org.ametys.runtime.plugin.component.ThreadSafeComponentManager;
044
045/**
046 * Implementation of {@link SearchUIModel} for contents referencing the same content's values for a given metadata.
047 */
048public class ReferencingContentsWithSameValuesSearchUIModel extends AbstractSearchUIModel
049{
050    /** ComponentManager for {@link SearchUICriterion}s. */
051    protected ThreadSafeComponentManager<SearchUICriterion> _searchCriterionManager;
052    
053    /** ComponentManager for {@link SearchUIColumn}s. */
054    protected ThreadSafeComponentManager<SearchUIColumn> _searchColumnManager;
055    /** The search column roles. */
056    protected List<String> _searchColumnRoles;
057    
058    private AmetysObjectResolver _resolver;
059
060    @Override
061    public void service(ServiceManager manager) throws ServiceException
062    {
063        super.service(manager);
064        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
065    }
066    
067    @Override
068    public Set<String> getContentTypes(Map<String, Object> contextualParameters)
069    {
070        if (contextualParameters != null && contextualParameters.containsKey("contentId")  && contextualParameters.containsKey("referenceField"))
071        {
072            // Get the content type which has declared the attribute
073            String contentId = (String) contextualParameters.get("contentId");
074            Content content = _resolver.resolveById(contentId);
075            
076            String referenceAttributePath = (String) contextualParameters.get("referenceField");
077            if (content.hasDefinition(referenceAttributePath))
078            {
079                ModelItem modelItem = content.getDefinition(referenceAttributePath);
080                return Set.of(modelItem.getModel().getId());
081            }
082        }
083        
084        return Set.of();
085    }
086    
087    @Override
088    public Set<String> getExcludedContentTypes(Map<String, Object> contextualParameters)
089    {
090        return Collections.emptySet();
091    }
092    
093    @Override
094    public Map<String, SearchUICriterion> getCriteria(Map<String, Object> contextualParameters)
095    {
096        List<SearchUICriterion> criteria = new ArrayList<>();
097        
098        if (contextualParameters.containsKey("contentId"))
099        {
100            try
101            {
102                _searchCriterionManager = new ThreadSafeComponentManager<>();
103                _searchCriterionManager.setLogger(getLogger());
104                _searchCriterionManager.contextualize(_context);
105                _searchCriterionManager.service(_manager);
106                
107                List<String> roles = new ArrayList<>();
108                
109                String contentId = (String) contextualParameters.get("contentId");
110                
111                Content content = _resolver.resolveById(contentId);
112                
113                Set<String> setWithOnlyFirstCTypeId = Set.of(content.getTypes()[0]);
114                
115                Configuration conf = getIndexingFieldCriteriaConfiguration(setWithOnlyFirstCTypeId, Content.ATTRIBUTE_TITLE, Operator.SEARCH);
116                _searchCriterionManager.addComponent("cms", null, Content.ATTRIBUTE_TITLE, IndexingFieldSearchUICriterion.class, conf);
117                roles.add("title");
118                
119                conf = getSystemCriteriaConfiguration(setWithOnlyFirstCTypeId, "fulltext");
120                _searchCriterionManager.addComponent("cms", null, "fulltext", SystemSearchUICriterion.class, conf);
121                roles.add("fulltext");
122                
123                conf = getSystemCriteriaConfiguration(setWithOnlyFirstCTypeId, "workflowStep");
124                _searchCriterionManager.addComponent("cms", null, "workflowStep", SystemSearchUICriterion.class, conf);
125                roles.add("workflowStep");
126                
127                if (contextualParameters.containsKey("referenceField") && contextualParameters.containsKey("referenceValues"))
128                {
129                    String referenceMetadataPath = ((String) contextualParameters.get("referenceField")).replaceAll("\\.", "/");
130                    
131                    if (content.hasDefinition(referenceMetadataPath))
132                    {
133                        ModelItem modelItem = content.getDefinition(referenceMetadataPath);
134                        @SuppressWarnings("unchecked")
135                        List<String> referenceAttributeValues = (List<String>) contextualParameters.get("referenceValues");
136                        
137                        DefaultConfiguration referenceFieldConf = new DefaultConfiguration("criteria");
138                        // Default value(s)
139                        for (String referenceAttributeValue : referenceAttributeValues)
140                        {
141                            DefaultConfiguration defaultValueConf = new DefaultConfiguration("default-value");
142                            defaultValueConf.setValue(referenceAttributeValue);
143                            referenceFieldConf.addChild(defaultValueConf);
144                        }
145                        // AND query on multiple entries
146                        DefaultConfiguration multipleOperandConf = new DefaultConfiguration("multiple-operand");
147                        multipleOperandConf.setValue("and");
148                        referenceFieldConf.addChild(multipleOperandConf);
149                        // Hidden
150                        referenceFieldConf.setAttribute("hidden", "true");
151                        
152                        conf = getIndexingFieldCriteriaConfiguration(referenceFieldConf, Set.of(modelItem.getModel().getId()), referenceMetadataPath, null, null);
153                        _searchCriterionManager.addComponent("cms", null, "referenceField", IndexingFieldSearchUICriterion.class, conf);
154                        roles.add("referenceField");
155                    }
156                }
157                
158                _searchCriterionManager.initialize();
159                
160                // Lookup.
161                for (String role : roles)
162                {
163                    criteria.add(_searchCriterionManager.lookup(role));
164                }
165                
166                setCriteria(criteria);
167                
168                // Dispose
169                _searchCriterionManager.dispose();
170                _searchCriterionManager = null;
171            }
172            catch (Exception e)
173            {
174                getLogger().error("Error getting the search criteria.", e);
175                throw new IllegalStateException("Error getting the search criteria.", e);
176            }
177        }
178        else
179        {
180            setCriteria(criteria);
181        }
182        
183        return super.getCriteria(contextualParameters);
184    }
185
186    @Override
187    public Map<String, SearchUICriterion> getAdvancedCriteria(Map<String, Object> contextualParameters)
188    {
189        return Collections.emptyMap();
190    }
191    
192    @Override
193    public Map<String, SearchUICriterion> getFacetedCriteria(Map<String, Object> contextualParameters)
194    {
195        return Collections.emptyMap();
196    }
197    
198    @Override
199    public Map<String, SearchUIColumn> getResultFields(Map<String, Object> contextualParameters)
200    {
201        List<SearchUIColumn> columns = new ArrayList<>();
202        
203        try
204        {
205            _searchColumnManager = new ThreadSafeComponentManager<>();
206            _searchColumnManager.setLogger(getLogger());
207            _searchColumnManager.contextualize(_context);
208            _searchColumnManager.service(_manager);
209            
210            _searchColumnRoles = new ArrayList<>();
211            
212            if (contextualParameters.containsKey("contentId"))
213            {
214                String contentId = (String) contextualParameters.get("contentId");
215                
216                Content content = _resolver.resolveById(contentId);
217                
218                String firstCTypeId = content.getTypes()[0];
219                
220                // Column title
221                Configuration conf = getMetadataColumnConfiguration(Set.of(firstCTypeId), Content.ATTRIBUTE_TITLE);
222                _searchColumnManager.addComponent("cms", null, Content.ATTRIBUTE_TITLE, MetadataSearchUIColumn.class, conf);
223                _searchColumnRoles.add(Content.ATTRIBUTE_TITLE);
224                
225                if (contextualParameters.containsKey("referenceField"))
226                {
227                    String referenceAttributePath = ((String) contextualParameters.get("referenceField")).replaceAll("\\.", "/");
228                    if (content.hasDefinition(referenceAttributePath))
229                    {
230                        ModelItem modelItem = content.getDefinition(referenceAttributePath);
231                        conf = getMetadataColumnConfiguration(Set.of(modelItem.getModel().getId()), referenceAttributePath);
232                        _searchColumnManager.addComponent("cms", null, "referenceField", MetadataSearchUIColumn.class, conf);
233                        _searchColumnRoles.add("referenceField");
234                    }
235                }
236            }
237            
238            addSystemColumnComponent("contributor");
239            addSystemColumnComponent("lastModified");
240            addSystemColumnComponent("contentTypes");
241            addSystemColumnComponent("contentLanguage");
242            addSystemColumnComponent("workflowStep");
243            
244            _searchColumnManager.initialize();
245            
246            for (String role : _searchColumnRoles)
247            {
248                columns.add(_searchColumnManager.lookup(role));
249            }
250            
251            _searchColumnManager.dispose();
252            _searchColumnManager = null;
253        }
254        catch (Exception e)
255        {
256            getLogger().error("Error getting the result fields.", e);
257            throw new IllegalStateException("Error getting the result fields.", e);
258        }
259        
260        setResultFields(columns);
261        
262        return super.getResultFields(contextualParameters);
263    }
264    
265    /**
266     * Add a system column component to the manager.
267     * @param property the system property.
268     * @throws ConfigurationException if a configuration error occurs.
269     * @throws ComponentException if a component cannot be initialized.
270     */
271    protected void addSystemColumnComponent(String property) throws ConfigurationException, ComponentException
272    {
273        Configuration conf = getSystemColumnConfiguration(null, property);
274        _searchColumnManager.addComponent("cms", null, property, SystemSearchUIColumn.class, conf);
275        _searchColumnRoles.add(property);
276    }
277
278}