001/*
002 *  Copyright 2020 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.workspaces.search.module;
017
018import java.util.Collection;
019import java.util.HashSet;
020import java.util.List;
021import java.util.Objects;
022import java.util.Set;
023import java.util.stream.Collectors;
024
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.cocoon.environment.Request;
028import org.apache.commons.lang3.StringUtils;
029import org.xml.sax.SAXException;
030
031import org.ametys.cms.contenttype.ContentTypesHelper;
032import org.ametys.cms.repository.Content;
033import org.ametys.cms.search.Sort.Order;
034import org.ametys.cms.search.content.ContentSearcherFactory.SimpleContentSearcher;
035import org.ametys.cms.search.query.AndQuery;
036import org.ametys.cms.search.query.ContentTypeQuery;
037import org.ametys.cms.search.query.FullTextQuery;
038import org.ametys.cms.search.query.MatchAllQuery;
039import org.ametys.cms.search.query.OrQuery;
040import org.ametys.cms.search.query.Query;
041import org.ametys.cms.search.query.Query.Operator;
042import org.ametys.cms.search.query.TagQuery;
043import org.ametys.plugins.workspaces.WorkspacesConstants;
044import org.ametys.plugins.workspaces.categories.Category;
045import org.ametys.plugins.workspaces.categories.CategoryCMSTag;
046import org.ametys.plugins.workspaces.project.objects.Project;
047import org.ametys.web.repository.content.WebContent;
048import org.ametys.web.repository.site.Site;
049import org.ametys.web.search.query.SiteQuery;
050
051/**
052 * Generator for posts search module
053 *
054 */
055public class NewsSearchModuleGenerator extends AbstractContentSolrSearchModuleGenerator
056{
057    private ContentTypesHelper _ctypeHelper;
058
059    @Override
060    public void service(ServiceManager smanager) throws ServiceException
061    {
062        super.service(smanager);
063        _ctypeHelper = (ContentTypesHelper) smanager.lookup(ContentTypesHelper.ROLE);
064    }
065    
066    @Override
067    protected SimpleContentSearcher getSearcher()
068    {
069        return _contentSearcherFactory.create(WorkspacesConstants.PROJECT_NEWS_CONTENT_TYPE_ID, WorkspacesConstants.CATALOG_NEWS_CONTENT_TYPE_ID);
070    }
071    
072    @Override
073    protected String getSortFieldName()
074    {
075        return "lastModified";
076    }
077    
078    @Override
079    protected Order getSortOrder()
080    {
081        return Order.DESC;
082    }
083    
084    @Override
085    protected Query getQuery(String siteName, String lang, String textfield, Request request)
086    {
087        Collection<Query> queries = new HashSet<>();
088        
089        if (StringUtils.isNotBlank(textfield))
090        {
091            Query fullTextQuery = new FullTextQuery(textfield, lang, Operator.SEARCH);
092            queries.add(fullTextQuery);
093        }
094        
095        List<Project> projects = getProjects(request, true);
096        Query projectNewsQuery = getProjectNewsQuery(request, projects);
097        
098        boolean noProjectSelected = StringUtils.isBlank(request.getParameter("project"));
099        if (noProjectSelected)
100        {
101            Query catalogNewsQuery = getCatalogNewsQuery(request, siteName);
102            queries.add(new OrQuery(projectNewsQuery, catalogNewsQuery));
103        }
104        else
105        {
106            queries.add(projectNewsQuery);
107        }
108        
109        return new AndQuery(queries);
110    }
111    
112    /**
113     * Get the query for projects' news
114     * @param request the request
115     * @param projects the targeted projects
116     * @return the query
117     */
118    protected Query getProjectNewsQuery(Request request, List<Project> projects)
119    {
120        Query projectNewsTypeQuery = new ContentTypeQuery(Operator.EQ,  WorkspacesConstants.PROJECT_NEWS_CONTENT_TYPE_ID);
121        Query projectSiteQuery = getSiteQuery(projects);
122        
123        return new AndQuery(projectNewsTypeQuery, projectSiteQuery);
124    }
125    
126    /**
127     * Get the query for catalog's news
128     * @param request the request
129     * @param siteName the current site name
130     * @return the query
131     */
132    protected Query getCatalogNewsQuery(Request request, String siteName)
133    {
134        Collection<Query> queries = new HashSet<>();
135        
136        queries.add(new ContentTypeQuery(Operator.EQ, WorkspacesConstants.CATALOG_NEWS_CONTENT_TYPE_ID));
137        queries.add(new SiteQuery(siteName));
138        
139        List<String> categories = getCategories(request);
140        if (categories != null && !categories.isEmpty())
141        {
142            List<String> tagCategories = categories.stream()
143                .map(c -> CategoryCMSTag.TAG_PREFIX + c)
144                .collect(Collectors.toList());
145            
146            queries.add(new TagQuery(tagCategories.toArray(new String[tagCategories.size()])));
147        }
148        
149        return new AndQuery(queries);
150    }
151    
152    /**
153     * Create a query to match the sites corresponding to the projects
154     * If projects is empty or null, then it will match nothing
155     * Else it will match the sites linked to the projects
156     * @param projects projects to filter
157     * @return a Query to match the sites linked to the projects
158     */
159    protected Query getSiteQuery(List<Project> projects)
160    {
161        List<String> sites = getSiteNames(projects);
162        if (sites == null || sites.isEmpty())
163        {
164            return new MatchAllQuery();
165        }
166        else
167        {
168            return new SiteQuery(sites);
169        }
170    }
171    
172    /**
173     * Get the list of sites linked to the list of projects
174     * @param projects a list of projects where we want to get the list of sites
175     * @return a list of site names, or null if the projects were null;
176     */
177    protected List<String> getSiteNames(List<Project> projects)
178    {
179        if (projects == null)
180        {
181            return null;
182        }
183        return projects.stream()
184                .map(Project::getSite)
185                .filter(Objects::nonNull)
186                .map(Site::getName)
187                .collect(Collectors.toList());
188    }
189    
190    @Override
191    protected void saxAdditionalInformation(Content content) throws SAXException
192    {
193        super.saxAdditionalInformation(content);
194        
195        if (_ctypeHelper.isInstanceOf(content,  WorkspacesConstants.PROJECT_NEWS_CONTENT_TYPE_ID))
196        {
197            // SAX project
198            String siteName = ((WebContent) content).getSiteName();
199            Project project = _projectManager.getProject(siteName);
200            if (project != null)
201            {
202                saxProject(project);
203            }
204        }
205        else
206        {
207            // SAX category
208            saxCategory(content);
209        }
210    }
211    
212    /**
213     * SAX category
214     * @param content the news content
215     * @throws SAXException if an error occured while saxing
216     */
217    protected void saxCategory(Content content) throws SAXException
218    {
219        Set<String> tags = content.getTags();
220        
221        for (String tagName : tags)
222        {
223            Category category = _categoryProviderEP.getTag(tagName, null);
224            saxCategory(category);
225        }
226    }
227    
228}