001/*
002 *  Copyright 2017 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.plugins.thesaurus.search;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.List;
021import java.util.Map;
022
023import org.apache.avalon.framework.configuration.Configuration;
024import org.apache.avalon.framework.configuration.ConfigurationException;
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.commons.lang.BooleanUtils;
028import org.apache.commons.lang.StringUtils;
029
030import org.ametys.cms.contenttype.MetadataType;
031import org.ametys.cms.repository.Content;
032import org.ametys.cms.search.query.OrQuery;
033import org.ametys.cms.search.query.Query;
034import org.ametys.cms.search.query.Query.Operator;
035import org.ametys.cms.search.query.StringQuery;
036import org.ametys.cms.search.ui.model.SearchUICriterion;
037import org.ametys.cms.search.ui.model.impl.IndexingFieldSearchUICriterion;
038import org.ametys.plugins.repository.AmetysObjectIterable;
039import org.ametys.plugins.thesaurus.ThesaurusDAO;
040import org.ametys.runtime.i18n.I18nizableText;
041import org.ametys.runtime.parameter.ParameterHelper;
042import org.ametys.runtime.parameter.ParameterHelper.ParameterType;
043
044/**
045 * Implementation of {@link SearchUICriterion} for a custom criteria for a metadata of type 'content' linked to a term of thesaurus.
046 */
047public class TermSearchUICriterion extends IndexingFieldSearchUICriterion
048{
049    /** The thesaurus DAO */
050    protected ThesaurusDAO _thesaurusDAO;
051
052    @Override
053    public void service(ServiceManager smanager) throws ServiceException
054    {
055        super.service(smanager);
056        _thesaurusDAO = (ThesaurusDAO) smanager.lookup(ThesaurusDAO.ROLE);
057    }
058    
059    @Override
060    public void configure(Configuration configuration) throws ConfigurationException
061    {
062        super.configure(configuration);
063        
064        MetadataType type = getType();
065        if (type == MetadataType.CONTENT || type == MetadataType.SUB_CONTENT)
066        {
067            setWidget("edition.select-term");
068            
069            // Override the widget parameters to disable search and creation
070            Map<String, I18nizableText> widgetParameters = getWidgetParameters();
071            widgetParameters.put("allowCreation", new I18nizableText("false"));
072            widgetParameters.put("allowSearch", new I18nizableText("true"));
073            
074            widgetParameters.put("allowSeeIndexedContent", new I18nizableText("false"));
075            widgetParameters.put("allowToggleAutoposting", new I18nizableText("true"));
076            widgetParameters.put("hideCandidates", new I18nizableText("false"));
077            widgetParameters.put("disableCandidateCreation", new I18nizableText("true"));
078            widgetParameters.put("showSelectedTermAlert", new I18nizableText("false"));
079            widgetParameters.put("genericTermAlertTitle", new I18nizableText("plugin.thesaurus", "PLUGINS_THESAURUS_CHOOSETERMHELPER_SEARCH_GENERIC_TERM_TITLE"));
080            widgetParameters.put("genericTermAlertDesc", new I18nizableText("plugin.thesaurus", "PLUGINS_THESAURUS_CHOOSETERMHELPER_SEARCH_GENERIC_TERM_DESC"));
081            
082            setWidgetParameters(widgetParameters);
083        }
084        else
085        {
086            List<String> pathParts = new ArrayList<>(_joinPaths);
087            pathParts.add(_fieldPath);
088            throw new ConfigurationException("Invalid search criteria for metadata " + StringUtils.join(pathParts, '/') + ". The metadata have to be of type 'content'");
089        }
090    }
091    
092    
093    @SuppressWarnings("unchecked")
094    @Override
095    protected Query getContentQuery(Object value, String language, String fieldPath, Operator operator)
096    {
097        if (value instanceof Map<?, ?>)
098        {
099            // Handle autoposting
100            Map<String, Object> valueAsMap = (Map<String, Object>) value;
101            
102            List<String> termIds = new ArrayList<>();
103            
104            Object rawValue = valueAsMap.get("value");
105            if (rawValue instanceof Collection<?>)
106            {
107                termIds.addAll((List<String>) rawValue);
108            }
109            else
110            {
111                String stringValue = (String) ParameterHelper.castValue((String) value, ParameterType.STRING);
112                termIds.add(stringValue);
113            }
114            
115            boolean autoposting = BooleanUtils.isTrue((Boolean) valueAsMap.get("autoposting"));
116            if (autoposting)
117            {
118                return getAutopostingTermsQuery(termIds, language, fieldPath, operator);
119            }
120            else
121            {
122                return super.getContentQuery(termIds, language, fieldPath, operator);
123            }
124        }
125        else
126        {
127            return super.getContentQuery(value, language, fieldPath, operator);
128        }
129    }
130    
131    /**
132     * Get a term query with autoposting.
133     * @param termIds The value to use for this criterion.
134     * @param language The search language.
135     * @param fieldPath The field path.
136     * @param operator The query operator to use.
137     * @return The query.
138     */
139    protected Query getAutopostingTermsQuery (List<String> termIds, String language, String fieldPath, Operator operator)
140    {
141        List<Query> queries = new ArrayList<>();
142        for (String termId : termIds)
143        {
144            queries.add(getAutopostingTermQuery(termId, language, fieldPath, operator));
145        }
146        return new OrQuery(queries);
147    }
148    
149    /**
150     * Get a term query with autoposting.
151     * @param termId The id of the term
152     * @param language The search language.
153     * @param fieldPath The field path.
154     * @param operator The query operator to use.
155     * @return The query.
156     */
157    protected Query getAutopostingTermQuery(String termId, String language, String fieldPath, Operator operator)
158    {
159        List<String> aupostingTermIds = new ArrayList<>();
160        aupostingTermIds.add(termId);
161        aupostingTermIds.addAll(getChildTermIds(termId));
162        
163        List<Query> queries = new ArrayList<>();
164        for (String val : aupostingTermIds)
165        {
166            queries.add(new StringQuery(fieldPath, operator, val, language));
167        }
168        
169        return new OrQuery(queries);
170    }
171    
172    /**
173     * Get the id of child terms recursively
174     * @param parentId The id of parent term
175     * @return The ids
176     */
177    protected List<String> getChildTermIds(String parentId)
178    {
179        List<String> chilIds = new ArrayList<>();
180        
181        AmetysObjectIterable<Content> chidren = _thesaurusDAO.getChildTerms(parentId);
182        for (Content child : chidren)
183        {
184            chilIds.add(child.getId());
185            chilIds.addAll(getChildTermIds(child.getId()));
186        }
187        
188        return chilIds;
189    }
190}