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 Collection<String> getFields() 125 { 126 return Arrays.asList("DCTitle", "DCDescription", "DCSubject", "full"); 127 } 128 129 @Override 130 protected Map<String, FacetField> getFacets(Request request) throws IllegalArgumentException 131 { 132 return MapUtils.EMPTY_MAP; 133 } 134 135 @Override 136 protected Set<QueryFacet> getQueryFacets(Request request) 137 { 138 return Collections.EMPTY_SET; 139 } 140 141 @Override 142 protected Map<String, List<String>> getFacetValues(Request request, Collection<String> siteNames, String language) throws IllegalArgumentException 143 { 144 return MapUtils.EMPTY_MAP; 145 } 146 147 @Override 148 protected Collection<String> getQueryFacetValues(Request request) 149 { 150 return Collections.EMPTY_SET; 151 } 152 153 @Override 154 protected void saxHits(SearchResults<AmetysObject> results, int start, int maxResults) throws SAXException, IOException 155 { 156 long limit = Math.min(start + maxResults, results.getResults().getSize()); 157 float maxScore = results.getMaxScore(); 158 159 Iterator<SearchResult<AmetysObject>> it = results.getResults().iterator(); 160 for (int i = start; i < limit; i++) 161 { 162 SearchResult<AmetysObject> searchResult = it.next(); 163 float score = searchResult.getScore(); 164 AmetysObject ametysObject = searchResult.getObject(); 165 if (ametysObject instanceof Resource) 166 { 167 saxResourceHit(score, maxScore, (Resource) ametysObject); 168 } 169 } 170 } 171 172 @Override 173 protected Sort getSortField(Request request) 174 { 175 if (request.getParameter("sort-by-title-for-sorting") != null) 176 { 177 return new Sort(SolrFieldNames.TITLE_SORT, Order.ASC); 178 } 179 else if (request.getParameter("sort-by-date") != null) 180 { 181 return new Sort(SolrFieldNames.DATE_FOR_SORTING, Order.ASC); 182 } 183 184 return new Sort("score", Order.DESC); 185 } 186 187 @Override 188 protected List<Sort> getPrimarySortFields(Request request) 189 { 190 return Collections.emptyList(); 191 } 192 193 @Override 194 protected void saxFormFields(Request request, String siteName, String lang) throws SAXException 195 { 196 XMLUtils.createElement(contentHandler, "textfield"); 197 198 if (parameters.getParameterAsBoolean("search-by-creator", false)) 199 { 200 XMLUtils.createElement(contentHandler, "creator"); 201 } 202 203 if (parameters.getParameterAsBoolean("search-by-format", false)) 204 { 205 _saxFormatCriteria(); 206 } 207 208 if (parameters.getParameterAsBoolean("search-by-publisher", false)) 209 { 210 _saxPublisherCriteria(); 211 } 212 213 if (parameters.getParameterAsBoolean("search-by-rights", false)) 214 { 215 _saxRightsCriteria(); 216 } 217 218 _saxSitemapCriteria(); 219 220 boolean multisite = parameters.getParameterAsBoolean("search-multisite", false); 221 if (multisite) 222 { 223 XMLUtils.createElement(contentHandler, "multisite"); 224 225 XMLUtils.startElement(contentHandler, "sites"); 226 Collection<String> allSites = _siteManager.getSiteNames(); 227 for (String name : allSites) 228 { 229 Site site = _siteManager.getSite(name); 230 AttributesImpl attr = new AttributesImpl(); 231 attr.addCDATAAttribute("name", name); 232 if (name.equals(siteName)) 233 { 234 attr.addCDATAAttribute("current", "true"); 235 } 236 XMLUtils.createElement(contentHandler, "site", attr, site.getTitle()); 237 } 238 XMLUtils.endElement(contentHandler, "sites"); 239 } 240 } 241 242 private void _saxSitemapCriteria() throws SAXException 243 { 244 @SuppressWarnings("unchecked") 245 Map<String, Object> parentContext = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT); 246 if (parentContext == null) 247 { 248 parentContext = Collections.emptyMap(); 249 } 250 251 String[] pages = (String[]) parentContext.get("search-by-pages"); 252 if (ArrayUtils.isNotEmpty(pages)) 253 { 254 XMLUtils.startElement(contentHandler, "pages"); 255 256 for (String pageID : pages) 257 { 258 Page page = _resolver.resolveById(pageID); 259 AttributesImpl attr = new AttributesImpl(); 260 attr.addCDATAAttribute("id", pageID); 261 attr.addCDATAAttribute("path", page.getSitemap().getName() + "/" + page.getPathInSitemap()); 262 attr.addCDATAAttribute("title", page.getTitle()); 263 attr.addCDATAAttribute("long-title", page.getLongTitle()); 264 XMLUtils.createElement(contentHandler, "page", attr); 265 } 266 267 XMLUtils.endElement(contentHandler, "pages"); 268 } 269 } 270 271 @Override 272 protected void saxFormValues(Request request, int start, int offset) throws SAXException 273 { 274 _saxTextField(request); 275 _saxPages(request); 276 _saxCreatorField (request); 277 _saxFormats(request); 278 _saxPublisher(request); 279 _saxRights(request); 280 _saxMultisite(request); 281 } 282 283 private void _saxTextField(Request request) throws SAXException 284 { 285 String textfield = request.getParameter("textfield"); 286 XMLUtils.createElement(contentHandler, "textfield", textfield != null ? textfield : ""); 287 } 288 289 private void _saxPages(Request request) throws SAXException 290 { 291 String[] pages = request.getParameterValues("pages"); 292 if (pages != null && pages.length > 0 && !(pages.length == 1 && pages[0].equals(""))) 293 { 294 for (String path : pages) 295 { 296 XMLUtils.createElement(contentHandler, "page", path); 297 } 298 } 299 } 300 301 private void _saxCreatorField(Request request) throws SAXException 302 { 303 String creator = request.getParameter("creator"); 304 if (!StringUtils.isEmpty(creator)) 305 { 306 XMLUtils.createElement(contentHandler, "creator", creator != null ? creator : ""); 307 } 308 } 309 310 private void _saxFormats(Request request) throws SAXException 311 { 312 String[] formats = request.getParameterValues("dc_format"); 313 if (formats != null && formats.length > 0 && !(formats.length == 1 && formats[0].equals(""))) 314 { 315 for (String format : formats) 316 { 317 XMLUtils.createElement(contentHandler, "format", format); 318 } 319 } 320 } 321 322 private void _saxPublisher (Request request) throws SAXException 323 { 324 String[] publishers = request.getParameterValues("dc_publisher"); 325 if (publishers != null && publishers.length > 0 && !(publishers.length == 1 && publishers[0].equals(""))) 326 { 327 for (String publisher : publishers) 328 { 329 XMLUtils.createElement(contentHandler, "publisher", publisher); 330 } 331 } 332 } 333 334 private void _saxRights (Request request) throws SAXException 335 { 336 String[] rights = request.getParameterValues("dc_rights"); 337 if (rights != null && rights.length > 0 && !(rights.length == 1 && rights[0].equals(""))) 338 { 339 for (String right : rights) 340 { 341 XMLUtils.createElement(contentHandler, "rights", right); 342 } 343 } 344 } 345 346 private void _saxMultisite(Request request) throws SAXException 347 { 348 boolean multisite = request.getParameter("multisite") != null; 349 if (multisite) 350 { 351 XMLUtils.createElement(contentHandler, "multisite"); 352 353 String[] sites = request.getParameterValues("sites"); 354 if (sites != null && sites.length > 0 && !(sites.length == 1 && sites[0].equals(""))) 355 { 356 for (String site : sites) 357 { 358 XMLUtils.createElement(contentHandler, "site", site); 359 } 360 } 361 } 362 } 363 364 private void _saxFormatCriteria () throws SAXException 365 { 366 XMLUtils.startElement(contentHandler, "formats"); 367 368 AttributesImpl attr = new AttributesImpl(); 369 attr.addCDATAAttribute("value", StringUtils.join(PDF_MIMETYPE, ",")); 370 XMLUtils.startElement(contentHandler, "format", attr); 371 new I18nizableText("plugin.web", "PLUGINS_WEB_DOCUMENT_FORMAT_PDF").toSAX(contentHandler); 372 XMLUtils.endElement(contentHandler, "format"); 373 374 attr = new AttributesImpl(); 375 attr.addCDATAAttribute("value", StringUtils.join(IMAGE_MIMETYPE, ",")); 376 XMLUtils.startElement(contentHandler, "format", attr); 377 new I18nizableText("plugin.web", "PLUGINS_WEB_DOCUMENT_FORMAT_IMAGE").toSAX(contentHandler); 378 XMLUtils.endElement(contentHandler, "format"); 379 380 attr = new AttributesImpl(); 381 attr.addCDATAAttribute("value", StringUtils.join(TEXT_MIMETYPE, ",")); 382 XMLUtils.startElement(contentHandler, "format", attr); 383 new I18nizableText("plugin.web", "PLUGINS_WEB_DOCUMENT_FORMAT_TEXT").toSAX(contentHandler); 384 XMLUtils.endElement(contentHandler, "format"); 385 386 attr = new AttributesImpl(); 387 attr.addCDATAAttribute("value", StringUtils.join(PRES_MIMETYPE, ",")); 388 XMLUtils.startElement(contentHandler, "format", attr); 389 new I18nizableText("plugin.web", "PLUGINS_WEB_DOCUMENT_FORMAT_PRES").toSAX(contentHandler); 390 XMLUtils.endElement(contentHandler, "format"); 391 392 attr = new AttributesImpl(); 393 attr.addCDATAAttribute("value", StringUtils.join(SPREADSHEET_MIMETYPE, ",")); 394 XMLUtils.startElement(contentHandler, "format", attr); 395 new I18nizableText("plugin.web", "PLUGINS_WEB_DOCUMENT_FORMAT_SPREADSHEET").toSAX(contentHandler); 396 XMLUtils.endElement(contentHandler, "format"); 397 398 attr = new AttributesImpl(); 399 attr.addCDATAAttribute("value", StringUtils.join(VIDEO_MIMETYPE, ",")); 400 XMLUtils.startElement(contentHandler, "format", attr); 401 new I18nizableText("plugin.web", "PLUGINS_WEB_DOCUMENT_FORMAT_VIDEO").toSAX(contentHandler); 402 XMLUtils.endElement(contentHandler, "format"); 403 404 attr = new AttributesImpl(); 405 attr.addCDATAAttribute("value", StringUtils.join(AUDIO_MIMETYPE, ",")); 406 XMLUtils.startElement(contentHandler, "format", attr); 407 new I18nizableText("plugin.web", "PLUGINS_WEB_DOCUMENT_FORMAT_AUDIO").toSAX(contentHandler); 408 XMLUtils.endElement(contentHandler, "format"); 409 410 XMLUtils.endElement(contentHandler, "formats"); 411 } 412 413 private void _saxPublisherCriteria () throws SAXException 414 { 415 Map<String, I18nizableText> entries = _dcProvider.getEntries("dc_publisher"); 416 if (entries != null) 417 { 418 XMLUtils.startElement(contentHandler, "publishers"); 419 420 for (String value : entries.keySet()) 421 { 422 AttributesImpl attr = new AttributesImpl(); 423 attr.addCDATAAttribute("value", value); 424 XMLUtils.startElement(contentHandler, "publisher", attr); 425 entries.get(value).toSAX(contentHandler); 426 XMLUtils.endElement(contentHandler, "publisher"); 427 } 428 429 XMLUtils.endElement(contentHandler, "publishers"); 430 } 431 432 } 433 434 private void _saxRightsCriteria () throws SAXException 435 { 436 Map<String, I18nizableText> entries = _dcProvider.getEntries("dc_rights"); 437 if (entries != null) 438 { 439 XMLUtils.startElement(contentHandler, "rights"); 440 441 for (String value : entries.keySet()) 442 { 443 AttributesImpl attr = new AttributesImpl(); 444 attr.addCDATAAttribute("value", value); 445 XMLUtils.startElement(contentHandler, "rights", attr); 446 entries.get(value).toSAX(contentHandler); 447 XMLUtils.endElement(contentHandler, "rights"); 448 } 449 450 XMLUtils.endElement(contentHandler, "rights"); 451 } 452 } 453 454 private void _addTextFieldQuery(Collection<Query> queries, String language, Request request) throws IllegalArgumentException 455 { 456 String text = request.getParameter("textfield"); 457 458 if (StringUtils.isNotBlank(text)) 459 { 460 queries.add(new FullTextQuery(text, language)); 461 } 462 } 463 464 private void _addPagesQuery(Collection<Query> queries, Request request) 465 { 466 List<Query> pageQueries = new ArrayList<>(); 467 468 String[] pages = request.getParameterValues("pages"); 469 if (pages != null && pages.length > 0 && !(pages.length == 1 && pages[0].equals(""))) 470 { 471 for (String pageIds : pages) 472 { 473 for (String pageId : StringUtils.split(pageIds, ",")) 474 { 475 pageQueries.add(new JoinQuery(new PageQuery(pageId, true), SolrWebFieldNames.ATTACHMENT_PAGE_ID)); 476 } 477 } 478 } 479 480 queries.add(new OrQuery(pageQueries)); 481 } 482 483 private void _addCreatorFieldQuery(Collection<Query> queries, Request request) 484 { 485 String creator = request.getParameter("dc_creator"); 486 487 if (StringUtils.isNotEmpty(creator)) 488 { 489 String escapedCreator = ClientUtils.escapeQueryChars(creator); 490 queries.add(new DublinCoreQuery("creator", escapedCreator)); 491 } 492 } 493 494 private void _addFormatQuery(Collection<Query> queries, Request request) 495 { 496 String[] formats = request.getParameterValues("dc_format"); 497 if (formats != null && formats.length > 0 && !(formats.length == 1 && formats[0].equals(""))) 498 { 499 List<Query> formatQueries = new ArrayList<>(); 500 501 for (String formatList : formats) 502 { 503 for (String value : StringUtils.split(formatList, ',')) 504 { 505 String escapedFormat = ClientUtils.escapeQueryChars(value); 506 formatQueries.add(new DublinCoreQuery("format", escapedFormat)); 507 } 508 } 509 510 queries.add(new OrQuery(formatQueries)); 511 } 512 } 513 514 private void _addPublisherQuery(Collection<Query> queries, Request request) 515 { 516 String[] publishers = request.getParameterValues("dc_publisher"); 517 if (publishers != null && publishers.length > 0 && !(publishers.length == 1 && publishers[0].equals(""))) 518 { 519 List<Query> pubQueries = new ArrayList<>(); 520 521 for (String pubList : publishers) 522 { 523 for (String value : StringUtils.split(pubList, ',')) 524 { 525 String escapedPub = ClientUtils.escapeQueryChars(value); 526 pubQueries.add(new DublinCoreQuery("publisher", escapedPub)); 527 } 528 } 529 530 queries.add(new OrQuery(pubQueries)); 531 } 532 } 533 534 private void _addRightsQuery(Collection<Query> queries, Request request) 535 { 536 String[] rights = request.getParameterValues("dc_rights"); 537 if (rights != null && rights.length > 0 && !(rights.length == 1 && rights[0].equals(""))) 538 { 539 List<Query> rightQueries = new ArrayList<>(); 540 541 for (String rightList : rights) 542 { 543 for (String value : StringUtils.split(rightList, ',')) 544 { 545 String escapedRight = ClientUtils.escapeQueryChars(value); 546 rightQueries.add(new DublinCoreQuery("rights", escapedRight)); 547 } 548 } 549 550 queries.add(new OrQuery(rightQueries)); 551 } 552 } 553}