001/* 002 * Copyright 2010 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.web.frontoffice; 017 018import java.io.IOException; 019import java.util.ArrayList; 020import java.util.Arrays; 021import java.util.Collection; 022import java.util.Collections; 023import java.util.Iterator; 024import java.util.List; 025import java.util.Map; 026 027import org.apache.cocoon.environment.ObjectModelHelper; 028import org.apache.cocoon.environment.Request; 029import org.apache.cocoon.xml.AttributesImpl; 030import org.apache.cocoon.xml.XMLUtils; 031import org.apache.commons.lang3.ArrayUtils; 032import org.apache.commons.lang3.StringUtils; 033import org.apache.solr.client.solrj.util.ClientUtils; 034import org.xml.sax.SAXException; 035 036import org.ametys.cms.content.indexing.solr.SolrFieldNames; 037import org.ametys.cms.search.SearchResult; 038import org.ametys.cms.search.SearchResults; 039import org.ametys.cms.search.Sort; 040import org.ametys.cms.search.Sort.Order; 041import org.ametys.cms.search.query.AndQuery; 042import org.ametys.cms.search.query.DublinCoreQuery; 043import org.ametys.cms.search.query.FullTextQuery; 044import org.ametys.cms.search.query.OrQuery; 045import org.ametys.cms.search.query.Query; 046import org.ametys.plugins.explorer.resources.Resource; 047import org.ametys.plugins.repository.AmetysObject; 048import org.ametys.runtime.i18n.I18nizableText; 049import org.ametys.web.indexing.solr.SolrWebFieldNames; 050import org.ametys.web.repository.page.Page; 051import org.ametys.web.repository.site.Site; 052import org.ametys.web.search.query.PageQuery; 053import org.ametys.web.search.query.SiteQuery; 054import org.ametys.web.search.query.SitemapQuery; 055 056/** 057 * Generates the results of a search performed on front office 058 */ 059public class SearchResourcesGenerator extends AbstractSearchGenerator 060{ 061 /** MIME types for images */ 062 protected static final String[] IMAGE_MIMETYPE = new String [] {"image/gif", "image/png", "image/jpeg"}; 063 /** MIME types for videos */ 064 protected static final String[] VIDEO_MIMETYPE = new String [] {"application/x-shockwave-flash", "video/mpeg,video/mp4", "video/quicktime", "video/x-flv", "video/msvideo", "video/x-msvideo", "video/x-ms-wmv"}; 065 /** MIME types for audio files */ 066 protected static final String[] AUDIO_MIMETYPE = new String [] {"audio/mpeg", "audio/x-mpeg", "audio/x-ms-wma", "audio/vnd.rn-realaudio", "audio/x-wav"}; 067 /** MIME types for pdf */ 068 protected static final String[] PDF_MIMETYPE = new String [] {"application/pdf"}; 069 /** MIME types for text documents */ 070 protected static final String[] TEXT_MIMETYPE = new String [] {"application/msword", "vnd.oasis.opendocument.text", "application/rtf,text/rtf"}; 071 /** MIME types for presentation */ 072 protected static final String[] PRES_MIMETYPE = new String [] {"application/vnd.ms-powerpoint", "application/vnd.oasis.opendocument.presentation"}; 073 /** MIME types for spreadsheets */ 074 protected static final String[] SPREADSHEET_MIMETYPE = new String [] {"vnd.oasis.opendocument.spreadsheet", "application/vnd.ms-excel"}; 075 076 @Override 077 protected Query getQuery(Request request, Collection<String> siteNames, String language) throws IllegalArgumentException 078 { 079 List<Query> queries = new ArrayList<>(); 080 081 _addTextFieldQuery(queries, language, request); 082 _addPagesQuery(queries, request); 083 _addCreatorFieldQuery(queries, request); 084 _addFormatQuery(queries, request); 085 _addPublisherQuery(queries, request); 086 _addRightsQuery(queries, request); 087 088 return new AndQuery(queries); 089 } 090 091 @Override 092 protected Collection<Query> getFilterQueries(Request request, Collection<String> siteNames, String language) throws IllegalArgumentException 093 { 094 Query siteQuery = new SiteQuery(siteNames); 095 Query langQuery = new SitemapQuery(language); 096 097 return Arrays.asList(siteQuery, langQuery); 098 } 099 100 @Override 101 protected Collection<String> getDocumentTypes(Request request) 102 { 103 return Arrays.asList(TYPE_PAGE_RESOURCE); 104 } 105 106 @Override 107 protected Collection<String> getFields() 108 { 109 return Arrays.asList("DCTitle", "DCDescription", "DCSubject", "full"); 110 } 111 112 @Override 113 protected Map<String, Criterion> getCriteria(Request request, String siteName, String lang) 114 { 115 return Collections.emptyMap(); 116 } 117 118 @Override 119 protected void saxHits(SearchResults<AmetysObject> results, int start, int maxResults) throws SAXException, IOException 120 { 121 long limit = Math.min(start + maxResults, results.getResults().getSize()); 122 float maxScore = results.getMaxScore(); 123 124 Iterator<SearchResult<AmetysObject>> it = results.getResults().iterator(); 125 for (int i = start; i < limit; i++) 126 { 127 SearchResult<AmetysObject> searchResult = it.next(); 128 float score = searchResult.getScore(); 129 AmetysObject ametysObject = searchResult.getObject(); 130 if (ametysObject instanceof Resource) 131 { 132 saxResourceHit(score, maxScore, (Resource) ametysObject); 133 } 134 } 135 } 136 137 @Override 138 protected Sort getSortField(Request request) 139 { 140 if (request.getParameter("sort-by-title-for-sorting") != null) 141 { 142 return new Sort(SolrFieldNames.TITLE_SORT, Order.ASC); 143 } 144 else if (request.getParameter("sort-by-date") != null) 145 { 146 return new Sort(SolrFieldNames.DATE_FOR_SORTING, Order.ASC); 147 } 148 149 return new Sort("score", Order.DESC); 150 } 151 152 @Override 153 protected List<Sort> getPrimarySortFields(Request request) 154 { 155 return Collections.emptyList(); 156 } 157 158 @Override 159 protected void saxFormFields(Request request, SearchResults<AmetysObject> searchResults, String siteName, String lang) throws SAXException 160 { 161 XMLUtils.createElement(contentHandler, "textfield"); 162 163 if (parameters.getParameterAsBoolean("search-by-creator", false)) 164 { 165 XMLUtils.createElement(contentHandler, "creator"); 166 } 167 168 if (parameters.getParameterAsBoolean("search-by-format", false)) 169 { 170 _saxFormatCriteria(); 171 } 172 173 if (parameters.getParameterAsBoolean("search-by-publisher", false)) 174 { 175 _saxPublisherCriteria(); 176 } 177 178 if (parameters.getParameterAsBoolean("search-by-rights", false)) 179 { 180 _saxRightsCriteria(); 181 } 182 183 _saxSitemapCriteria(); 184 185 boolean multisite = parameters.getParameterAsBoolean("search-multisite", false); 186 if (multisite) 187 { 188 XMLUtils.createElement(contentHandler, "multisite"); 189 190 XMLUtils.startElement(contentHandler, "sites"); 191 Collection<String> allSites = _siteManager.getSiteNames(); 192 for (String name : allSites) 193 { 194 Site site = _siteManager.getSite(name); 195 AttributesImpl attr = new AttributesImpl(); 196 attr.addCDATAAttribute("name", name); 197 if (name.equals(siteName)) 198 { 199 attr.addCDATAAttribute("current", "true"); 200 } 201 XMLUtils.createElement(contentHandler, "site", attr, site.getTitle()); 202 } 203 XMLUtils.endElement(contentHandler, "sites"); 204 } 205 } 206 207 private void _saxSitemapCriteria() throws SAXException 208 { 209 @SuppressWarnings("unchecked") 210 Map<String, Object> parentContext = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT); 211 if (parentContext == null) 212 { 213 parentContext = Collections.emptyMap(); 214 } 215 216 String[] pages = (String[]) parentContext.get("search-by-pages"); 217 if (ArrayUtils.isNotEmpty(pages)) 218 { 219 XMLUtils.startElement(contentHandler, "pages"); 220 221 for (String pageID : pages) 222 { 223 Page page = _resolver.resolveById(pageID); 224 AttributesImpl attr = new AttributesImpl(); 225 attr.addCDATAAttribute("id", pageID); 226 attr.addCDATAAttribute("path", page.getSitemap().getName() + "/" + page.getPathInSitemap()); 227 attr.addCDATAAttribute("title", page.getTitle()); 228 attr.addCDATAAttribute("long-title", page.getLongTitle()); 229 XMLUtils.createElement(contentHandler, "page", attr); 230 } 231 232 XMLUtils.endElement(contentHandler, "pages"); 233 } 234 } 235 236 @Override 237 protected void saxFormValues(Request request, SearchResults<AmetysObject> searchResults, int start, int offset) throws SAXException 238 { 239 _saxTextField(request); 240 _saxPages(request); 241 _saxCreatorField (request); 242 _saxFormats(request); 243 _saxPublisher(request); 244 _saxRights(request); 245 _saxMultisite(request); 246 } 247 248 private void _saxTextField(Request request) throws SAXException 249 { 250 String textfield = request.getParameter("textfield"); 251 XMLUtils.createElement(contentHandler, "textfield", textfield != null ? textfield : ""); 252 } 253 254 private void _saxPages(Request request) throws SAXException 255 { 256 String[] pages = request.getParameterValues("pages"); 257 if (pages != null && pages.length > 0 && !(pages.length == 1 && pages[0].equals(""))) 258 { 259 for (String path : pages) 260 { 261 XMLUtils.createElement(contentHandler, "page", path); 262 } 263 } 264 } 265 266 private void _saxCreatorField(Request request) throws SAXException 267 { 268 String creator = request.getParameter("creator"); 269 if (!StringUtils.isEmpty(creator)) 270 { 271 XMLUtils.createElement(contentHandler, "creator", creator != null ? creator : ""); 272 } 273 } 274 275 private void _saxFormats(Request request) throws SAXException 276 { 277 String[] formats = request.getParameterValues("dc_format"); 278 if (formats != null && formats.length > 0 && !(formats.length == 1 && formats[0].equals(""))) 279 { 280 for (String format : formats) 281 { 282 XMLUtils.createElement(contentHandler, "format", format); 283 } 284 } 285 } 286 287 private void _saxPublisher (Request request) throws SAXException 288 { 289 String[] publishers = request.getParameterValues("dc_publisher"); 290 if (publishers != null && publishers.length > 0 && !(publishers.length == 1 && publishers[0].equals(""))) 291 { 292 for (String publisher : publishers) 293 { 294 XMLUtils.createElement(contentHandler, "publisher", publisher); 295 } 296 } 297 } 298 299 private void _saxRights (Request request) throws SAXException 300 { 301 String[] rights = request.getParameterValues("dc_rights"); 302 if (rights != null && rights.length > 0 && !(rights.length == 1 && rights[0].equals(""))) 303 { 304 for (String right : rights) 305 { 306 XMLUtils.createElement(contentHandler, "rights", right); 307 } 308 } 309 } 310 311 private void _saxMultisite(Request request) throws SAXException 312 { 313 boolean multisite = request.getParameter("multisite") != null; 314 if (multisite) 315 { 316 XMLUtils.createElement(contentHandler, "multisite"); 317 318 String[] sites = request.getParameterValues("sites"); 319 if (sites != null && sites.length > 0 && !(sites.length == 1 && sites[0].equals(""))) 320 { 321 for (String site : sites) 322 { 323 XMLUtils.createElement(contentHandler, "site", site); 324 } 325 } 326 } 327 } 328 329 private void _saxFormatCriteria () throws SAXException 330 { 331 XMLUtils.startElement(contentHandler, "formats"); 332 333 AttributesImpl attr = new AttributesImpl(); 334 attr.addCDATAAttribute("value", StringUtils.join(PDF_MIMETYPE, ",")); 335 XMLUtils.startElement(contentHandler, "format", attr); 336 new I18nizableText("plugin.web", "PLUGINS_WEB_DOCUMENT_FORMAT_PDF").toSAX(contentHandler); 337 XMLUtils.endElement(contentHandler, "format"); 338 339 attr = new AttributesImpl(); 340 attr.addCDATAAttribute("value", StringUtils.join(IMAGE_MIMETYPE, ",")); 341 XMLUtils.startElement(contentHandler, "format", attr); 342 new I18nizableText("plugin.web", "PLUGINS_WEB_DOCUMENT_FORMAT_IMAGE").toSAX(contentHandler); 343 XMLUtils.endElement(contentHandler, "format"); 344 345 attr = new AttributesImpl(); 346 attr.addCDATAAttribute("value", StringUtils.join(TEXT_MIMETYPE, ",")); 347 XMLUtils.startElement(contentHandler, "format", attr); 348 new I18nizableText("plugin.web", "PLUGINS_WEB_DOCUMENT_FORMAT_TEXT").toSAX(contentHandler); 349 XMLUtils.endElement(contentHandler, "format"); 350 351 attr = new AttributesImpl(); 352 attr.addCDATAAttribute("value", StringUtils.join(PRES_MIMETYPE, ",")); 353 XMLUtils.startElement(contentHandler, "format", attr); 354 new I18nizableText("plugin.web", "PLUGINS_WEB_DOCUMENT_FORMAT_PRES").toSAX(contentHandler); 355 XMLUtils.endElement(contentHandler, "format"); 356 357 attr = new AttributesImpl(); 358 attr.addCDATAAttribute("value", StringUtils.join(SPREADSHEET_MIMETYPE, ",")); 359 XMLUtils.startElement(contentHandler, "format", attr); 360 new I18nizableText("plugin.web", "PLUGINS_WEB_DOCUMENT_FORMAT_SPREADSHEET").toSAX(contentHandler); 361 XMLUtils.endElement(contentHandler, "format"); 362 363 attr = new AttributesImpl(); 364 attr.addCDATAAttribute("value", StringUtils.join(VIDEO_MIMETYPE, ",")); 365 XMLUtils.startElement(contentHandler, "format", attr); 366 new I18nizableText("plugin.web", "PLUGINS_WEB_DOCUMENT_FORMAT_VIDEO").toSAX(contentHandler); 367 XMLUtils.endElement(contentHandler, "format"); 368 369 attr = new AttributesImpl(); 370 attr.addCDATAAttribute("value", StringUtils.join(AUDIO_MIMETYPE, ",")); 371 XMLUtils.startElement(contentHandler, "format", attr); 372 new I18nizableText("plugin.web", "PLUGINS_WEB_DOCUMENT_FORMAT_AUDIO").toSAX(contentHandler); 373 XMLUtils.endElement(contentHandler, "format"); 374 375 XMLUtils.endElement(contentHandler, "formats"); 376 } 377 378 private void _saxPublisherCriteria () throws SAXException 379 { 380 Map<String, I18nizableText> entries = _dcProvider.getEntries("dc_publisher"); 381 if (entries != null) 382 { 383 XMLUtils.startElement(contentHandler, "publishers"); 384 385 for (String value : entries.keySet()) 386 { 387 AttributesImpl attr = new AttributesImpl(); 388 attr.addCDATAAttribute("value", value); 389 XMLUtils.startElement(contentHandler, "publisher", attr); 390 entries.get(value).toSAX(contentHandler); 391 XMLUtils.endElement(contentHandler, "publisher"); 392 } 393 394 XMLUtils.endElement(contentHandler, "publishers"); 395 } 396 397 } 398 399 private void _saxRightsCriteria () throws SAXException 400 { 401 Map<String, I18nizableText> entries = _dcProvider.getEntries("dc_rights"); 402 if (entries != null) 403 { 404 XMLUtils.startElement(contentHandler, "rights"); 405 406 for (String value : entries.keySet()) 407 { 408 AttributesImpl attr = new AttributesImpl(); 409 attr.addCDATAAttribute("value", value); 410 XMLUtils.startElement(contentHandler, "rights", attr); 411 entries.get(value).toSAX(contentHandler); 412 XMLUtils.endElement(contentHandler, "rights"); 413 } 414 415 XMLUtils.endElement(contentHandler, "rights"); 416 } 417 } 418 419 private void _addTextFieldQuery(Collection<Query> queries, String language, Request request) throws IllegalArgumentException 420 { 421 String text = request.getParameter("textfield"); 422 423 if (StringUtils.isNotBlank(text)) 424 { 425 queries.add(new FullTextQuery(text, language)); 426 } 427 } 428 429 private void _addPagesQuery(Collection<Query> queries, Request request) 430 { 431 List<Query> pageQueries = new ArrayList<>(); 432 433 String[] pages = request.getParameterValues("pages"); 434 if (pages != null && pages.length > 0 && !(pages.length == 1 && pages[0].equals(""))) 435 { 436 for (String pageIds : pages) 437 { 438 for (String pageId : StringUtils.split(pageIds, ",")) 439 { 440 pageQueries.add(() -> "{!join from=\"" + SolrFieldNames.ID + "\" to=\"" + SolrWebFieldNames.PAGE_ID + "\" v=\"" + new PageQuery(pageId, true).build().replace("\"", "\\\"") + "\"}"); 441 } 442 } 443 } 444 445 queries.add(new OrQuery(pageQueries)); 446 } 447 448 private void _addCreatorFieldQuery(Collection<Query> queries, Request request) 449 { 450 String creator = request.getParameter("dc_creator"); 451 452 if (StringUtils.isNotEmpty(creator)) 453 { 454 String escapedCreator = ClientUtils.escapeQueryChars(creator); 455 queries.add(new DublinCoreQuery("creator", escapedCreator)); 456 } 457 } 458 459 private void _addFormatQuery(Collection<Query> queries, Request request) 460 { 461 String[] formats = request.getParameterValues("dc_format"); 462 if (formats != null && formats.length > 0 && !(formats.length == 1 && formats[0].equals(""))) 463 { 464 List<Query> formatQueries = new ArrayList<>(); 465 466 for (String formatList : formats) 467 { 468 for (String value : StringUtils.split(formatList, ',')) 469 { 470 String escapedFormat = ClientUtils.escapeQueryChars(value); 471 formatQueries.add(new DublinCoreQuery("format", escapedFormat)); 472 } 473 } 474 475 queries.add(new OrQuery(formatQueries)); 476 } 477 } 478 479 private void _addPublisherQuery(Collection<Query> queries, Request request) 480 { 481 String[] publishers = request.getParameterValues("dc_publisher"); 482 if (publishers != null && publishers.length > 0 && !(publishers.length == 1 && publishers[0].equals(""))) 483 { 484 List<Query> pubQueries = new ArrayList<>(); 485 486 for (String pubList : publishers) 487 { 488 for (String value : StringUtils.split(pubList, ',')) 489 { 490 String escapedPub = ClientUtils.escapeQueryChars(value); 491 pubQueries.add(new DublinCoreQuery("publisher", escapedPub)); 492 } 493 } 494 495 queries.add(new OrQuery(pubQueries)); 496 } 497 } 498 499 private void _addRightsQuery(Collection<Query> queries, Request request) 500 { 501 String[] rights = request.getParameterValues("dc_rights"); 502 if (rights != null && rights.length > 0 && !(rights.length == 1 && rights[0].equals(""))) 503 { 504 List<Query> rightQueries = new ArrayList<>(); 505 506 for (String rightList : rights) 507 { 508 for (String value : StringUtils.split(rightList, ',')) 509 { 510 String escapedRight = ClientUtils.escapeQueryChars(value); 511 rightQueries.add(new DublinCoreQuery("rights", escapedRight)); 512 } 513 } 514 515 queries.add(new OrQuery(rightQueries)); 516 } 517 } 518}