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