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}