1 /** 2 * Copyright (C) 2005-2010 Alfresco Software Limited. 3 * 4 * This file is part of Alfresco 5 * 6 * Alfresco is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU Lesser General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * Alfresco is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public License 17 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 /** 21 * Search component. 22 * 23 * @namespace Alfresco 24 * @class Alfresco.Search 25 */ 26 (function() 27 { 28 /** 29 * YUI Library aliases 30 */ 31 var Dom = YAHOO.util.Dom, 32 Event = YAHOO.util.Event; 33 34 /** 35 * Alfresco Slingshot aliases 36 */ 37 var $html = Alfresco.util.encodeHTML; 38 39 /** 40 * Search constructor. 41 * 42 * @param {String} htmlId The HTML id of the parent element 43 * @return {Alfresco.Search} The new Search instance 44 * @constructor 45 */ 46 Alfresco.Search = function(htmlId) 47 { 48 Alfresco.Search.superclass.constructor.call(this, "Alfresco.Search", htmlId, ["button", "container", "datasource", "datatable", "paginator", "json"]); 49 50 // Decoupled event listeners 51 YAHOO.Bubbling.on("onSearch", this.onSearch, this); 52 53 return this; 54 }; 55 56 YAHOO.extend(Alfresco.Search, Alfresco.component.Base, 57 { 58 /** 59 * Object container for initialization options 60 * 61 * @property options 62 * @type object 63 */ 64 options: 65 { 66 /** 67 * Current siteId 68 * 69 * @property siteId 70 * @type string 71 */ 72 siteId: "", 73 74 /** 75 * Current site title 76 * 77 * @property siteTitle 78 * @type string 79 */ 80 siteTitle: "", 81 82 /** 83 * Maximum number of results displayed. 84 * 85 * @property maxSearchResults 86 * @type int 87 * @default 250 88 */ 89 maxSearchResults: 250, 90 91 /** 92 * Results page size. 93 * 94 * @property pageSize 95 * @type int 96 * @default 50 97 */ 98 pageSize: 50, 99 100 /** 101 * Search term to use for the initial search 102 * @property initialSearchTerm 103 * @type string 104 * @default "" 105 */ 106 initialSearchTerm: "", 107 108 /** 109 * Search tag to use for the initial search 110 * @property initialSearchTag 111 * @type string 112 * @default "" 113 */ 114 initialSearchTag: "", 115 116 /** 117 * States whether all sites should be searched. 118 * 119 * @property initialSearchAllSites 120 * @type boolean 121 */ 122 initialSearchAllSites: true, 123 124 /** 125 * States whether repository should be searched. 126 * This is in preference to current or all sites. 127 * 128 * @property initialSearchRepository 129 * @type boolean 130 */ 131 initialSearchRepository: false, 132 133 /** 134 * Sort property to use for the initial search. 135 * Empty default value will use score relevance default. 136 * @property initialSort 137 * @type string 138 * @default "" 139 */ 140 initialSort: "", 141 142 /** 143 * Advanced Search query - forms data json format based search. 144 * @property searchQuery 145 * @type string 146 * @default "" 147 */ 148 searchQuery: "", 149 150 /** 151 * Number of characters required for a search. 152 * 153 * @property minSearchTermLength 154 * @type int 155 * @default 1 156 */ 157 minSearchTermLength: 1 158 }, 159 160 /** 161 * Search term used for the last search. 162 */ 163 searchTerm: "", 164 165 /** 166 * Search tag used for the last search. 167 */ 168 searchTag: "", 169 170 /** 171 * Whether the search was over all sites or just the current one 172 */ 173 searchAllSites: true, 174 175 /** 176 * Whether the search is over the entire repository - in preference to site or all sites 177 */ 178 searchRepository: false, 179 180 /** 181 * Search sort used for the last search. 182 */ 183 searchSort: "", 184 185 /** 186 * Number of search results. 187 */ 188 resultsCount: 0, 189 190 /** 191 * Current visible page index - counts from 1 192 */ 193 currentPage: 1, 194 195 /** 196 * True if there are more results than the ones listed in the table. 197 */ 198 hasMoreResults: false, 199 200 /** 201 * Fired by YUI when parent element is available for scripting. 202 * Component initialisation, including instantiation of YUI widgets and event listener binding. 203 * 204 * @method onReady 205 */ 206 onReady: function Search_onReady() 207 { 208 var me = this; 209 210 // DataSource definition 211 var uriSearchResults = Alfresco.constants.PROXY_URI_RELATIVE + "slingshot/search?"; 212 this.widgets.dataSource = new YAHOO.util.DataSource(uriSearchResults, 213 { 214 responseType: YAHOO.util.DataSource.TYPE_JSON, 215 connXhrMode: "queueRequests", 216 responseSchema: 217 { 218 resultsList: "items" 219 } 220 }); 221 222 // YUI Paginator definition 223 var handlePagination = function Search_handlePagination(state, me) 224 { 225 me.currentPage = state.page; 226 me.widgets.paginator.setState(state); 227 }; 228 this.widgets.paginator = new YAHOO.widget.Paginator( 229 { 230 containers: [this.id + "-paginator-top", this.id + "-paginator-bottom"], 231 rowsPerPage: this.options.pageSize, 232 initialPage: 1, 233 template: this.msg("pagination.template"), 234 pageReportTemplate: this.msg("pagination.template.page-report"), 235 previousPageLinkLabel: this.msg("pagination.previousPageLinkLabel"), 236 nextPageLinkLabel: this.msg("pagination.nextPageLinkLabel") 237 }); 238 this.widgets.paginator.subscribe("changeRequest", handlePagination, this); 239 240 // setup of the datatable. 241 this._setupDataTable(); 242 243 // set initial value and register the "enter" event on the search text field 244 var queryInput = Dom.get(this.id + "-search-text"); 245 queryInput.value = this.options.initialSearchTerm; 246 247 this.widgets.enterListener = new YAHOO.util.KeyListener(queryInput, 248 { 249 keys: YAHOO.util.KeyListener.KEY.ENTER 250 }, 251 { 252 fn: me._searchEnterHandler, 253 scope: this, 254 correctScope: true 255 }, "keydown").enable(); 256 257 // trigger the initial search 258 YAHOO.Bubbling.fire("onSearch", 259 { 260 searchTerm: this.options.initialSearchTerm, 261 searchTag: this.options.initialSearchTag, 262 searchSort: this.options.initialSort, 263 searchAllSites: this.options.initialSearchAllSites, 264 searchRepository: this.options.initialSearchRepository 265 }); 266 267 // toggle site scope links 268 var toggleLink = Dom.get(this.id + "-site-link"); 269 Event.addListener(toggleLink, "click", this.onSiteSearch, this, true); 270 toggleLink = Dom.get(this.id + "-all-sites-link"); 271 Event.addListener(toggleLink, "click", this.onAllSiteSearch, this, true); 272 toggleLink = Dom.get(this.id + "-repo-link"); 273 Event.addListener(toggleLink, "click", this.onRepositorySearch, this, true); 274 275 // search YUI button 276 this.widgets.searchButton = Alfresco.util.createYUIButton(this, "search-button", this.onSearchClick); 277 278 // menu button for sort options 279 this.widgets.sortButton = new YAHOO.widget.Button(this.id + "-sort-menubutton", 280 { 281 type: "menu", 282 menu: this.id + "-sort-menu", 283 menualignment: ["tr", "br"], 284 lazyloadmenu: false 285 }); 286 // set initially selected sort button label 287 var menuItems = this.widgets.sortButton.getMenu().getItems(); 288 for (var m in menuItems) 289 { 290 if (menuItems[m].value === this.options.initialSort) 291 { 292 this.widgets.sortButton.set("label", this.msg("label.sortby", menuItems[m].cfg.getProperty("text"))); 293 break; 294 } 295 } 296 // event handler for sort menu 297 this.widgets.sortButton.getMenu().subscribe("click", function(p_sType, p_aArgs) 298 { 299 var menuItem = p_aArgs[1]; 300 if (menuItem) 301 { 302 me.refreshSearch( 303 { 304 searchSort: menuItem.value 305 }); 306 } 307 }); 308 309 // Hook action events 310 var fnActionHandler = function Search_fnActionHandler(layer, args) 311 { 312 var owner = YAHOO.Bubbling.getOwnerByTagName(args[1].anchor, "span"); 313 if (owner !== null) 314 { 315 if (typeof me[owner.className] == "function") 316 { 317 args[1].stop = true; 318 var tagId = owner.id.substring(me.id.length + 1); 319 me[owner.className].call(me, tagId); 320 } 321 } 322 return true; 323 }; 324 YAHOO.Bubbling.addDefaultAction("search-tag", fnActionHandler); 325 326 // Finally show the component body here to prevent UI artifacts on YUI button decoration 327 Dom.setStyle(this.id + "-body", "visibility", "visible"); 328 }, 329 330 _setupDataTable: function Search_setupDataTable() 331 { 332 /** 333 * DataTable Cell Renderers 334 * 335 * Each cell has a custom renderer defined as a custom function. See YUI documentation for details. 336 * These MUST be inline in order to have access to the Alfresco.Search class (via the "me" variable). 337 */ 338 var me = this; 339 340 /** 341 * Thumbnail custom datacell formatter 342 * 343 * @method renderCellThumbnail 344 * @param elCell {object} 345 * @param oRecord {object} 346 * @param oColumn {object} 347 * @param oData {object|string} 348 */ 349 renderCellThumbnail = function Search_renderCellThumbnail(elCell, oRecord, oColumn, oData) 350 { 351 oColumn.width = 100; 352 oColumn.height = 100; 353 Dom.setStyle(elCell.parentNode, "width", oColumn.width + "px"); 354 Dom.setStyle(elCell, "height", oColumn.height + "px"); 355 Dom.addClass(elCell, "thumbnail-cell"); 356 357 var url = me._getBrowseUrlForRecord(oRecord); 358 var imageUrl = Alfresco.constants.URL_RESCONTEXT + 'components/search/images/generic-result.png'; 359 360 // use the preview image for a document type 361 var dataType = oRecord.getData("type"); 362 switch (dataType) 363 { 364 case "document": 365 imageUrl = Alfresco.constants.PROXY_URI_RELATIVE + "api/node/" + oRecord.getData("nodeRef").replace(":/", ""); 366 imageUrl += "/content/thumbnails/doclib?c=queue&ph=true"; 367 break; 368 369 case "folder": 370 imageUrl = Alfresco.constants.URL_RESCONTEXT + 'components/search/images/folder.png'; 371 break; 372 373 case "blogpost": 374 imageUrl = Alfresco.constants.URL_RESCONTEXT + 'components/search/images/blog-post.png'; 375 break; 376 377 case "forumpost": 378 imageUrl = Alfresco.constants.URL_RESCONTEXT + 'components/search/images/topic-post.png'; 379 break; 380 381 case "calendarevent": 382 imageUrl = Alfresco.constants.URL_RESCONTEXT + 'components/search/images/calendar-event.png'; 383 break; 384 385 case "wikipage": 386 imageUrl = Alfresco.constants.URL_RESCONTEXT + 'components/search/images/wiki-page.png'; 387 break; 388 389 case "link": 390 imageUrl = Alfresco.constants.URL_RESCONTEXT + 'components/search/images/link.png'; 391 break; 392 393 case "datalist": 394 imageUrl = Alfresco.constants.URL_RESCONTEXT + 'components/search/images/datalist.png'; 395 break; 396 397 case "datalistitem": 398 imageUrl = Alfresco.constants.URL_RESCONTEXT + 'components/search/images/datalistitem.png'; 399 break; 400 } 401 402 // Render the cell 403 var name = oRecord.getData("displayName"); 404 var htmlName = $html(name); 405 var html = '<span><a href="' + url + '"><img src="' + imageUrl + '" alt="' + htmlName + '" title="' + htmlName + '" /></a></span>'; 406 if (dataType === "document") 407 { 408 var viewUrl = Alfresco.constants.PROXY_URI_RELATIVE + "api/node/content/" + oRecord.getData("nodeRef").replace(":/", "") + "/" + oRecord.getData("name"); 409 html = '<div class="action-overlay">' + 410 '<a href="' + encodeURI(viewUrl) + '" target="_blank"><img title="' + $html(me.msg("label.viewinbrowser")) + 411 '" src="' + Alfresco.constants.URL_RESCONTEXT + 'components/search/images/view-in-browser-16.png" width="16" height="16"/></a>' + 412 '<a href="' + encodeURI(viewUrl + "?a=true") + '" style="padding-left:4px" target="_blank"><img title="' + $html(me.msg("label.download")) + 413 '" src="' + Alfresco.constants.URL_RESCONTEXT + 'components/search/images/download-16.png" width="16" height="16"/></a>' + 414 '</div>' + html; 415 } 416 elCell.innerHTML = html; 417 }; 418 419 /** 420 * Description/detail custom cell formatter 421 * 422 * @method renderCellDescription 423 * @param elCell {object} 424 * @param oRecord {object} 425 * @param oColumn {object} 426 * @param oData {object|string} 427 */ 428 renderCellDescription = function Search_renderCellDescription(elCell, oRecord, oColumn, oData) 429 { 430 // apply styles 431 Dom.setStyle(elCell.parentNode, "line-height", "1.5em"); 432 433 // site and repository items render with different information available 434 var site = oRecord.getData("site"); 435 var url = me._getBrowseUrlForRecord(oRecord); 436 437 // displayname and link to details page 438 var displayName = oRecord.getData("displayName"); 439 var desc = '<h3 class="itemname"><a href="' + url + '" class="theme-color-1">' + $html(displayName) + '</a>'; 440 // add title (if any) to displayname area 441 var title = oRecord.getData("title"); 442 if (title && title !== displayName) 443 { 444 desc += '<span class="title">(' + $html(title) + ')</span>'; 445 } 446 desc += '</h3>'; 447 448 // description (if any) 449 var txt = oRecord.getData("description"); 450 if (txt) 451 { 452 desc += '<div class="details meta">' + $html(txt) + '</div>'; 453 } 454 455 // detailed information, includes site etc. type specific 456 desc += '<div class="details">'; 457 var type = oRecord.getData("type"); 458 switch (type) 459 { 460 case "document": 461 case "folder": 462 case "blogpost": 463 case "forumpost": 464 case "calendarevent": 465 case "wikipage": 466 case "datalist": 467 case "datalistitem": 468 case "link": 469 desc += me.msg("label." + type); 470 break; 471 472 default: 473 desc += me.msg("label.unknown"); 474 break; 475 } 476 477 // link to the site and other meta-data details 478 if (site) 479 { 480 desc += ' ' + me.msg("message.insite"); 481 desc += ' <a href="' + Alfresco.constants.URL_PAGECONTEXT + 'site/' + $html(site.shortName) + '/dashboard">' + $html(site.title) + '</a>'; 482 } 483 if (oRecord.getData("size") !== -1) 484 { 485 desc += ' ' + me.msg("message.ofsize"); 486 desc += ' <span class="meta">' + Alfresco.util.formatFileSize(oRecord.getData("size")) + '</span>'; 487 } 488 if (oRecord.getData("modifiedBy")) 489 { 490 desc += ' ' + me.msg("message.modifiedby"); 491 desc += ' <a href="' + Alfresco.constants.URL_PAGECONTEXT + 'user/' + encodeURI(oRecord.getData("modifiedByUser")) + '/profile">' + $html(oRecord.getData("modifiedBy")) + '</a>'; 492 } 493 desc += ' ' + me.msg("message.modifiedon") + ' <span class="meta">' + Alfresco.util.formatDate(oRecord.getData("modifiedOn")) + '</span>'; 494 desc += '</div>'; 495 496 // folder path (if any) 497 if (type === "document" || type === "folder") 498 { 499 var path = oRecord.getData("path"); 500 if (site) 501 { 502 if (path === null || path === undefined) 503 { 504 path = ""; 505 } 506 desc += '<div class="details">' + me.msg("message.infolderpath") + 507 ': <a href="' + me._getBrowseUrlForFolderPath(path, site) + '">' + $html('/' + path) + '</a></div>'; 508 } 509 else 510 { 511 if (path) 512 { 513 desc += '<div class="details">' + me.msg("message.infolderpath") + 514 ': <a href="' + me._getBrowseUrlForFolderPath(path) + '">' + $html(path) + '</a></div>'; 515 } 516 } 517 } 518 519 // tags (if any) 520 var tags = oRecord.getData("tags"); 521 if (tags.length !== 0) 522 { 523 var i, j; 524 desc += '<div class="details"><span class="tags">' + me.msg("label.tags") + ': '; 525 for (i = 0, j = tags.length; i < j; i++) 526 { 527 desc += '<span id="' + me.id + '-' + $html(tags[i]) + '" class="searchByTag"><a class="search-tag" href="#">' + $html(tags[i]) + '</a> </span>'; 528 } 529 desc += '</span></div>'; 530 } 531 532 elCell.innerHTML = desc; 533 }; 534 535 // DataTable column defintions 536 var columnDefinitions = [ 537 { 538 key: "image", label: me.msg("message.preview"), sortable: false, formatter: renderCellThumbnail, width: 100 539 }, 540 { 541 key: "summary", label: me.msg("label.description"), sortable: false, formatter: renderCellDescription 542 }]; 543 544 // DataTable definition 545 this.widgets.dataTable = new YAHOO.widget.DataTable(this.id + "-results", columnDefinitions, this.widgets.dataSource, 546 { 547 renderLoopSize: Alfresco.util.RENDERLOOPSIZE, 548 initialLoad: false, 549 paginator: this.widgets.paginator, 550 MSG_LOADING: "" 551 }); 552 553 // show initial message 554 this._setDefaultDataTableErrors(this.widgets.dataTable); 555 if (this.options.initialSearchTerm.length === 0 && this.options.initialSearchTag.length === 0) 556 { 557 this.widgets.dataTable.set("MSG_EMPTY", ""); 558 } 559 560 // Override abstract function within DataTable to set custom error message 561 this.widgets.dataTable.doBeforeLoadData = function Search_doBeforeLoadData(sRequest, oResponse, oPayload) 562 { 563 if (oResponse.error) 564 { 565 try 566 { 567 var response = YAHOO.lang.JSON.parse(oResponse.responseText); 568 me.widgets.dataTable.set("MSG_ERROR", response.message); 569 } 570 catch(e) 571 { 572 me._setDefaultDataTableErrors(me.widgets.dataTable); 573 } 574 } 575 else if (oResponse.results) 576 { 577 // clear the empty error message 578 me.widgets.dataTable.set("MSG_EMPTY", ""); 579 580 // update the results count, update hasMoreResults. 581 me.hasMoreResults = (oResponse.results.length > me.options.maxSearchResults); 582 if (me.hasMoreResults) 583 { 584 oResponse.results = oResponse.results.slice(0, me.options.maxSearchResults); 585 me.resultsCount = me.options.maxSearchResults; 586 } 587 else 588 { 589 me.resultsCount = oResponse.results.length; 590 } 591 592 if (me.resultsCount > me.options.pageSize) 593 { 594 Dom.removeClass(me.id + "-paginator-top", "hidden"); 595 Dom.removeClass(me.id + "-search-bar-bottom", "hidden"); 596 } 597 } 598 // Must return true to have the "Loading..." message replaced by the error message 599 return true; 600 }; 601 602 // Rendering complete event handler 603 me.widgets.dataTable.subscribe("renderEvent", function() 604 { 605 // Update the paginator 606 me.widgets.paginator.setState( 607 { 608 page: me.currentPage, 609 totalRecords: me.resultsCount 610 }); 611 me.widgets.paginator.render(); 612 }); 613 }, 614 615 /** 616 * Constructs the completed browse url for a record. 617 * @param record {string} the record 618 */ 619 _getBrowseUrlForRecord: function Search__getBrowseUrlForRecord(record) 620 { 621 var url = null; 622 623 var name = record.getData("name"), 624 type = record.getData("type"), 625 site = record.getData("site"), 626 path = record.getData("path"); 627 628 switch (type) 629 { 630 case "document": 631 { 632 url = "document-details?nodeRef=" + record.getData("nodeRef"); 633 break; 634 } 635 636 case "folder": 637 { 638 if (path !== null) 639 { 640 if (site) 641 { 642 url = "documentlibrary?path=" + encodeURIComponent(this._buildSpaceNamePath(path.split("/"), name)); 643 } 644 else 645 { 646 url = "repository?path=" + encodeURIComponent(this._buildSpaceNamePath(path.split("/").slice(2), name)); 647 } 648 } 649 break; 650 } 651 652 case "blogpost": 653 { 654 url = "blog-postview?postId=" + name; 655 break; 656 } 657 658 case "forumpost": 659 { 660 url = "discussions-topicview?topicId=" + name; 661 break; 662 } 663 664 case "calendarevent": 665 { 666 url = record.getData("container") + "?date=" + Alfresco.util.formatDate(record.getData("modifiedOn"), "yyyy-mm-dd"); 667 break; 668 } 669 670 case "wikipage": 671 { 672 url = "wiki-page?title=" + name; 673 break; 674 } 675 676 case "link": 677 { 678 url = "links-view?linkId=" + name; 679 break; 680 } 681 682 case "datalist": 683 case "datalistitem": 684 { 685 url = "data-lists?list=" + name; 686 break; 687 } 688 } 689 690 if (url !== null) 691 { 692 // browse urls always go to a page. We assume that the url contains the page name and all 693 // parameters. Add the absolute path and the optional site param 694 if (site) 695 { 696 url = Alfresco.constants.URL_PAGECONTEXT + "site/" + site.shortName + "/" + url; 697 } 698 else 699 { 700 url = Alfresco.constants.URL_PAGECONTEXT + url; 701 } 702 } 703 704 return (url !== null ? url : '#'); 705 }, 706 707 /** 708 * Constructs the folder url for a record. 709 * @param path {string} folder path 710 * For a site relative item this can be empty (root of doclib) or any path - without a leading slash 711 * For a repository item, this can never be empty - but will contain leading slash and Company Home root 712 */ 713 _getBrowseUrlForFolderPath: function Search__getBrowseUrlForFolderPath(path, site) 714 { 715 var url = null; 716 if (site) 717 { 718 url = Alfresco.constants.URL_PAGECONTEXT + "site/" + site.shortName + "/documentlibrary?path=" + encodeURIComponent('/' + path); 719 } 720 else 721 { 722 url = Alfresco.constants.URL_PAGECONTEXT + "repository?path=" + encodeURIComponent('/' + path.split('/').slice(2).join('/')); 723 } 724 return url; 725 }, 726 727 _buildSpaceNamePath: function Search__buildSpaceNamePath(pathParts, name) 728 { 729 return (pathParts.length !== 0 ? ("/" + pathParts.join("/")) : "") + "/" + name; 730 }, 731 732 /** 733 * DEFAULT ACTION EVENT HANDLERS 734 * Handlers for standard events fired from YUI widgets, e.g. "click" 735 */ 736 737 /** 738 * Perform a search for a given tag 739 * The tag is simply handled as search term 740 */ 741 searchByTag: function Search_searchTag(param) 742 { 743 this.refreshSearch( 744 { 745 searchTag: param, 746 searchTerm: "", 747 searchQuery: "" 748 }); 749 }, 750 751 /** 752 * Refresh the search page by full URL refresh 753 * 754 * @method refreshSearch 755 * @param args {object} search args 756 */ 757 refreshSearch: function Search_refreshSearch(args) 758 { 759 var searchTerm = this.searchTerm; 760 if (args.searchTerm !== undefined) 761 { 762 searchTerm = args.searchTerm; 763 } 764 var searchTag = this.searchTag; 765 if (args.searchTag !== undefined) 766 { 767 searchTag = args.searchTag; 768 } 769 var searchAllSites = this.searchAllSites; 770 if (args.searchAllSites !== undefined) 771 { 772 searchAllSites = args.searchAllSites; 773 } 774 var searchRepository = this.searchRepository; 775 if (args.searchRepository !== undefined) 776 { 777 searchRepository = args.searchRepository; 778 } 779 var searchSort = this.searchSort; 780 if (args.searchSort !== undefined) 781 { 782 searchSort = args.searchSort; 783 } 784 var searchQuery = this.options.searchQuery; 785 if (args.searchQuery !== undefined) 786 { 787 searchQuery = args.searchQuery; 788 } 789 790 // redirect back to the search page - with appropriate site context 791 var url = Alfresco.constants.URL_PAGECONTEXT; 792 if (this.options.siteId.length !== 0) 793 { 794 url += "site/" + this.options.siteId + "/"; 795 } 796 797 // add search data webscript arguments 798 url += "search?t=" + encodeURIComponent(searchTerm); 799 if (searchSort.length !== 0) 800 { 801 url += "&s=" + searchSort; 802 } 803 if (searchQuery.length !== 0) 804 { 805 // if we have a query (already encoded), then apply it 806 // other options such as tag, terms, all sites, repo etc. are trumped 807 url += "&q=" + searchQuery; 808 } 809 else 810 { 811 if (searchTag.length !== 0) 812 { 813 url += "&tag=" + encodeURIComponent(searchTag); 814 } 815 url += "&a=" + searchAllSites + "&r=" + searchRepository; 816 } 817 window.location = url; 818 }, 819 820 /** 821 * BUBBLING LIBRARY EVENT HANDLERS FOR PAGE EVENTS 822 * Disconnected event handlers for inter-component event notification 823 */ 824 825 /** 826 * Execute Search event handler 827 * 828 * @method onSearch 829 * @param layer {object} Event fired 830 * @param args {array} Event parameters (depends on event type) 831 */ 832 onSearch: function Search_onSearch(layer, args) 833 { 834 var obj = args[1]; 835 if (obj !== null) 836 { 837 var searchTerm = this.searchTerm; 838 if (obj.searchTerm !== undefined) 839 { 840 searchTerm = obj.searchTerm; 841 } 842 var searchTag = this.searchTag; 843 if (obj.searchTag !== undefined) 844 { 845 searchTag = obj.searchTag; 846 } 847 var searchAllSites = this.searchAllSites; 848 if (obj.searchAllSites !== undefined) 849 { 850 searchAllSites = obj.searchAllSites; 851 } 852 var searchRepository = this.searchRepository; 853 if (obj.searchRepository !== undefined) 854 { 855 searchRepository = obj.searchRepository; 856 } 857 var searchSort = this.searchSort; 858 if (obj.searchSort !== undefined) 859 { 860 searchSort = obj.searchSort; 861 } 862 this._performSearch( 863 { 864 searchTerm: searchTerm, 865 searchTag: searchTag, 866 searchAllSites: searchAllSites, 867 searchRepository: searchRepository, 868 searchSort: searchSort 869 }); 870 } 871 }, 872 873 /** 874 * Event handler that gets fired when user clicks the Search button. 875 * 876 * @method onSearchClick 877 * @param e {object} DomEvent 878 * @param obj {object} Object passed back from addListener method 879 */ 880 onSearchClick: function Search_onSearchClick(e, obj) 881 { 882 this.refreshSearch( 883 { 884 searchTag: "", 885 searchTerm: YAHOO.lang.trim(Dom.get(this.id + "-search-text").value), 886 searchQuery: "" 887 }); 888 }, 889 890 /** 891 * Click event for Current Site search link 892 * 893 * @method onSiteSearch 894 */ 895 onSiteSearch: function Search_onSiteSearch(e, args) 896 { 897 this.refreshSearch( 898 { 899 searchAllSites: false, 900 searchRepository: false 901 }); 902 }, 903 904 /** 905 * Click event for All Sites search link 906 * 907 * @method onAllSiteSearch 908 */ 909 onAllSiteSearch: function Search_onAllSiteSearch(e, args) 910 { 911 this.refreshSearch( 912 { 913 searchAllSites: true, 914 searchRepository: false 915 }); 916 }, 917 918 /** 919 * Click event for Repository search link 920 * 921 * @method onRepositorySearch 922 */ 923 onRepositorySearch: function Search_onRepositorySearch(e, args) 924 { 925 this.refreshSearch( 926 { 927 searchRepository: true 928 }); 929 }, 930 931 /** 932 * Search text box ENTER key event handler 933 * 934 * @method _searchEnterHandler 935 */ 936 _searchEnterHandler: function Search__searchEnterHandler(e, args) 937 { 938 this.refreshSearch( 939 { 940 searchTag: "", 941 searchTerm: YAHOO.lang.trim(Dom.get(this.id + "-search-text").value), 942 searchQuery: "" 943 }); 944 }, 945 946 /** 947 * Updates search results list by calling data webscript with current site and query term 948 * 949 * @method _performSearch 950 * @param args {object} search args 951 */ 952 _performSearch: function Search__performSearch(args) 953 { 954 var searchTerm = YAHOO.lang.trim(args.searchTerm), 955 searchTag = YAHOO.lang.trim(args.searchTag), 956 searchAllSites = args.searchAllSites, 957 searchRepository = args.searchRepository, 958 searchSort = args.searchSort; 959 960 if (this.options.searchQuery.length === 0 && 961 searchTag.length === 0 && 962 searchTerm.replace(/\*/g, "").length < this.options.minSearchTermLength) 963 { 964 Alfresco.util.PopupManager.displayMessage( 965 { 966 text: this.msg("message.minimum-length", this.options.minSearchTermLength) 967 }); 968 return; 969 } 970 971 // empty results table 972 this.widgets.dataTable.deleteRows(0, this.widgets.dataTable.getRecordSet().getLength()); 973 974 // update the ui to show that a search is on-going 975 this.widgets.dataTable.set("MSG_EMPTY", ""); 976 this.widgets.dataTable.render(); 977 978 // Success handler 979 function successHandler(sRequest, oResponse, oPayload) 980 { 981 // update current state on success 982 this.searchTerm = searchTerm; 983 this.searchTag = searchTag; 984 this.searchAllSites = searchAllSites; 985 this.searchRepository = searchRepository; 986 this.searchSort = searchSort; 987 988 this.widgets.dataTable.onDataReturnInitializeTable.call(this.widgets.dataTable, sRequest, oResponse, oPayload); 989 990 // update the results info text 991 this._updateResultsInfo(); 992 993 // set focus to search input textbox 994 Dom.get(this.id + "-search-text").focus(); 995 } 996 997 // Failure handler 998 function failureHandler(sRequest, oResponse) 999 { 1000 if (oResponse.status == 401) 1001 { 1002 // Our session has likely timed-out, so refresh to offer the login page 1003 window.location.reload(); 1004 } 1005 else 1006 { 1007 try 1008 { 1009 var response = YAHOO.lang.JSON.parse(oResponse.responseText); 1010 this.widgets.dataTable.set("MSG_ERROR", response.message); 1011 this.widgets.dataTable.showTableMessage(response.message, YAHOO.widget.DataTable.CLASS_ERROR); 1012 } 1013 catch(e) 1014 { 1015 this._setDefaultDataTableErrors(this.widgets.dataTable); 1016 this.widgets.dataTable.render(); 1017 } 1018 } 1019 } 1020 1021 this.widgets.dataSource.sendRequest(this._buildSearchParams(searchRepository, searchAllSites, searchTerm, searchTag, searchSort), 1022 { 1023 success: successHandler, 1024 failure: failureHandler, 1025 scope: this 1026 }); 1027 }, 1028 1029 /** 1030 * Updates the results info text. 1031 * 1032 * @method _updateResultsInfo 1033 */ 1034 _updateResultsInfo: function Search__updateResultsInfo() 1035 { 1036 // update the search results field 1037 var text; 1038 var resultsCount = '<b>' + this.resultsCount + '</b>'; 1039 if (this.hasMoreResults) 1040 { 1041 text = this.msg("search.info.resultinfomore", resultsCount); 1042 } 1043 else 1044 { 1045 text = this.msg("search.info.resultinfo", resultsCount); 1046 } 1047 1048 // apply the context 1049 if (this.searchRepository || this.options.searchQuery.length !== 0) 1050 { 1051 text += ' ' + this.msg("search.info.foundinrepository"); 1052 } 1053 else if (this.searchAllSites) 1054 { 1055 text += ' ' + this.msg("search.info.foundinallsite"); 1056 } 1057 else 1058 { 1059 text += ' ' + this.msg("search.info.foundinsite", $html(this.options.siteTitle)); 1060 } 1061 1062 // set the text 1063 Dom.get(this.id + '-search-info').innerHTML = text; 1064 }, 1065 1066 /** 1067 * Build URI parameter string for search JSON data webscript 1068 * 1069 * @method _buildSearchParams 1070 */ 1071 _buildSearchParams: function Search__buildSearchParams(searchRepository, searchAllSites, searchTerm, searchTag, searchSort) 1072 { 1073 var site = searchAllSites ? "" : this.options.siteId; 1074 var params = YAHOO.lang.substitute("site={site}&term={term}&tag={tag}&maxResults={maxResults}&sort={sort}&query={query}&repo={repo}", 1075 { 1076 site: encodeURIComponent(site), 1077 repo: searchRepository.toString(), 1078 term: encodeURIComponent(searchTerm), 1079 tag: encodeURIComponent(searchTag), 1080 sort: encodeURIComponent(searchSort), 1081 query: encodeURIComponent(this.options.searchQuery), 1082 maxResults: this.options.maxSearchResults + 1 // to calculate whether more results were available 1083 }); 1084 1085 return params; 1086 }, 1087 1088 /** 1089 * Resets the YUI DataTable errors to our custom messages 1090 * NOTE: Scope could be YAHOO.widget.DataTable, so can't use "this" 1091 * 1092 * @method _setDefaultDataTableErrors 1093 * @param dataTable {object} Instance of the DataTable 1094 */ 1095 _setDefaultDataTableErrors: function Search__setDefaultDataTableErrors(dataTable) 1096 { 1097 var msg = Alfresco.util.message; 1098 dataTable.set("MSG_EMPTY", msg("message.empty", "Alfresco.Search")); 1099 dataTable.set("MSG_ERROR", msg("message.error", "Alfresco.Search")); 1100 } 1101 }); 1102 })();