diff --git a/app/assets/javascripts/app/controllers/_ui_element/tag.coffee b/app/assets/javascripts/app/controllers/_ui_element/tag.coffee index 47a375043..df4426a69 100644 --- a/app/assets/javascripts/app/controllers/_ui_element/tag.coffee +++ b/app/assets/javascripts/app/controllers/_ui_element/tag.coffee @@ -1,6 +1,8 @@ # coffeelint: disable=camel_case_classes class App.UiElement.tag @render: (attribute) -> + if !attribute.id + attribute.id = 'tag-' + new Date().getTime() + '-' + Math.floor(Math.random() * 999999) item = $( App.view('generic/input')(attribute: attribute) ) source = "#{App.Config.get('api_path')}/tag_search" possibleTags = {} diff --git a/app/assets/javascripts/app/controllers/_ui_element/ticket_selector.coffee b/app/assets/javascripts/app/controllers/_ui_element/ticket_selector.coffee index 925bd79ae..ba9bcad44 100644 --- a/app/assets/javascripts/app/controllers/_ui_element/ticket_selector.coffee +++ b/app/assets/javascripts/app/controllers/_ui_element/ticket_selector.coffee @@ -26,6 +26,7 @@ class App.UiElement.ticket_selector '^select$': ['is', 'is not'] '^input$': ['contains', 'contains not'] '^textarea$': ['contains', 'contains not'] + '^tag$': ['contains all', 'contains one', 'contains all not', 'contains one not'] if attribute.hasChanged operators_type = diff --git a/app/assets/stylesheets/zammad.scss b/app/assets/stylesheets/zammad.scss index 357691da4..22671a350 100644 --- a/app/assets/stylesheets/zammad.scss +++ b/app/assets/stylesheets/zammad.scss @@ -5711,6 +5711,11 @@ footer { opacity: .5; } +/* allow/show autocomplete in modal dialog */ +.ui-autocomplete.ui-widget-content { + z-index: 1100; +} + .drox { color: hsl(60,1%,74%); } diff --git a/app/models/ticket.rb b/app/models/ticket.rb index 66c4c8b00..7fb4f8754 100644 --- a/app/models/ticket.rb +++ b/app/models/ticket.rb @@ -399,6 +399,7 @@ get count of tickets and tickets which match on selector access_condition = Ticket.access_condition(current_user) ticket_count = Ticket.where(access_condition).where(query, *bind_params).joins(tables).count tickets = Ticket.where(access_condition).where(query, *bind_params).joins(tables).limit(limit) + [ticket_count, tickets] end @@ -440,7 +441,11 @@ condition example 'ticket.escalation_at' => { operator: 'is not', # not value: nil, - } + }, + 'ticket.tags' => { + operator: 'contains all', # contains all|contains one|contains all not|contains one not + value: 'tag1, tag2', + }, } =end @@ -506,6 +511,9 @@ condition example # get attributes attributes = attribute.split(/\./) attribute = "#{attributes[0]}s.#{attributes[1]}" + if attributes[0] == 'ticket' && attributes[1] == 'tags' + selector['value'] = selector['value'].split(/,/).collect(&:strip) + end if query != '' query += ' AND ' @@ -571,6 +579,70 @@ condition example query += "#{attribute} NOT #{like} (?)" value = "%#{selector['value']}%" bind_params.push value + elsif selector['operator'] == 'contains all' && attributes[0] == 'ticket' && attributes[1] == 'tags' + query += "#{selector['value'].count} = ( + SELECT + COUNT(*) + FROM + tag_objects, + tag_items, + tags + WHERE + tickets.id = tags.o_id AND + tag_objects.id = tags.tag_object_id AND + tag_objects.name = 'Ticket' AND + tag_items.id = tags.tag_item_id AND + tag_items.name IN (?) + )" + bind_params.push selector['value'] + elsif selector['operator'] == 'contains one' && attributes[0] == 'ticket' && attributes[1] == 'tags' + query += "1 <= ( + SELECT + COUNT(*) + FROM + tag_objects, + tag_items, + tags + WHERE + tickets.id = tags.o_id AND + tag_objects.id = tags.tag_object_id AND + tag_objects.name = 'Ticket' AND + tag_items.id = tags.tag_item_id AND + tag_items.name IN (?) + )" + bind_params.push selector['value'] + elsif selector['operator'] == 'contains all not' && attributes[0] == 'ticket' && attributes[1] == 'tags' + query += "0 = ( + SELECT + COUNT(*) + FROM + tag_objects, + tag_items, + tags + WHERE + tickets.id = tags.o_id AND + tag_objects.id = tags.tag_object_id AND + tag_objects.name = 'Ticket' AND + tag_items.id = tags.tag_item_id AND + tag_items.name IN (?) + )" + bind_params.push selector['value'] + elsif selector['operator'] == 'contains one not' && attributes[0] == 'ticket' && attributes[1] == 'tags' + query += "( + SELECT + COUNT(*) + FROM + tag_objects, + tag_items, + tags + WHERE + tickets.id = tags.o_id AND + tag_objects.id = tags.tag_object_id AND + tag_objects.name = 'Ticket' AND + tag_items.id = tags.tag_item_id AND + tag_items.name IN (?) + ) BETWEEN ( #{selector['value'].count} - 1 ) AND #{selector['value'].count}" + bind_params.push selector['value'] elsif selector['operator'] == 'before (absolute)' query += "#{attribute} <= ?" bind_params.push selector['value'] @@ -649,6 +721,7 @@ condition example raise "Invalid operator '#{selector['operator']}' for '#{selector['value'].inspect}'" end } + [query, bind_params, tables] end diff --git a/test/unit/ticket_selector_test.rb b/test/unit/ticket_selector_test.rb index 71fc4b517..bece536c7 100644 --- a/test/unit/ticket_selector_test.rb +++ b/test/unit/ticket_selector_test.rb @@ -2,12 +2,13 @@ require 'test_helper' class TicketSelectorTest < ActiveSupport::TestCase - agent1 = nil - agent2 = nil - group = nil + agent1 = nil + agent2 = nil + group = nil organization1 = nil - customer1 = nil - customer2 = nil + customer1 = nil + customer2 = nil + test 'aaa - setup' do # create base @@ -998,4 +999,122 @@ class TicketSelectorTest < ActiveSupport::TestCase travel_back end + test 'ticket tags filter' do + ticket_tags_1 = Ticket.create!( + title: 'some title1', + group: group, + customer_id: customer1.id, + owner_id: agent1.id, + state: Ticket::State.lookup(name: 'new'), + priority: Ticket::Priority.lookup(name: '2 normal'), + created_at: '2015-02-05 16:37:00', + updated_by_id: 1, + created_by_id: 1, + ) + ticket_tags_2 = Ticket.create!( + title: 'some title1', + group: group, + customer_id: customer1.id, + owner_id: agent1.id, + state: Ticket::State.lookup(name: 'new'), + priority: Ticket::Priority.lookup(name: '2 normal'), + created_at: '2015-02-05 16:37:00', + updated_by_id: 1, + created_by_id: 1, + ) + ticket_tags_3 = Ticket.create!( + title: 'some title1', + group: group, + customer_id: customer1.id, + owner_id: agent1.id, + state: Ticket::State.lookup(name: 'new'), + priority: Ticket::Priority.lookup(name: '2 normal'), + created_at: '2015-02-05 16:37:00', + updated_by_id: 1, + created_by_id: 1, + ) + + Tag.tag_add( + object: 'Ticket', + o_id: ticket_tags_1.id, + item: 'contains_all_1', + created_by_id: 1, + ) + Tag.tag_add( + object: 'Ticket', + o_id: ticket_tags_1.id, + item: 'contains_all_2', + created_by_id: 1, + ) + Tag.tag_add( + object: 'Ticket', + o_id: ticket_tags_1.id, + item: 'contains_all_3', + created_by_id: 1, + ) + Tag.tag_add( + object: 'Ticket', + o_id: ticket_tags_2.id, + item: 'contains_all_3', + created_by_id: 1, + ) + + # search all with contains all + condition = { + 'ticket.tags' => { + operator: 'contains all', + value: 'contains_all_1, contains_all_2, contains_all_3', + }, + } + ticket_count, tickets = Ticket.selectors(condition, 10, agent1) + assert_equal(1, ticket_count) + + condition = { + 'ticket.tags' => { + operator: 'contains all', + value: 'contains_all_1, contains_all_2, contains_all_3, xxx', + }, + } + ticket_count, tickets = Ticket.selectors(condition, 10, agent1) + assert_equal(0, ticket_count) + + # search all with contains one + condition = { + 'ticket.tags' => { + operator: 'contains one', + value: 'contains_all_1, contains_all_2, contains_all_3', + }, + } + ticket_count, tickets = Ticket.selectors(condition, 10, agent1) + assert_equal(2, ticket_count) + + condition = { + 'ticket.tags' => { + operator: 'contains one', + value: 'contains_all_1, contains_all_2' + }, + } + ticket_count, tickets = Ticket.selectors(condition, 10, agent1) + assert_equal(1, ticket_count) + + # search all with contains one not + condition = { + 'ticket.tags' => { + operator: 'contains one', + value: 'contains_all_1, contains_all_3' + }, + } + ticket_count, tickets = Ticket.selectors(condition, 10, agent1) + assert_equal(2, ticket_count) + + condition = { + 'ticket.tags' => { + operator: 'contains one', + value: 'contains_all_1, contains_all_2, contains_all_3' + }, + } + ticket_count, tickets = Ticket.selectors(condition, 10, agent1) + assert_equal(2, ticket_count) + end + end