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}