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