├── .gitignore ├── assets ├── logo.png ├── banner.png ├── logo-small.png ├── screenshot-1.png ├── screenshot-2.png ├── screenshot-3.png ├── logo-promotion.png ├── icon_nav_bar_active.png ├── icon_nav_bar_hover.png └── icon_nav_bar_inactive.png ├── manifest.json ├── templates ├── layout.hdbs ├── ticket_filter_string.hdbs ├── main.hdbs ├── _tickets_export.hdbs ├── topic_options.hdbs ├── results.hdbs └── ticket_options.hdbs ├── README.md ├── app.css ├── translations └── en.json ├── LICENSE └── app.js /.gitignore: -------------------------------------------------------------------------------- 1 | /tmp 2 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendesk/advanced_search/master/assets/logo.png -------------------------------------------------------------------------------- /assets/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendesk/advanced_search/master/assets/banner.png -------------------------------------------------------------------------------- /assets/logo-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendesk/advanced_search/master/assets/logo-small.png -------------------------------------------------------------------------------- /assets/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendesk/advanced_search/master/assets/screenshot-1.png -------------------------------------------------------------------------------- /assets/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendesk/advanced_search/master/assets/screenshot-2.png -------------------------------------------------------------------------------- /assets/screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendesk/advanced_search/master/assets/screenshot-3.png -------------------------------------------------------------------------------- /assets/logo-promotion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendesk/advanced_search/master/assets/logo-promotion.png -------------------------------------------------------------------------------- /assets/icon_nav_bar_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendesk/advanced_search/master/assets/icon_nav_bar_active.png -------------------------------------------------------------------------------- /assets/icon_nav_bar_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendesk/advanced_search/master/assets/icon_nav_bar_hover.png -------------------------------------------------------------------------------- /assets/icon_nav_bar_inactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendesk/advanced_search/master/assets/icon_nav_bar_inactive.png -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Advanced Search", 3 | "author": { 4 | "name": "Joe McCarron", 5 | "email": "success_apps@zendesk.com", 6 | "url": "https://github.com/jstjoe" 7 | }, 8 | "defaultLocale": "en", 9 | "private": false, 10 | "location": "nav_bar", 11 | "version": "0.10.2", 12 | "frameworkVersion": "1.0" 13 | } 14 | -------------------------------------------------------------------------------- /templates/layout.hdbs: -------------------------------------------------------------------------------- 1 |
2 |
7 |
8 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ⚠️ Use of this software is subject to important terms and conditions as set forth in the License file ⚠️ 2 | 3 | # Advanced Search for Zendesk 4 | 5 | ## Description: 6 | 7 | Beta app designed to provide a GUI for search filter keywords, as well as export of search results to CSV (via data URIs) 8 | 9 | ## Screenshot(s): 10 | ![screenshot-1](https://d1eipm3vz40hy0.cloudfront.net/images/p-apps-marketplace/apps/45270/screenshot-1.png) 11 | -------------------------------------------------------------------------------- /app.css: -------------------------------------------------------------------------------- 1 | input { 2 | border: 1px solid #d3d3d3; 3 | } 4 | /*.ticket_options label { 5 | display: block; 6 | }*/ 7 | .column input { 8 | width: 400px; 9 | margin-bottom: 10px; 10 | } 11 | .column_1 { 12 | float: left; 13 | margin-right: 20px; 14 | } 15 | 16 | .results-well { 17 | overflow: scroll; 18 | } 19 | .no_wrap { 20 | white-space: nowrap; 21 | } 22 | 23 | input[type="checkbox"] { 24 | vertical-align: middle; 25 | margin-bottom: 4px; 26 | } 27 | 28 | a.pull-right { 29 | margin-top: 10px; 30 | } 31 | 32 | a.pull-right.btn { 33 | margin-top: 0px; 34 | } 35 | -------------------------------------------------------------------------------- /templates/ticket_filter_string.hdbs: -------------------------------------------------------------------------------- 1 | {{#filters}}{{#if status}} status{{{status}}}{{/if}}{{#if ticket_type}} ticket_type:{{ticket_type}}{{/if}}{{#if priority}} priority{{{priority}}}{{/if}}{{#if date}} {{{date}}}{{/if}}{{#if group}} group:"{{group}}"{{/if}}{{#if assignee}} assignee:"{{assignee}}"{{/if}}{{#if submitter}} submitter:"{{submitter}}"{{/if}}{{#if organization}} organization:"{{organization}}"{{/if}}{{#if requester}} requester:"{{requester}}"{{/if}}{{#if commenter}} commenter:"{{commenter}}"{{/if}}{{#if cc}} cc:"{{cc}}"{{/if}}{{#if subject}} subject:"{{subject}}"{{/if}}{{#if description}} description:"{{description}}"{{/if}}{{#if tags}} tags:"{{tags}}"{{/if}}{{#if via}} via:{{via}}{{/if}}{{/filters}} 2 | -------------------------------------------------------------------------------- /translations/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "package": "app_name", 4 | "description": { 5 | "value": "Play the famous zen tunes in your help desk.", 6 | "title": "app description" 7 | }, 8 | "name": { 9 | "value": "Buddha Machine", 10 | "title": "app name" 11 | } 12 | }, 13 | 14 | "loading": { 15 | "value": "Welcome to this Sample App", 16 | "title": "loading placeholder" 17 | }, 18 | 19 | "fetch": { 20 | "done": { 21 | "value": "Good", 22 | "title": "fetch success" 23 | }, 24 | "fail": { 25 | "value": "failed to fecth information from the server", 26 | "title": "fetch failure" 27 | } 28 | }, 29 | 30 | "id": { 31 | "value": "ID", 32 | "title": "user id" 33 | }, 34 | 35 | "email": { 36 | "value": "Email", 37 | "title": "user email" 38 | }, 39 | 40 | "name": { 41 | "value": "Name", 42 | "title": "user name" 43 | }, 44 | 45 | "role": { 46 | "value": "Role", 47 | "title": "user role" 48 | }, 49 | 50 | "groups": { 51 | "value": "Groups", 52 | "title": "user groups" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /templates/main.hdbs: -------------------------------------------------------------------------------- 1 |
2 |
37 | 38 |
39 | {{!-- inject options here --}} 40 |
41 |
42 | {{!-- inject results here --}} 43 | 44 |
45 | {{spinner "dotted"}} 46 | This query has no results. Try broadening your search. 47 |
48 | -------------------------------------------------------------------------------- /templates/_tickets_export.hdbs: -------------------------------------------------------------------------------- 1 | id{{#if columns.subject}},Subject{{/if}}{{#if columns.requester}},"Requester Name"{{/if}}{{#if columns.requester_email}},"Requester Email"{{/if}}{{#if columns.group}},Group{{/if}}{{#if columns.assignee}},"Assignee Name"{{/if}}{{#if columns.assignee_email}},"Assignee Email"{{/if}}{{#if columns.status}},Status{{/if}}{{#if columns.type}},Type{{/if}}{{#if columns.priority}},Priority{{/if}}{{#if columns.created_at}},Created{{/if}}{{#if columns.updated_at}},Updated{{/if}}{{#if columns.external_id}},External_ID{{/if}}{{#if columns.channel}},Channel{{/if}}{{#if columns.description}},Description{{/if}}{{#if columns.recipient}},Recipient{{/if}}{{#if columns.submitter}},Submitter{{/if}}{{#if columns.organization}},Organization{{/if}}{{#if columns.collaborators}},CCs{{/if}}{{#if columns.forum_topic}},Topic{{/if}}{{#if columns.problem_id}},Problem{{/if}}{{#if columns.has_incidents}},Incidents{{/if}}{{#if columns.tags}},Tags{{/if}}{{#each columns.customFields}},{{title}}{{/each}} 2 | {{#tickets}}{{id}}{{#if ../columns.subject}},"{{{subject}}}"{{/if}}{{#if ../columns.requester}},"{{requester}}"{{/if}}{{#if ../columns.requester_email}},"{{requester_email}}"{{/if}}{{#if ../columns.group}},{{group_id}}{{/if}}{{#if ../columns.assignee}},{{assignee}}{{/if}}{{#if ../columns.assignee_email}},{{assignee_email}}{{/if}}{{#if ../columns.status}},{{status}}{{/if}}{{#if ../columns.type}},{{type}}{{/if}}{{#if ../columns.priority}},{{priority}}{{/if}}{{#if ../columns.created_at}},"{{created_at}}"{{/if}}{{#if ../columns.updated_at}},"{{updated_at}}"{{/if}}{{#if ../columns.external_id}},"{{external_id}}"{{/if}}{{#if ../columns.channel}},"{{channel}}"{{/if}}{{#if ../columns.description}},"{{{description}}}"{{/if}}{{#if ../columns.recipient}},"{{recipient}}"{{/if}}{{#if ../columns.submitter}},"{{submitter}}"{{/if}}{{#if ../columns.submitter_email}},"{{submitter_email}}"{{/if}}{{#if ../columns.organization}},{{organization}}{{/if}}{{#if ../columns.collaborators}},"{{collaborators}}"{{/if}}{{#if ../columns.forum_topic}},{{forum_topic}}{{/if}}{{#if ../columns.problem_id}},{{problem_id}}{{/if}}{{#if ../columns.has_incidents}},{{has_incidents}}{{/if}}{{#if ../columns.tags}},"{{tags}}"{{/if}}{{#each custom_fields}},"{{#if textarea}}{{{value}}}{{else}}{{value}}{{/if}}"{{/each}} 3 | {{/tickets}} 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2013 Zendesk 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | Capitalized terms used herein have the meaning set forth in the Zendesk, Inc. 16 | (“Zendesk”) Terms of Service (available at www.zendesk.com/company/terms) (the “Terms”). 17 | The software made available herein constitutes “ZendeskLabs Software” which may be 18 | implemented to enable features or functionality to be utilized in connection with a 19 | subscription to the Service. 20 | Notwithstanding anything to the contrary set forth in the Terms or any other 21 | agreement by and between you and Zendesk, ZendeskLabs Software is provided 22 | “AS IS” and on an “AS AVAILABLE” basis, and that Zendesk makes no warranty 23 | as to the ZendeskLabs Software. 24 | 25 | ZENDESK DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 26 | TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, 27 | NONINFRINGEMENT AND THOSE ARISING FROM A COURSE OF DEALING OR USAGE OF TRADE 28 | RELATED TO THE ZENDESKLABS SOFTWARE, ITS USE OR ANY INABILITY TO USE IT OR 29 | THE RESULTS OF ITS USE. 30 | 31 | Use of ZendeskLabs Software is subject to the following risks and conditions: 32 | (i) the ZendeskLabs Software is not an component of the Service that 33 | has been designed for commercial release for by Zendesk; 34 | 35 | (ii) the ZendeskLabs Software may not be in final form and may contain errors, 36 | design flaws or other problems; (iii) the ZendeskLabs Software is not expected to 37 | function fully or adequately upon installation, and it is expected and anticipated that 38 | further testing, modification and development may be necessary to make the 39 | ZendeskLabs Software functional; 40 | 41 | (iv) it may not be possible to make the ZendeskLabs Software functional; 42 | 43 | (v) use of the ZendeskLabs Software may result in unexpected results, loss of data, 44 | project delays or other unpredictable damage or loss; and 45 | 46 | (vi) Zendesk is under no obligation to release and/or offer for sale commercial 47 | versions of the ZendeskLabs Software, and Zendesk has the right to unilaterally 48 | abandon development or availability of the ZendeskLabs Software at any time and 49 | without any obligation or liability to You. You further agree that Zendesk shall 50 | have no obligation to correct any bugs, defects or errors in the ZendeskLabs Software 51 | or otherwise to support or maintain the ZendeskLabs Software. 52 | 53 | If you elect to utilize any ZendeskLabs Software, you are agreeing to release Zendesk 54 | from any claim with regard to the ZendeskLabs Software, its operation, availability or 55 | its failure to operate or be available. Without limiting the generality of the foregoing, 56 | 57 | You acknowledge and agree that neither the use, availability nor operation of any 58 | ZendeskLabs Software shall be subject to any service level commitment applicable to the Service. 59 | -------------------------------------------------------------------------------- /templates/topic_options.hdbs: -------------------------------------------------------------------------------- 1 | 2 | 8 | 17 |    18 | 19 | 26 |    27 | 28 | 34 | 41 |    42 | 43 | 50 | {{!-- this should be conditional on a date filter being selected above --}} 51 | 57 | 58 |

59 | 60 | 61 | 62 |    63 | 64 | 65 | 66 |    67 | 68 | 69 |

70 | 71 | 72 | 73 |    74 | 75 | 76 |

77 | 78 | 79 | 80 |    81 | 82 | 83 |

84 | 85 | 86 | 87 |    88 | 89 | 90 |

91 | 92 | 93 | 94 |    95 | 96 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /templates/results.hdbs: -------------------------------------------------------------------------------- 1 | {{#if exportEnabled}}

Results ({{count}}) Download CSV

{{/if}} 2 | 3 |
4 |
5 | 6 | 7 | {{!-- if result_type == ticket --}} 8 | {{/if}} 10 | {{#if columns.subject}}{{/if}} 11 | {{#if columns.requester}}{{/if}} 12 | {{#if columns.requester_email}}{{/if}} 13 | {{#if columns.group}}{{/if}} 14 | {{#if columns.assignee}}{{/if}} 15 | {{#if columns.assignee_email}}{{/if}} 16 | {{#if columns.status}}{{/if}} 17 | {{#if columns.type}}{{/if}} 18 | {{#if columns.priority}}{{/if}} 19 | {{#if columns.created_at}}{{/if}} 20 | {{#if columns.updated_at}}{{/if}} 21 | 22 | {{#if columns.external_id}}{{/if}} 23 | {{#if columns.channel}}{{/if}} 24 | {{#if columns.description}}{{/if}} 25 | {{#if columns.recipient}}{{/if}} 26 | {{#if columns.submitter}}{{/if}} 27 | {{#if columns.submitter_email}}{{/if}} 28 | {{#if columns.organization}}{{/if}} 29 | {{#if columns.collaborators}}{{/if}} 30 | {{#if columns.forum_topic}}{{/if}} 31 | {{#if columns.problem_id}}{{/if}} 32 | {{#if columns.has_incidents}}{{/if}} 33 | {{#if columns.tags}}{{/if}} 34 | 35 | {{#each columns.customFields}} 36 | {{!-- prints the custom field's ID as a column --}} 37 | 38 | 39 | {{/each}} 40 | 41 | 42 | {{!-- if topic --}} 43 | 44 | {{!-- if user --}} 45 | 46 | {{!-- if organization --}} 47 | 48 | 49 | 50 | {{#results}} 51 | {{!-- if ticket --}} 52 | 53 | 54 | {{#if ../columns.id}} 55 | 56 | {{/if}} 57 | {{#if ../columns.subject}} 58 | 59 | {{/if}} 60 | {{#if ../columns.requester}} 61 | 62 | {{/if}} 63 | {{#if ../columns.requester_email}} 64 | 65 | {{/if}} 66 | {{#if ../columns.group}} 67 | 68 | {{/if}} 69 | {{#if ../columns.assignee}} 70 | 71 | {{/if}} 72 | {{#if ../columns.assignee_email}} 73 | 74 | {{/if}} 75 | {{#if ../columns.status}} 76 | 77 | {{/if}} 78 | {{#if ../columns.type}} 79 | 80 | {{/if}} 81 | {{#if ../columns.priority}} 82 | 83 | {{/if}} 84 | {{#if ../columns.created_at}} 85 | 86 | {{/if}} 87 | {{#if ../columns.updated_at}} 88 | 89 | {{/if}} 90 | 91 | {{#if ../columns.external_id}} 92 | 93 | {{/if}} 94 | {{#if ../columns.channel}} 95 | 96 | {{/if}} 97 | {{#if ../columns.description}} 98 | 99 | {{/if}} 100 | {{#if ../columns.recipient}} 101 | 102 | {{/if}} 103 | {{#if ../columns.submitter}} 104 | 105 | {{/if}} 106 | {{#if ../columns.submitter_email}} 107 | 108 | {{/if}} 109 | {{#if ../columns.organization}} 110 | 111 | {{/if}} 112 | {{#if ../columns.collaborators}} 113 | 114 | {{/if}} 115 | {{#if ../columns.forum_topic}} 116 | 117 | {{/if}} 118 | {{#if ../columns.problem_id}} 119 | 120 | {{/if}} 121 | {{#if ../columns.has_incidents}} 122 | 123 | {{/if}} 124 | {{#if ../columns.tags}} 125 | 126 | {{/if}} 127 | 128 | {{#if ../columns.customFields}} 129 | {{#each custom_fields}} 130 | {{!-- prints the custom fields as a column --}} 131 | 132 | {{/each}} 133 | {{/if}} 134 | 135 | 136 | {{!-- if topic --}} 137 | 138 | {{!-- if user --}} 139 | 140 | {{!-- if organization --}} 141 | {{/results}} 142 | 143 |
9 | {{#if columns.id}}IDSubjectRequester NameRequester EmailGroupAssignee NameAssignee EmailStatusTypePriorityCreatedUpdatedExternal IDChannelDescriptionRecipient AddressSubmitter NameSubmitter EmailOrganizationCCsLinked TopicLinked ProblemHas Incidents?Tags{{title}}
{{id}}{{subject}}{{requester}}{{requester_email}}{{group_id}}{{assignee}}{{assignee_email}}{{{status_label}}}{{#if type}}{{type}}{{/if}}{{priority}}{{created_at}}{{updated_at}}{{external_id}}{{via.channel}}{{description}}{{recipient}}{{submitter}}{{submitter_email}}{{organization_id}}{{#each collaborators}}{{this.name}}{{#unless @last}}, {{/unless}}{{/each}}{{forum_topic}}{{problem_id}}{{has_incidents}}{{tags}}{{value}}
144 |
145 | {{!-- pagination --}} 146 | 147 | 159 | -------------------------------------------------------------------------------- /templates/ticket_options.hdbs: -------------------------------------------------------------------------------- 1 |
2 | 3 | 9 | 18 |    19 | 20 | 27 |    28 | 29 | 35 | 42 |    43 | 44 | 51 | {{!-- this should be conditional on a date filter being selected above --}} 52 | 58 | 59 |

60 | {{!-- line 2 --}} 61 |
62 |
63 | 64 |
65 |
66 | 67 |
68 |
69 | 70 |
71 |
72 | 73 |
74 |
75 | 76 |
77 |
78 | 79 | {{!-- column 2 --}} 80 |
81 |
82 | 83 |
84 |
85 | 86 |
87 |
88 | 89 |
90 |
91 | 92 |
93 |
94 | 109 |
110 | 111 | 112 |
113 | 114 |
115 |

Columns

116 |
117 |
System Attributes
118 | ID | 119 | Type | 120 | Subject | 121 | Group | 122 | Assignee Name| 123 | Assignee Email| 124 | Requester Name| 125 | Requester Email| 126 | Status | 127 | Priority | 128 | Created at | 129 | Updated at 130 |
131 | External ID | 132 | Channel | 133 | Description | 134 | Recipient address | 135 | Submitter Name| 136 | Submitter Email| 137 | Organization | 138 | Collaborator(s) | 139 | Forum Topic | 140 | Problem ID | 141 | Has Incidents? | 142 | Tags 143 |
144 |
145 |
Custom Fields
146 |
147 | {{!-- insert custom field checkboxes here --}} 148 | {{#each customFields}} 149 | {{title}} {{#unless @last}}|{{/unless}} 150 | {{/each}} 151 | 152 |
153 |
154 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | /*global Blob*/ 2 | /*global URL*/ 3 | /*global File*/ 4 | 5 | (function() { 6 | return { 7 | // EVENTS ================================================================================================================= 8 | events: { 9 | 'app.activated':'onAppActivated', 10 | 'pane.activated':'onPaneActivated', 11 | 'change select.type':'onTypeChanged', 12 | 'keyup input.user':'findUsers', 13 | // 'keyup input.string':'onTextEntered', 14 | // 'change select.dateType':'onDateTypeChanged', 15 | // 'change input.startDate':'onStartDateChanged', 16 | // 'change input.endDate':'onEndDateChanged', 17 | // 'change select.group':'onGroupChanged', 18 | // 'change select.assignee':'onAssigneeChanged', 19 | 'click button.addFilter':'onAddFilterClicked', 20 | 'click button.search':'onSearchClicked', 21 | 'click a.prev_page':'onPrevClicked', 22 | 'click a.next_page':'onNextClicked', 23 | 24 | // request events 25 | 'search.done':'onSearchComplete', 26 | // 'search.fail':'onSearchFail', 27 | 'getUrl.done':'onSearchComplete', 28 | 'getTicketFields.done':'setCustomFields' 29 | }, 30 | 31 | // REQUESTS ================================================================================================================= 32 | requests: { 33 | // searchIncremental: function(query, sort_by, sort_order, page) { 34 | // return { 35 | // url: helpers.fmt('/api/v2/search/incremental?query=%@&sort_by=%@&sort_order=%@&page=%@', query, sort_by, sort_order, page) 36 | // }; 37 | // }, 38 | 39 | autocompleteUsers: function(name) { 40 | return { 41 | url: '/api/v2/users/autocomplete.json?name=' + name 42 | }; 43 | }, 44 | 45 | search: function(query, sort_by, sort_order, page) { 46 | return { 47 | url: helpers.fmt('/api/v2/search.json?query=%@&sort_by=%@&sort_order=%@&page=%@', query, sort_by, sort_order, page) 48 | }; 49 | }, 50 | 51 | getUrl: function(url) { 52 | return { 53 | url: url 54 | }; 55 | }, 56 | 57 | getAssignees: function(page) { 58 | return { 59 | url: helpers.fmt('/api/v2/users.json?role[]=agent&role[]=admin&page=%@', page) 60 | }; 61 | }, 62 | 63 | getUsersBatch: function(userBatch) { 64 | var ids = userBatch.toString(); 65 | return { 66 | url: '/api/v2/users/show_many.json?ids=' + ids 67 | }; 68 | }, 69 | 70 | getTicketFields: function(url) { 71 | if(!url) {url = '/api/v2/ticket_fields.json';} 72 | return { 73 | url: url 74 | }; 75 | } 76 | }, 77 | 78 | // EVENT CALLBACKS ========================================================================================================== 79 | onAppActivated: function() { 80 | if (File && Blob && URL) { 81 | // Browser is fully supportive for export 82 | this.exportEnabled = true; 83 | } else { 84 | // Browser not supported. Disable export 85 | this.exportEnabled = false; 86 | } 87 | }, 88 | 89 | onPaneActivated: function(data) { 90 | if (data.firstLoad) { 91 | this.switchTo('main'); 92 | this.$('span.loading').hide(); 93 | this.$('span.no_results').hide(); 94 | this.userIDs = []; 95 | this.users = []; 96 | this.ticketFields = []; 97 | this.customFields = []; 98 | this.columns = {}; 99 | this.ajax('getTicketFields'); 100 | } 101 | }, 102 | 103 | onTypeChanged: function(e) { 104 | var type = e.currentTarget.value, 105 | options_html = ''; 106 | 107 | switch (type) { 108 | case "ticket": 109 | options_html = this.renderTemplate("ticket_options", { 110 | customFields: this.customFields 111 | }); 112 | break; 113 | case "topic": 114 | options_html = this.renderTemplate("topic_options"); 115 | break; 116 | case "user": 117 | options_html = this.renderTemplate("user_options"); 118 | break; 119 | case "organization": 120 | options_html = this.renderTemplate("organization_options"); 121 | break; 122 | case "": 123 | options_html = "Choose a specific type to get access to additional filter options."; 124 | break; 125 | } 126 | //inject additional options 127 | this.$("div.type_options").html(options_html); 128 | // autocomplete ticket options 129 | var userFields = ["assignee","requester","submitter","cc","commenter"]; 130 | _.each(userFields, function(title) { 131 | this.$('input.' + title).autocomplete({ 132 | minLength: 0 133 | }); 134 | }, this); 135 | }, 136 | 137 | findUsers: function(e) { 138 | var name = e.currentTarget.value; 139 | var encodedQuery = encodeURIComponent(name); 140 | this.ajax('autocompleteUsers', encodedQuery).done(function(response) { 141 | var users = _.map(response.users, function(user) { 142 | return { 143 | label: user.name + " | " + user.email, 144 | value: user.email || user.id 145 | }; 146 | }); 147 | 148 | this.$('input#' + e.currentTarget.id).autocomplete({ 149 | source: users 150 | }); 151 | }); 152 | }, 153 | 154 | onAddFilterClicked: function(e) { 155 | if (e) {e.preventDefault();} 156 | // render various filters 157 | }, 158 | 159 | 160 | onSearchClicked: function(e) { 161 | if (e) {e.preventDefault();} 162 | this.$('div.results').html(''); 163 | var string = this.$('input.string').val(), 164 | type = this.$('form.main_search select.type').val(); 165 | this.type = type; 166 | var filter_string = ''; 167 | switch (type) { 168 | case "ticket"://if searching for tickets 169 | var status_operator = ''; 170 | // TODO change to another switch 171 | if(this.$('form.ticket_filters select.status_operator').val() == 'greater') { 172 | status_operator = '>'; 173 | } else if (this.$('form.ticket_filters select.status_operator').val() == 'less') { 174 | status_operator = '<'; 175 | } else if (this.$('form.ticket_filters select.status_operator').val() == ':') { 176 | status_operator = ':'; 177 | } 178 | var priority_operator = ''; 179 | if(this.$('form.ticket_filters select.priority_operator').val() == 'greater') { 180 | priority_operator = '>'; 181 | } else if (this.$('form.ticket_filters select.priority_operator').val() == 'less') { 182 | priority_operator = '<'; 183 | } else if (this.$('form.ticket_filters select.priority_operator').val() == ':') { 184 | priority_operator = ':'; 185 | } 186 | var date_operator = ''; 187 | if(this.$('form.ticket_filters select.date_operator').val() == 'greater') { 188 | date_operator = '>'; 189 | } else if (this.$('form.ticket_filters select.date_operator').val() == 'less') { 190 | date_operator = '<'; 191 | } else if (this.$('form.ticket_filters select.date_operator').val() == ':') { 192 | date_operator = ':'; 193 | } 194 | var ticket_filters = { 195 | "status": status_operator + this.$('form.ticket_filters select.status_value').val(), 196 | "ticket_type": this.$('form.ticket_filters select.ticket_type').val(), 197 | "priority": priority_operator + this.$('form.ticket_filters select.priority_value').val(), 198 | "date": this.$('form.ticket_filters select.date_type').val() + date_operator + this.$('form.ticket_filters input.date_value').val(), 199 | "group": this.$('form.ticket_filters input.group').val(), 200 | "assignee": this.$('form.ticket_filters input.assignee').val(), 201 | "submitter": this.$('form.ticket_filters input.submitter').val(), 202 | "organization": this.$('form.ticket_filters input.organization').val(), 203 | "requester": this.$('form.ticket_filters input.requester').val(), 204 | "commenter": this.$('form.ticket_filters input.commenter').val(), 205 | "cc": this.$('form.ticket_filters input.cc').val(), 206 | "subject": this.$('form.ticket_filters input.subject').val(), 207 | "description": this.$('form.ticket_filters input.description').val(), 208 | "tags": this.$('form.ticket_filters input.tags').val(), //.split(/\W/) 209 | "via": this.$('form.ticket_filters select.via').val() 210 | }; 211 | 212 | // render a template to build the filters string 213 | filter_string = this.renderTemplate('ticket_filter_string', { 214 | filters: ticket_filters 215 | }); 216 | 217 | this.columns = { 218 | type: this.$('form.ticket_columns .type').prop('checked'), 219 | id: this.$('form.ticket_columns .id').prop('checked'), 220 | subject: this.$('form.ticket_columns .subject').prop('checked'), 221 | group: this.$('form.ticket_columns .group').prop('checked'), 222 | assignee: this.$('form.ticket_columns .assignee').prop('checked'), 223 | assignee_email: this.$('form.ticket_columns .assignee_email').prop('checked'), 224 | requester: this.$('form.ticket_columns .requester').prop('checked'), 225 | requester_email: this.$('form.ticket_columns .requester_email').prop('checked'), 226 | status: this.$('form.ticket_columns .status').prop('checked'), 227 | priority: this.$('form.ticket_columns .priority').prop('checked'), 228 | created_at: this.$('form.ticket_columns .created').prop('checked'), 229 | updated_at: this.$('form.ticket_columns .updated').prop('checked'), 230 | 231 | external_id: this.$('form.ticket_columns .external_id').prop('checked'), 232 | channel: this.$('form.ticket_columns .channel').prop('checked'), 233 | description: this.$('form.ticket_columns .description').prop('checked'), 234 | recipient: this.$('form.ticket_columns .recipient').prop('checked'), 235 | submitter: this.$('form.ticket_columns .submitter').prop('checked'), 236 | submitter_email: this.$('form.ticket_columns .submitter_email').prop('checked'), 237 | organization: this.$('form.ticket_columns .organization').prop('checked'), 238 | collaborators: this.$('form.ticket_columns .collaborators').prop('checked'), 239 | forum_topic: this.$('form.ticket_columns .forum_topic').prop('checked'), 240 | problem_id: this.$('form.ticket_columns .problem_id').prop('checked'), 241 | has_incidents: this.$('form.ticket_columns .has_incidents').prop('checked'), 242 | tags: this.$('form.ticket_columns .tags').prop('checked'), 243 | 244 | customFields: this.selectCustomFields() 245 | }; 246 | break; 247 | // TODO add cases for other objects 248 | } // end switch 249 | 250 | //no matter the type... 251 | this.results = []; 252 | var query = string + filter_string + ' type:' + type, 253 | sort_by = this.$('select.sort_by').val(), 254 | sort_order = this.$('select.sort_order').val(), 255 | page = '1'; 256 | if(query.length < 2) { 257 | services.notify("A search query must have at least two characters.", "error"); 258 | } else { 259 | var encodedQuery = encodeURIComponent(query); 260 | // store the query globally 261 | this.encodedQuery = encodedQuery; 262 | this.$("span.no_results").hide(); 263 | this.$("span.loading").show(); 264 | // make the request 265 | this.ajax('search', encodedQuery, sort_by, sort_order, page); 266 | } 267 | }, 268 | 269 | onPrevClicked: function(e) { 270 | e.preventDefault(); 271 | this.results = []; 272 | this.ajax('getUrl', this.prev_page); 273 | this.$('div.results').html(''); 274 | this.$("span.loading").show(); 275 | }, 276 | 277 | onNextClicked: function(e) { 278 | e.preventDefault(); 279 | this.results = []; 280 | this.ajax('getUrl', this.next_page); 281 | this.$('div.results').html(''); 282 | this.$("span.loading").show(); 283 | }, 284 | 285 | // onFilterSelected: function(e) { 286 | // if (e) {e.preventDefault();} 287 | // //grab the selection and render the additional filter UI 288 | // //use a global variable to track the number of these filters rendered, and give them an ID to indicate? 289 | // }, 290 | 291 | // REQUEST CALLBACKS ========================================================================================================== 292 | setCustomFields: function(ticket_fields) { 293 | this.ticketFields = this.ticketFields.concat(ticket_fields.ticket_fields); 294 | 295 | if (ticket_fields.next_page) { 296 | this.ajax('getTicketFields', ticket_fields.next_page); 297 | return; 298 | } else { 299 | this.customFields = _.filter(this.ticketFields, function(field) { 300 | return !_.contains(['subject', 'description', 'status', 'tickettype', 'priority', 'group', 'assignee'], field.type) && field.active; 301 | }); 302 | var e = {"currentTarget": {"value": "ticket"}}; 303 | this.onTypeChanged(e); 304 | } 305 | }, 306 | 307 | // findOrgs: function() { 308 | 309 | // }, 310 | 311 | // foundOrgs: function(response) { 312 | // var organizations = response.organizations; 313 | 314 | // }, 315 | 316 | onSearchComplete: function(response) { 317 | var allPages = this.$('.all_pages').prop('checked'); 318 | this.results = this.results.concat(response.results); 319 | var next_page, 320 | prev_page; 321 | if(allPages && response.next_page) { 322 | // get the next page by URL 323 | this.ajax('getUrl', response.next_page); 324 | return; 325 | } else { 326 | // TODO: add buttons # numbering 327 | if(response.next_page) { 328 | next_page = response.next_page; 329 | this.next_page = response.next_page; 330 | } 331 | if(response.previous_page) { 332 | prev_page = response.previous_page; 333 | this.prev_page = response.previous_page; 334 | } 335 | this.numberOfResults = response.count; 336 | } 337 | var results = this.results; 338 | if(results.length === 0) { 339 | this.$("span.loading").hide(); 340 | this.$('span.no_results').show(); 341 | return; 342 | } 343 | // TODO make conditional for results type - e.g. this.type == 'tickets' 344 | // massage the data... 345 | _.each(results, function(result, n) { 346 | // store user IDs 347 | var last; 348 | if(results.length == n+1) {last = true;} 349 | else {last = false;} 350 | var users = _.union(result.collaborator_ids, [result.assignee_id, result.requester_id, result.submitter_id]); 351 | this.addUsers(users, last); 352 | }.bind(this)); 353 | }, 354 | 355 | // HELPER METHODS =========================================================================================================== 356 | 357 | addUsers: function(ids, last) { 358 | _.each(ids, function(id) { 359 | this.userIDs.push(id); 360 | }.bind(this)); 361 | this.userIDs = _.filter(_.uniq(this.userIDs), Boolean); 362 | if(this.userIDs.length >= 100 || last) { 363 | var userBatch = _.first(this.userIDs, 100); 364 | this.userIDs = _.rest(this.userIDs, 99); 365 | this.ajax('getUsersBatch', userBatch).done(function(response) { 366 | this.users = this.users.concat(response.users); 367 | _.defer(function(){ 368 | this.encodeResults(this.results); 369 | }.bind(this)); 370 | }); 371 | } 372 | }, 373 | 374 | selectCustomFields: function() { 375 | var that = this, 376 | customFields = this.customFields, 377 | selected = this.$('.custom_field_options input').map(function () { 378 | if( that.$(this).prop('checked') ) { 379 | return that.$(this).attr('data-field-option-id'); 380 | } 381 | }); 382 | selected = _.toArray(selected); 383 | var columns = _.filter(customFields, function(cf) { 384 | return _.contains(selected, cf.id.toString()); 385 | }); 386 | return columns; 387 | }, 388 | 389 | encodeResults: function(results) { 390 | this.encoded = []; 391 | var custom_fields = this.columns.customFields; 392 | var cfIDs = _.map(custom_fields, function(cf) { 393 | return cf.id; 394 | }); 395 | _.each(results, function(result, n) { 396 | // filter the custom field result set down to the selected columns 397 | result.custom_fields = _.filter(result.custom_fields, function(cf) { 398 | return _.contains(cfIDs, cf.id); 399 | }); 400 | result.custom_fields = _.map(result.custom_fields, function(cf) { 401 | var field = _.find(custom_fields, function(f) { return f.id == cf.id; }); 402 | // add flag to textarea fields (used in the template) 403 | if(field.type == 'textarea') { 404 | cf.textarea = true; 405 | } 406 | return cf; 407 | }); 408 | if(result.description) { 409 | result.description = result.description.replace(/"/g, '\"\"'); 410 | } 411 | // format dates 412 | result.created_at = new Date(result.created_at).toLocaleString(); 413 | result.updated_at = new Date(result.updated_at).toLocaleString(); 414 | // look up users from unique array 415 | var assignee = _.find(this.users, function(user) { return user.id == result.assignee_id; }), 416 | requester = _.find(this.users, function(user) { return user.id == result.requester_id; }), 417 | submitter = _.find(this.users, function(user) { return user.id == result.submitter_id; }); 418 | var collaborators = _.map(result.collaborator_ids, function(id) { 419 | return _.find(this.users, function(user) { return user.id == id; }); 420 | }, this); 421 | // replace user ids w/ names 422 | if(assignee) { 423 | result.assignee = assignee.name; 424 | result.assignee_email = assignee.email; 425 | } 426 | else { 427 | result.assignee = result.assignee_id; 428 | result.assignee_email = null; 429 | } 430 | if(requester) { 431 | result.requester = requester.name; 432 | result.requester_email = requester.email; 433 | } 434 | else { 435 | result.requester = result.requester_id; 436 | result.requester_email = null; 437 | } 438 | if(submitter) { 439 | result.submitter = submitter.name; 440 | result.submitter_email = submitter.email; 441 | } 442 | else { 443 | result.submitter = result.submitter_id; 444 | result.submitter_email = null; 445 | } 446 | if(collaborators) {result.collaborators = collaborators;} 447 | //add status labels 448 | result.status_label = helpers.fmt('%@', result.status, result.status); 449 | }.bind(this)); 450 | // create export 451 | var url; 452 | if (this.exportEnabled === true) { 453 | var data = this.renderTemplate('_tickets_export', { 454 | tickets: results, 455 | columns: this.columns 456 | }); 457 | var file = new File([data], 'tickets.csv'); 458 | url = URL.createObjectURL(file); 459 | } else { 460 | url = false; 461 | } 462 | // display results 463 | var results_html = this.renderTemplate('results', { 464 | results: results, 465 | // encoded_results: this.encoded, 466 | count: this.numberOfResults, 467 | next_page: this.next_page, 468 | prev_page: this.prev_page, 469 | columns: this.columns, 470 | download: url, 471 | exportEnabled: this.exportEnabled 472 | }); 473 | this.$("span.loading").hide(); 474 | this.$('div.results').html(results_html); 475 | } 476 | }; 477 | }()); 478 | --------------------------------------------------------------------------------