001/*
002 *  Copyright 2016 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.explorer;
017
018import java.io.IOException;
019import java.util.ArrayList;
020import java.util.List;
021import java.util.Map;
022
023import org.apache.avalon.framework.service.ServiceException;
024import org.apache.avalon.framework.service.ServiceManager;
025import org.apache.cocoon.ProcessingException;
026import org.apache.cocoon.environment.ObjectModelHelper;
027import org.apache.cocoon.environment.Request;
028import org.apache.cocoon.xml.XMLUtils;
029import org.apache.commons.lang.StringUtils;
030import org.apache.solr.client.solrj.util.ClientUtils;
031import org.xml.sax.SAXException;
032
033import org.ametys.cms.content.indexing.solr.SolrFieldNames;
034import org.ametys.cms.search.Sort;
035import org.ametys.cms.search.Sort.Order;
036import org.ametys.cms.search.query.AndQuery;
037import org.ametys.cms.search.query.DocumentTypeQuery;
038import org.ametys.cms.search.query.DublinCoreQuery;
039import org.ametys.cms.search.query.FilenameQuery;
040import org.ametys.cms.search.query.FullTextQuery;
041import org.ametys.cms.search.query.NotQuery;
042import org.ametys.cms.search.query.OrQuery;
043import org.ametys.cms.search.query.Query;
044import org.ametys.cms.search.query.Query.Operator;
045import org.ametys.cms.search.query.QuerySyntaxException;
046import org.ametys.cms.search.query.ResourceLocationQuery;
047import org.ametys.cms.search.query.UsersQuery;
048import org.ametys.cms.search.solr.SearcherFactory;
049import org.ametys.core.user.UserIdentity;
050import org.ametys.core.util.JSONUtils;
051import org.ametys.plugins.core.user.UserHelper;
052import org.ametys.plugins.explorer.ExplorerNode;
053import org.ametys.plugins.explorer.resources.Resource;
054import org.ametys.plugins.explorer.resources.generators.SearchGenerator;
055import org.ametys.plugins.repository.AmetysObjectIterable;
056
057/**
058 * Search resources in a specific explorer node.
059 */
060public class SearchResourcesGenerator extends SearchGenerator
061{
062    /** The searcher factory. */
063    protected SearcherFactory _searcherFactory;
064    
065    /** The JSON utils */
066    protected JSONUtils _jsonUtils;
067    
068    /** The user helper */
069    protected UserHelper _userHelper;
070    
071    @Override
072    public void service(ServiceManager sManager) throws ServiceException
073    {
074        super.service(sManager);
075        _searcherFactory = (SearcherFactory) sManager.lookup(SearcherFactory.ROLE);
076        _jsonUtils = (JSONUtils) sManager.lookup(JSONUtils.ROLE);
077        _userHelper = (UserHelper) manager.lookup(UserHelper.ROLE);
078    }
079    
080    @Override
081    public void generate() throws IOException, SAXException, ProcessingException
082    {
083        Request request = ObjectModelHelper.getRequest(objectModel);
084        
085        String id = request.getParameter("id");
086        String rootId = request.getParameter("rootId");
087        ExplorerNode node = _resolver.resolveById(id);
088        
089        Query query = getQuery(request, node, rootId);
090        
091        contentHandler.startDocument();
092        XMLUtils.startElement(contentHandler, "resources");
093        
094        try
095        {
096            Sort sort = new Sort(SolrFieldNames.TITLE_SORT, Order.ASC);
097            
098            AmetysObjectIterable<Resource> resources = _searcherFactory.create()
099                                                        .withQuery(query)
100                                                        .withFilterQueries(new DocumentTypeQuery(SolrFieldNames.TYPE_RESOURCE))
101                                                        .withSort(sort)
102                                                        .search();
103            
104            for (Resource resource : resources)
105            {
106                saxResource(resource);
107            }
108        }
109        catch (Exception e)
110        {
111            getLogger().error("Error searching resources in the explorer node " + id, e);
112            throw new ProcessingException("Error searching resources in the explorer node " + id, e);
113        }
114        
115        XMLUtils.endElement(contentHandler, "resources");
116        contentHandler.endDocument();
117    }
118    
119    /**
120     * Build the query object corresponding to the user search.
121     * @param request The user request.
122     * @param explorerNode The explorer node to search into.
123     * @param rootId The root ID, can be null to search all resources.
124     * @return the query object.
125     */
126    protected Query getQuery(Request request, ExplorerNode explorerNode, String rootId)
127    {
128        List<Query> queries = new ArrayList<>();
129        
130        if (explorerNode != null || StringUtils.isNotEmpty(rootId))
131        {
132            String path = explorerNode == null ? "" : explorerNode.getExplorerPath();
133            queries.add(new ResourceLocationQuery(rootId, path));
134        }
135        
136        // File name
137        String name = request.getParameter("name");
138        if (StringUtils.isNotEmpty(name))
139        {
140            queries.add(new FilenameQuery(name));
141        }
142        
143        // Full text
144        String fulltext = request.getParameter("fulltext");
145        if (StringUtils.isNotEmpty(fulltext))
146        {
147            queries.add(new FullTextQuery(fulltext, Operator.LIKE));
148        }
149        
150        String keywords = request.getParameter("keywords");
151        if (StringUtils.isNotEmpty(keywords))
152        {
153            Query subjectQuery = new DublinCoreQuery("subject", keywords);
154            Query descQuery = new DublinCoreQuery("description", keywords);
155            
156            queries.add(new OrQuery(subjectQuery, descQuery));
157        }
158        
159        String authorAsStr = request.getParameter("author");
160        Map<String, Object> json = _jsonUtils.convertJsonToMap(authorAsStr);
161        UserIdentity author = _userHelper.json2userIdentity(json);
162        if (null != author)
163        {
164            queries.add(new UsersQuery(SolrFieldNames.RESOURCE_CREATOR, author));
165        }
166        
167        return new AndQuery(queries);
168    }
169    
170    
171    
172    /**
173     * Represents a {@link Query} testing the resource creator.
174     */
175    class ResourceCreatorQuery implements Query
176    {
177        /** The operator. */
178        private Operator _operator;
179        
180        /** The value to test. */
181        private String _value;
182        
183        /**
184         * Build a string query.
185         * @param value the value.
186         */
187        public ResourceCreatorQuery(String value)
188        {
189            this(Operator.EQ, value);
190        }
191        
192        /**
193         * Build a string query.
194         * @param op the operator.
195         * @param value the value.
196         */
197        public ResourceCreatorQuery(Operator op, String value)
198        {
199            _operator = op;
200            _value = value;
201        }
202        
203        @Override
204        public String build() throws QuerySyntaxException
205        {
206            StringBuilder query = new StringBuilder();
207            
208            if (_operator == Operator.NE)
209            {
210                NotQuery.appendNegation(query);
211            }
212            
213            query.append(SolrFieldNames.RESOURCE_CREATOR).append(':')
214                          .append(ClientUtils.escapeQueryChars(_value));
215            
216            return query.toString();
217        }
218
219        @Override
220        public int hashCode()
221        {
222            final int prime = 31;
223            int result = 1;
224            result = prime * result + _getOuterType().hashCode();
225            result = prime * result + ((_operator == null) ? 0 : _operator.hashCode());
226            result = prime * result + ((_value == null) ? 0 : _value.hashCode());
227            return result;
228        }
229
230        @Override
231        public boolean equals(Object obj)
232        {
233            if (this == obj)
234            {
235                return true;
236            }
237            if (obj == null)
238            {
239                return false;
240            }
241            if (getClass() != obj.getClass())
242            {
243                return false;
244            }
245            ResourceCreatorQuery other = (ResourceCreatorQuery) obj;
246            if (!_getOuterType().equals(other._getOuterType()))
247            {
248                return false;
249            }
250            if (_operator != other._operator)
251            {
252                return false;
253            }
254            if (_value == null)
255            {
256                if (other._value != null)
257                {
258                    return false;
259                }
260            }
261            else if (!_value.equals(other._value))
262            {
263                return false;
264            }
265            return true;
266        }
267
268        private SearchResourcesGenerator _getOuterType()
269        {
270            return SearchResourcesGenerator.this;
271        }
272    }
273}