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