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