2014-02-03 19:23:00 +00:00
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
2013-06-12 15:59:58 +00:00
2012-07-29 20:25:31 +00:00
class Ticket < ApplicationModel
2013-09-29 21:37:49 +00:00
include Ticket :: Escalation
include Ticket :: Subject
include Ticket :: Permission
2014-10-17 12:15:54 +00:00
load 'ticket/assets.rb'
2013-09-29 21:37:49 +00:00
include Ticket :: Assets
2014-10-17 12:15:54 +00:00
load 'ticket/history_log.rb'
2013-09-29 21:37:49 +00:00
include Ticket :: HistoryLog
2014-10-17 12:15:54 +00:00
load 'ticket/activity_stream_log.rb'
2013-09-29 21:37:49 +00:00
include Ticket :: ActivityStreamLog
2014-10-17 12:15:54 +00:00
load 'ticket/search_index.rb'
2014-01-27 22:59:41 +00:00
include Ticket :: SearchIndex
2013-09-29 21:37:49 +00:00
extend Ticket :: Search
2015-12-14 09:23:14 +00:00
store :preferences
2015-01-07 21:28:15 +00:00
before_create :check_generate , :check_defaults , :check_title
2015-02-11 22:05:31 +00:00
before_update :check_defaults , :check_title , :reset_pending_time
2012-07-20 08:08:31 +00:00
before_destroy :destroy_dependencies
2015-02-01 12:08:11 +00:00
notify_clients_support
2013-09-29 16:40:42 +00:00
2015-02-25 20:52:14 +00:00
latest_change_support
2015-04-27 13:42:53 +00:00
activity_stream_support ignore_attributes : {
2016-04-27 09:21:07 +00:00
organization_id : true , # organization_id will channge automatically on user update
2015-04-27 13:42:53 +00:00
create_article_type_id : true ,
create_article_sender_id : true ,
article_count : true ,
2015-09-22 23:49:27 +00:00
first_response : true ,
first_response_escal_date : true ,
first_response_sla_time : true ,
first_response_in_min : true ,
first_response_diff_in_min : true ,
close_time : true ,
close_time_escal_date : true ,
close_time_sla_time : true ,
close_time_in_min : true ,
close_time_diff_in_min : true ,
update_time_escal_date : true ,
2016-02-04 13:35:05 +00:00
update_time_sla_time : true ,
2015-09-22 23:49:27 +00:00
update_time_in_min : true ,
update_time_diff_in_min : true ,
last_contact : true ,
last_contact_agent : true ,
last_contact_customer : true ,
2013-10-05 12:56:03 +00:00
}
2013-09-29 21:37:49 +00:00
2015-04-27 13:42:53 +00:00
history_support ignore_attributes : {
create_article_type_id : true ,
create_article_sender_id : true ,
article_count : true ,
2013-09-29 21:37:49 +00:00
}
2013-01-01 20:29:26 +00:00
2015-10-27 07:41:55 +00:00
search_index_support
2014-01-27 22:59:41 +00:00
2016-06-06 15:26:37 +00:00
belongs_to :group , class_name : 'Group'
2015-04-27 13:42:53 +00:00
has_many :articles , class_name : 'Ticket::Article' , after_add : :cache_update , after_remove : :cache_update
2016-06-06 15:26:37 +00:00
belongs_to :organization , class_name : 'Organization'
2015-04-27 13:42:53 +00:00
belongs_to :state , class_name : 'Ticket::State'
belongs_to :priority , class_name : 'Ticket::Priority'
belongs_to :owner , class_name : 'User'
belongs_to :customer , class_name : 'User'
belongs_to :created_by , class_name : 'User'
belongs_to :updated_by , class_name : 'User'
belongs_to :create_article_type , class_name : 'Ticket::Article::Type'
belongs_to :create_article_sender , class_name : 'Ticket::Article::Sender'
2012-04-10 14:06:46 +00:00
2014-09-09 23:42:20 +00:00
self . inheritance_column = nil
2013-03-28 23:13:15 +00:00
attr_accessor :callback_loop
2013-08-17 21:10:36 +00:00
= begin
2013-08-17 21:13:34 +00:00
list of agents in group of ticket
2013-08-17 21:10:36 +00:00
ticket = Ticket . find ( 123 )
2013-08-17 21:13:34 +00:00
result = ticket . agent_of_group
2013-08-17 21:10:36 +00:00
returns
2013-08-17 21:13:34 +00:00
result = [ user1 , user2 , ... ]
2013-08-17 21:10:36 +00:00
= end
2012-04-10 14:06:46 +00:00
def agent_of_group
2016-02-07 13:00:29 +00:00
Group . find ( group_id )
. users . where ( active : true )
. joins ( :roles )
. where ( 'roles.name' = > Z_ROLENAME_AGENT , 'roles.active' = > true )
2016-02-08 07:27:02 +00:00
. order ( 'users.login' )
2016-02-07 13:00:29 +00:00
. uniq ( )
2012-04-10 14:06:46 +00:00
end
2013-08-16 14:30:51 +00:00
= begin
2014-11-10 07:34:20 +00:00
get user access conditions
2015-02-15 09:23:55 +00:00
conditions = Ticket . access_condition ( User . find ( 1 ) )
2014-11-10 07:34:20 +00:00
returns
result = [ user1 , user2 , ... ]
= end
def self . access_condition ( user )
access_condition = [ ]
2015-05-08 08:15:45 +00:00
if user . role? ( Z_ROLENAME_AGENT )
2016-05-22 21:17:46 +00:00
group_ids = Group . select ( 'groups.id' ) . joins ( :users )
. where ( 'groups_users.user_id = ?' , user . id )
. where ( 'groups.active = ?' , true )
. map ( & :id )
2014-11-10 07:34:20 +00:00
access_condition = [ 'group_id IN (?)' , group_ids ]
else
2016-01-15 17:22:57 +00:00
access_condition = if ! user . organization || ( ! user . organization . shared || user . organization . shared == false )
[ 'tickets.customer_id = ?' , user . id ]
else
2016-05-22 21:17:46 +00:00
[ '(tickets.customer_id = ? OR tickets.organization_id = ?)' , user . id , user . organization . id ]
2016-01-15 17:22:57 +00:00
end
2014-11-10 07:34:20 +00:00
end
access_condition
end
= begin
2015-05-21 14:40:04 +00:00
processes tickets which have reached their pending time and sets next state_id
2016-02-22 23:28:13 +00:00
processed_tickets = Ticket . process_pending
2015-05-21 14:40:04 +00:00
returns
processed_tickets = [ < Ticket > , ... ]
= end
def self . process_pending
2016-02-20 10:12:15 +00:00
result = [ ]
2015-05-21 14:40:04 +00:00
2016-02-20 10:12:15 +00:00
# process pending action tickets
pending_action = Ticket :: StateType . find_by ( name : 'pending action' )
ticket_states_pending_action = Ticket :: State . where ( state_type_id : pending_action )
. where . not ( next_state_id : nil )
if ! ticket_states_pending_action . empty?
next_state_map = { }
ticket_states_pending_action . each { | state |
next_state_map [ state . id ] = state . next_state_id
}
tickets = where ( state_id : next_state_map . keys )
. where ( 'pending_time <= ?' , Time . zone . now )
tickets . each { | ticket |
ticket . state_id = next_state_map [ ticket . state_id ]
ticket . updated_at = Time . zone . now
ticket . updated_by_id = 1
ticket . save!
# we do not have an destructor at this point, so we need to
2016-04-14 07:17:13 +00:00
# execute object transaction manually
Observer :: Transaction . commit
2016-02-20 10:12:15 +00:00
result . push ticket
}
end
2015-05-21 14:40:04 +00:00
2016-02-20 10:12:15 +00:00
# process pending reminder tickets
pending_reminder = Ticket :: StateType . find_by ( name : 'pending reminder' )
ticket_states_pending_reminder = Ticket :: State . where ( state_type_id : pending_reminder )
2015-05-21 14:40:04 +00:00
2016-02-20 10:12:15 +00:00
if ! ticket_states_pending_reminder . empty?
reminder_state_map = { }
ticket_states_pending_reminder . each { | state |
reminder_state_map [ state . id ] = state . next_state_id
}
2015-05-21 14:40:04 +00:00
2016-02-20 10:12:15 +00:00
tickets = where ( state_id : reminder_state_map . keys )
. where ( 'pending_time <= ?' , Time . zone . now )
2015-05-21 14:40:04 +00:00
2016-02-20 10:12:15 +00:00
tickets . each { | ticket |
2015-05-21 14:40:04 +00:00
2016-05-19 08:20:38 +00:00
article_id = nil
article = Ticket :: Article . last_customer_agent_article ( ticket . id )
if article
article_id = article . id
end
2016-02-20 10:12:15 +00:00
# send notification
2016-04-15 21:56:10 +00:00
Transaction :: BackgroundJob . run (
object : 'Ticket' ,
type : 'reminder_reached' ,
2016-04-22 07:58:28 +00:00
object_id : ticket . id ,
2016-05-19 08:20:38 +00:00
article_id : article_id ,
2016-04-27 07:31:11 +00:00
user_id : 1 ,
2016-02-20 10:12:15 +00:00
)
result . push ticket
}
end
2015-05-22 06:58:32 +00:00
2015-05-21 14:40:04 +00:00
result
end
= begin
2016-02-22 23:28:13 +00:00
processes escalated tickets
processed_tickets = Ticket . process_escalation
returns
processed_tickets = [ < Ticket > , ... ]
= end
def self . process_escalation
result = [ ]
# get max warning diff
tickets = where ( 'escalation_time <= ?' , Time . zone . now + 15 . minutes )
tickets . each { | ticket |
# get sla
sla = ticket . escalation_calculation_get_sla
2016-05-19 08:20:38 +00:00
article_id = nil
article = Ticket :: Article . last_customer_agent_article ( ticket . id )
if article
article_id = article . id
end
2016-02-22 23:28:13 +00:00
# send escalation
if ticket . escalation_time < Time . zone . now
2016-04-15 21:56:10 +00:00
Transaction :: BackgroundJob . run (
object : 'Ticket' ,
type : 'escalation' ,
2016-04-22 07:58:28 +00:00
object_id : ticket . id ,
2016-05-19 08:20:38 +00:00
article_id : article_id ,
2016-04-27 07:31:11 +00:00
user_id : 1 ,
2016-02-22 23:28:13 +00:00
)
result . push ticket
next
end
# check if warning need to be sent
2016-04-15 21:56:10 +00:00
Transaction :: BackgroundJob . run (
object : 'Ticket' ,
type : 'escalation_warning' ,
2016-04-22 07:58:28 +00:00
object_id : ticket . id ,
2016-05-19 08:20:38 +00:00
article_id : article_id ,
2016-04-27 07:31:11 +00:00
user_id : 1 ,
2016-02-22 23:28:13 +00:00
)
result . push ticket
}
result
end
= begin
2013-08-16 14:30:51 +00:00
merge tickets
2013-08-17 20:04:57 +00:00
ticket = Ticket . find ( 123 )
result = ticket . merge_to (
2015-09-03 09:14:09 +00:00
ticket_id : 123 ,
user_id : 123 ,
2013-08-16 14:30:51 +00:00
)
returns
result = true | false
= end
2012-07-03 13:24:31 +00:00
def merge_to ( data )
2012-11-07 23:47:05 +00:00
2012-07-03 13:24:31 +00:00
# update articles
2016-05-04 09:45:05 +00:00
Ticket :: Article . where ( ticket_id : id ) . each ( & :touch )
2015-03-09 01:31:09 +00:00
# quiet update of reassign of articles
2016-05-04 09:45:05 +00:00
Ticket :: Article . where ( ticket_id : id ) . update_all ( [ 'ticket_id = ?' , data [ :ticket_id ] ] )
2012-11-07 23:47:05 +00:00
2015-01-31 13:33:05 +00:00
# touch new ticket (to broadcast change)
2016-05-04 09:45:05 +00:00
Ticket . find ( data [ :ticket_id ] ) . touch
2015-01-31 13:33:05 +00:00
2012-07-03 13:24:31 +00:00
# update history
2012-11-07 23:47:05 +00:00
2012-07-03 13:24:31 +00:00
# create new merge article
Ticket :: Article . create (
2015-05-07 12:10:38 +00:00
ticket_id : id ,
2016-05-04 09:45:05 +00:00
type_id : Ticket :: Article :: Type . lookup ( name : 'note' ) . id ,
sender_id : Ticket :: Article :: Sender . lookup ( name : Z_ROLENAME_AGENT ) . id ,
2015-04-27 13:42:53 +00:00
body : 'merged' ,
internal : false ,
created_by_id : data [ :user_id ] ,
updated_by_id : data [ :user_id ] ,
2012-07-03 13:24:31 +00:00
)
# add history to both
# link tickets
2012-08-21 10:28:41 +00:00
Link . add (
2015-04-27 13:42:53 +00:00
link_type : 'parent' ,
link_object_source : 'Ticket' ,
link_object_source_value : data [ :ticket_id ] ,
link_object_target : 'Ticket' ,
2015-05-07 12:10:38 +00:00
link_object_target_value : id
2012-08-21 10:28:41 +00:00
)
2012-07-03 13:24:31 +00:00
# set state to 'merged'
2016-05-04 09:45:05 +00:00
self . state_id = Ticket :: State . lookup ( name : 'merged' ) . id
2012-07-03 13:24:31 +00:00
# rest owner
2016-05-04 09:45:05 +00:00
self . owner_id = User . find_by ( login : '-' ) . id
2012-07-03 13:24:31 +00:00
# save ticket
2015-05-07 12:10:38 +00:00
save
2015-04-01 14:33:24 +00:00
end
= begin
2015-09-03 09:14:09 +00:00
check if online notifcation should be shown in general as already seen with current state
2015-04-01 14:33:24 +00:00
ticket = Ticket . find ( 1 )
2015-09-03 09:14:09 +00:00
seen = ticket . online_notification_seen_state ( user_id_check )
returns
result = true # or false
check if online notifcation should be shown for this user as already seen with current state
ticket = Ticket . find ( 1 )
seen = ticket . online_notification_seen_state ( check_user_id )
2015-04-01 11:47:10 +00:00
2015-04-01 14:33:24 +00:00
returns
2015-07-26 21:21:16 +00:00
result = true # or false
2015-04-01 14:33:24 +00:00
= end
2015-04-01 11:47:10 +00:00
2015-09-03 09:14:09 +00:00
def online_notification_seen_state ( user_id_check = nil )
2016-02-20 12:36:59 +00:00
state = Ticket :: State . lookup ( id : state_id )
state_type = Ticket :: StateType . lookup ( id : state . state_type_id )
2015-07-26 21:21:16 +00:00
2016-02-20 13:09:56 +00:00
# always to set unseen for ticket owner
if state_type . name != 'merged'
if user_id_check
return false if user_id_check == owner_id && user_id_check != updated_by_id
end
end
2015-07-26 21:21:16 +00:00
# set all to seen if pending action state is a closed or merged state
if state_type . name == 'pending action' && state . next_state_id
2016-02-20 12:36:59 +00:00
state = Ticket :: State . lookup ( id : state . next_state_id )
state_type = Ticket :: StateType . lookup ( id : state . state_type_id )
2015-07-26 21:21:16 +00:00
end
2015-08-31 09:13:40 +00:00
# set all to seen if new state is pending reminder state
2015-09-03 09:14:09 +00:00
if state_type . name == 'pending reminder'
if user_id_check
return false if owner_id == 1
return false if updated_by_id != owner_id && user_id_check == owner_id
return true
end
return true
end
2015-08-31 09:13:40 +00:00
2015-07-26 21:21:16 +00:00
# set all to seen if new state is a closed or merged state
2015-04-01 14:33:24 +00:00
return true if state_type . name == 'closed'
return true if state_type . name == 'merged'
false
2012-07-03 13:24:31 +00:00
end
2015-09-23 14:25:06 +00:00
= begin
get count of tickets and tickets which match on selector
2015-10-12 13:44:34 +00:00
ticket_count , tickets = Ticket . selectors ( params [ :condition ] , limit , current_user )
2015-09-23 14:25:06 +00:00
= end
2015-10-12 13:44:34 +00:00
def self . selectors ( selectors , limit = 10 , current_user = nil )
2016-03-01 14:26:46 +00:00
raise 'no selectors given' if ! selectors
2015-10-14 14:33:26 +00:00
query , bind_params , tables = selector2sql ( selectors , current_user )
2015-09-17 18:39:51 +00:00
return [ ] if ! query
2015-10-12 13:44:34 +00:00
if ! current_user
ticket_count = Ticket . where ( query , * bind_params ) . joins ( tables ) . count
tickets = Ticket . where ( query , * bind_params ) . joins ( tables ) . limit ( limit )
return [ ticket_count , tickets ]
end
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 )
2015-09-17 01:04:16 +00:00
[ ticket_count , tickets ]
end
2015-09-23 14:25:06 +00:00
= begin
generate condition query to search for tickets based on condition
2015-10-14 14:33:26 +00:00
query_condition , bind_condition = selector2sql ( params [ :condition ] , current_user )
2015-09-23 14:25:06 +00:00
condition example
{
'ticket.state_id' = > {
operator : 'is' ,
value : [ 1 , 2 , 5 ]
2015-10-12 13:44:34 +00:00
} ,
'ticket.created_at' = > {
operator : 'after (absolute)' , # after,before
value : '2015-10-17T06:00:00.000Z' ,
} ,
'ticket.created_at' = > {
operator : 'within next (relative)' , # before,within,in,after
range : 'day' , # minute|hour|day|month|year
value : '25' ,
} ,
2015-10-14 14:33:26 +00:00
'ticket.owner_id' = > {
operator : 'is' , # is not
pre_condition : 'current_user.id' ,
} ,
'ticket.owner_id' = > {
operator : 'is' , # is not
pre_condition : 'specific' ,
value : 4711 ,
} ,
2016-02-26 12:19:57 +00:00
'ticket.escalation_time' = > {
operator : 'is not' , # not
value : nil ,
}
2015-09-23 14:25:06 +00:00
}
= end
2015-10-14 14:33:26 +00:00
def self . selector2sql ( selectors , current_user = nil )
current_user_id = UserInfo . current_user_id
if current_user
current_user_id = current_user . id
end
2015-09-17 01:04:16 +00:00
return if ! selectors
2015-10-14 14:33:26 +00:00
# remember query and bind params
2015-09-17 01:04:16 +00:00
query = ''
bind_params = [ ]
2016-01-19 22:30:23 +00:00
like = Rails . application . config . db_like
2015-09-17 01:04:16 +00:00
2015-10-14 14:33:26 +00:00
# get tables to join
2015-10-12 13:44:34 +00:00
tables = ''
2015-09-17 18:39:51 +00:00
selectors . each { | attribute , selector |
selector = attribute . split ( / \ . / )
next if ! selector [ 1 ]
next if selector [ 0 ] == 'ticket'
next if tables . include? ( selector [ 0 ] )
2015-10-12 13:44:34 +00:00
if query != ''
query += ' AND '
end
if selector [ 0 ] == 'customer'
tables += ', users customers'
query += 'tickets.customer_id = customers.id'
elsif selector [ 0 ] == 'organization'
tables += ', organizations'
query += 'tickets.organization_id = organizations.id'
elsif selector [ 0 ] == 'owner'
tables += ', users owners'
query += 'tickets.owner_id = owners.id'
2016-05-03 00:36:44 +00:00
elsif selector [ 0 ] == 'article'
tables += ', ticket_articles articles'
query += 'tickets.id = articles.ticket_id'
2015-10-12 13:44:34 +00:00
else
2016-03-01 14:26:46 +00:00
raise " invalid selector #{ attribute . inspect } -> #{ selector . inspect } "
2015-10-12 13:44:34 +00:00
end
2015-09-17 18:39:51 +00:00
}
2015-10-14 14:33:26 +00:00
# add conditions
2015-09-23 14:25:06 +00:00
selectors . each { | attribute , selector_raw |
2015-10-14 14:33:26 +00:00
# validation
2016-03-01 14:26:46 +00:00
raise " Invalid selector #{ selector_raw . inspect } " if ! selector_raw
raise " Invalid selector #{ selector_raw . inspect } " if ! selector_raw . respond_to? ( :key? )
2015-09-23 14:25:06 +00:00
selector = selector_raw . stringify_keys
2016-03-01 14:26:46 +00:00
raise " Invalid selector, operator missing #{ selector . inspect } " if ! selector [ 'operator' ]
2015-10-14 14:33:26 +00:00
2016-02-26 12:19:57 +00:00
# validate value / allow empty but only if pre_condition exists
2016-03-18 02:04:49 +00:00
if ! selector . key? ( 'value' ) || ( ( selector [ 'value' ] . class == String || selector [ 'value' ] . class == Array ) && ( selector [ 'value' ] . respond_to? ( :empty? ) && selector [ 'value' ] . empty? ) )
2015-10-14 14:33:26 +00:00
return nil if selector [ 'pre_condition' ] . nil? || ( selector [ 'pre_condition' ] . respond_to? ( :empty? ) && selector [ 'pre_condition' ] . empty? )
end
# validate pre_condition values
2016-04-14 07:17:13 +00:00
return nil if selector [ 'pre_condition' ] && selector [ 'pre_condition' ] !~ / ^(not_set|current_user \ .|specific) /
2015-10-14 14:33:26 +00:00
# get attributes
2015-09-17 18:39:51 +00:00
attributes = attribute . split ( / \ . / )
attribute = " #{ attributes [ 0 ] } s. #{ attributes [ 1 ] } "
2015-10-14 14:33:26 +00:00
if query != ''
query += ' AND '
end
2015-09-17 01:04:16 +00:00
if selector [ 'operator' ] == 'is'
2016-04-14 07:17:13 +00:00
if selector [ 'pre_condition' ] == 'not_set'
2015-10-14 14:33:26 +00:00
if attributes [ 1 ] =~ / ^(created_by|updated_by|owner|customer|user)_id /
2016-04-14 09:39:54 +00:00
query += " #{ attribute } IN (?) "
2015-10-14 14:33:26 +00:00
bind_params . push 1
else
query += " #{ attribute } IS NOT NULL "
end
elsif selector [ 'pre_condition' ] == 'current_user.id'
2016-03-01 14:26:46 +00:00
raise " Use current_user.id in selector, but no current_user is set #{ selector . inspect } " if ! current_user_id
2015-10-14 14:33:26 +00:00
query += " #{ attribute } IN (?) "
bind_params . push current_user_id
elsif selector [ 'pre_condition' ] == 'current_user.organization_id'
2016-03-01 14:26:46 +00:00
raise " Use current_user.id in selector, but no current_user is set #{ selector . inspect } " if ! current_user_id
2015-10-14 14:33:26 +00:00
query += " #{ attribute } IN (?) "
user = User . lookup ( id : current_user_id )
bind_params . push user . organization_id
else
2016-02-26 12:19:57 +00:00
# rubocop:disable Style/IfInsideElse
if selector [ 'value' ] . nil?
2016-03-09 07:17:49 +00:00
query += " #{ attribute } IS NOT NULL "
2016-02-26 12:19:57 +00:00
else
query += " #{ attribute } IN (?) "
bind_params . push selector [ 'value' ]
end
# rubocop:enable Style/IfInsideElse
2015-10-14 14:33:26 +00:00
end
2015-09-17 01:04:16 +00:00
elsif selector [ 'operator' ] == 'is not'
2016-04-14 07:17:13 +00:00
if selector [ 'pre_condition' ] == 'not_set'
2015-10-14 14:33:26 +00:00
if attributes [ 1 ] =~ / ^(created_by|updated_by|owner|customer|user)_id /
2016-04-14 09:39:54 +00:00
query += " #{ attribute } NOT IN (?) "
2015-10-14 14:33:26 +00:00
bind_params . push 1
else
query += " #{ attribute } IS NULL "
end
elsif selector [ 'pre_condition' ] == 'current_user.id'
query += " #{ attribute } NOT IN (?) "
bind_params . push current_user_id
elsif selector [ 'pre_condition' ] == 'current_user.organization_id'
query += " #{ attribute } NOT IN (?) "
user = User . lookup ( id : current_user_id )
bind_params . push user . organization_id
else
2016-02-26 12:19:57 +00:00
# rubocop:disable Style/IfInsideElse
if selector [ 'value' ] . nil?
query += " #{ attribute } IS NOT NULL "
else
query += " #{ attribute } NOT IN (?) "
bind_params . push selector [ 'value' ]
end
# rubocop:enable Style/IfInsideElse
2015-10-14 14:33:26 +00:00
end
2015-09-17 01:04:16 +00:00
elsif selector [ 'operator' ] == 'contains'
2016-01-19 22:30:23 +00:00
query += " #{ attribute } #{ like } (?) "
2015-09-17 01:04:16 +00:00
value = " % #{ selector [ 'value' ] } % "
bind_params . push value
elsif selector [ 'operator' ] == 'contains not'
2016-01-19 22:30:23 +00:00
query += " #{ attribute } NOT #{ like } (?) "
2015-09-17 01:04:16 +00:00
value = " % #{ selector [ 'value' ] } % "
bind_params . push value
2015-09-17 18:39:51 +00:00
elsif selector [ 'operator' ] == 'before (absolute)'
query += " #{ attribute } <= ? "
bind_params . push selector [ 'value' ]
elsif selector [ 'operator' ] == 'after (absolute)'
query += " #{ attribute } >= ? "
2015-09-17 01:04:16 +00:00
bind_params . push selector [ 'value' ]
2015-10-12 13:44:34 +00:00
elsif selector [ 'operator' ] == 'within last (relative)'
query += " #{ attribute } >= ? "
time = nil
if selector [ 'range' ] == 'minute'
time = Time . zone . now - selector [ 'value' ] . to_i . minutes
elsif selector [ 'range' ] == 'hour'
time = Time . zone . now - selector [ 'value' ] . to_i . hours
elsif selector [ 'range' ] == 'day'
time = Time . zone . now - selector [ 'value' ] . to_i . days
elsif selector [ 'range' ] == 'month'
time = Time . zone . now - selector [ 'value' ] . to_i . months
elsif selector [ 'range' ] == 'year'
time = Time . zone . now - selector [ 'value' ] . to_i . years
else
2016-03-01 14:26:46 +00:00
raise " Unknown selector attributes ' #{ selector . inspect } ' "
2015-10-12 13:44:34 +00:00
end
bind_params . push time
elsif selector [ 'operator' ] == 'within next (relative)'
2015-10-12 21:31:33 +00:00
query += " #{ attribute } <= ? "
2015-10-12 13:44:34 +00:00
time = nil
if selector [ 'range' ] == 'minute'
time = Time . zone . now + selector [ 'value' ] . to_i . minutes
elsif selector [ 'range' ] == 'hour'
time = Time . zone . now + selector [ 'value' ] . to_i . hours
elsif selector [ 'range' ] == 'day'
time = Time . zone . now + selector [ 'value' ] . to_i . days
elsif selector [ 'range' ] == 'month'
time = Time . zone . now + selector [ 'value' ] . to_i . months
elsif selector [ 'range' ] == 'year'
time = Time . zone . now + selector [ 'value' ] . to_i . years
else
2016-03-01 14:26:46 +00:00
raise " Unknown selector attributes ' #{ selector . inspect } ' "
2015-10-12 13:44:34 +00:00
end
bind_params . push time
2015-09-17 18:39:51 +00:00
elsif selector [ 'operator' ] == 'before (relative)'
query += " #{ attribute } <= ? "
2015-10-12 13:44:34 +00:00
time = nil
if selector [ 'range' ] == 'minute'
time = Time . zone . now - selector [ 'value' ] . to_i . minutes
elsif selector [ 'range' ] == 'hour'
time = Time . zone . now - selector [ 'value' ] . to_i . hours
elsif selector [ 'range' ] == 'day'
time = Time . zone . now - selector [ 'value' ] . to_i . days
elsif selector [ 'range' ] == 'month'
time = Time . zone . now - selector [ 'value' ] . to_i . months
elsif selector [ 'range' ] == 'year'
time = Time . zone . now - selector [ 'value' ] . to_i . years
else
2016-03-01 14:26:46 +00:00
raise " Unknown selector attributes ' #{ selector . inspect } ' "
2015-10-12 13:44:34 +00:00
end
bind_params . push time
2015-09-17 18:39:51 +00:00
elsif selector [ 'operator' ] == 'after (relative)'
query += " #{ attribute } >= ? "
2015-10-12 13:44:34 +00:00
time = nil
if selector [ 'range' ] == 'minute'
time = Time . zone . now + selector [ 'value' ] . to_i . minutes
elsif selector [ 'range' ] == 'hour'
time = Time . zone . now + selector [ 'value' ] . to_i . hours
elsif selector [ 'range' ] == 'day'
time = Time . zone . now + selector [ 'value' ] . to_i . days
elsif selector [ 'range' ] == 'month'
time = Time . zone . now + selector [ 'value' ] . to_i . months
elsif selector [ 'range' ] == 'year'
time = Time . zone . now + selector [ 'value' ] . to_i . years
else
2016-03-01 14:26:46 +00:00
raise " Unknown selector attributes ' #{ selector . inspect } ' "
2015-10-12 13:44:34 +00:00
end
bind_params . push time
2015-09-17 01:04:16 +00:00
else
2016-03-01 14:26:46 +00:00
raise " Invalid operator ' #{ selector [ 'operator' ] } ' for ' #{ selector [ 'value' ] . inspect } ' "
2015-09-17 01:04:16 +00:00
end
}
2015-09-17 18:39:51 +00:00
[ query , bind_params , tables ]
2015-09-17 01:04:16 +00:00
end
2016-02-20 07:14:51 +00:00
= begin
2016-05-03 00:36:44 +00:00
perform changes on ticket
2016-05-03 11:03:10 +00:00
ticket . perform_changes ( { } , 'trigger' , item )
2016-05-03 00:36:44 +00:00
= end
2016-06-27 11:24:48 +00:00
def perform_changes ( perform , perform_origin , item = nil )
logger . debug " Perform #{ perform_origin } #{ perform . inspect } on Ticket.find( #{ id } ) "
2016-05-03 00:36:44 +00:00
changed = false
perform . each do | key , value |
( object_name , attribute ) = key . split ( '.' , 2 )
raise " Unable to update object #{ object_name } . #{ attribute } , only can update tickets and send notifications! " if object_name != 'ticket' && object_name != 'notification'
# send notification
if object_name == 'notification'
recipients = [ ]
if value [ 'recipient' ] == 'ticket_customer'
recipients . push User . lookup ( id : customer_id )
elsif value [ 'recipient' ] == 'ticket_owner'
recipients . push User . lookup ( id : owner_id )
elsif value [ 'recipient' ] == 'ticket_agents'
recipients = recipients . concat ( agent_of_group )
else
logger . error " Unknown email notification recipient ' #{ value [ 'recipient' ] } ' "
next
end
recipient_string = ''
recipient_already = { }
recipients . each { | user |
2016-05-03 11:03:10 +00:00
# send notifications only to email adresses
2016-05-03 00:36:44 +00:00
next if ! user . email
next if user . email !~ / @ /
2016-05-03 11:03:10 +00:00
# do not sent notifications to this recipients
2016-05-06 07:57:04 +00:00
send_no_auto_response_reg_exp = Setting . get ( 'send_no_auto_response_reg_exp' )
begin
next if user . email =~ / #{ send_no_auto_response_reg_exp } /i
rescue = > e
logger . error " ERROR: Invalid regex ' #{ send_no_auto_response_reg_exp } ' in setting send_no_auto_response_reg_exp "
logger . error 'ERROR: ' + e . inspect
next if user . email =~ / (mailer-daemon|postmaster|abuse|root)@.+? \ ..+? /i
end
2016-05-03 11:03:10 +00:00
2016-05-03 00:36:44 +00:00
email = user . email . downcase . strip
next if recipient_already [ email ]
recipient_already [ email ] = true
if recipient_string != ''
recipient_string += ', '
end
recipient_string += email
}
next if recipient_string == ''
group = self . group
next if ! group
email_address = group . email_address
next if ! email_address
next if ! email_address . channel_id
2016-05-03 11:03:10 +00:00
# check if notification should be send because of customer emails
if item && item [ :article_id ]
article = Ticket :: Article . lookup ( id : item [ :article_id ] )
if article
type = Ticket :: Article :: Type . lookup ( id : article . type_id )
sender = Ticket :: Article :: Sender . lookup ( id : article . sender_id )
if sender && sender . name == 'Customer' && type && type . name == 'email'
# get attachment
list = Store . list (
object : 'Ticket::Article::Mail' ,
o_id : article . id ,
)
if list && list [ 0 ]
file = Store . find ( list [ 0 ] . id )
if file
content = file . content
if content
parser = Channel :: EmailParser . new
mail = parser . parse ( content )
# check headers
next if mail [ 'x-loop' . to_sym ] =~ / yes /i
next if mail [ 'precedence' . to_sym ] =~ / bulk /i
next if mail [ 'auto-submitted' . to_sym ] =~ / auto-generated /i
next if mail [ 'x-auto-response-suppress' . to_sym ] =~ / yes /i
end
end
end
end
end
end
2016-05-03 00:36:44 +00:00
objects = {
ticket : self ,
article : articles . last ,
#recipient: user,
#changes: changes,
}
# get subject
2016-05-03 13:51:07 +00:00
value [ 'subject' ] . gsub! ( / \ # \ {config \ .(.+?) \ } / , '<%= c "\\1", false %>' )
2016-05-03 00:36:44 +00:00
value [ 'subject' ] . gsub! ( / \ # \ {(.+?) \ } / , '<%= d "\\1", false %>' )
subject = NotificationFactory :: Mailer . template (
templateInline : value [ 'subject' ] ,
locale : 'en-en' ,
objects : objects ,
)
subject = subject_build ( subject )
2016-05-03 13:51:07 +00:00
value [ 'body' ] . gsub! ( / \ # \ {config \ .(.+?) \ } / , '<%= c "\\1", true %>' )
2016-05-03 00:36:44 +00:00
value [ 'body' ] . gsub! ( / \ # \ {(.+?) \ } / , '<%= d "\\1", true %>' )
body = NotificationFactory :: Mailer . template (
templateInline : value [ 'body' ] ,
locale : 'en-en' ,
objects : objects ,
)
Ticket :: Article . create (
ticket_id : id ,
to : recipient_string ,
subject : subject ,
content_type : 'text/html' ,
body : body ,
internal : false ,
sender : Ticket :: Article :: Sender . find_by ( name : 'System' ) ,
type : Ticket :: Article :: Type . find_by ( name : 'email' ) ,
2016-06-27 11:24:48 +00:00
preferences : {
perform_origin : perform_origin ,
} ,
2016-05-03 00:36:44 +00:00
updated_by_id : 1 ,
created_by_id : 1 ,
)
next
end
# update tags
if key == 'ticket.tags'
next if value [ 'value' ] . empty?
tags = value [ 'value' ] . split ( / , / )
if value [ 'operator' ] == 'add'
tags . each { | tag |
Tag . tag_add (
object : 'Ticket' ,
o_id : id ,
item : tag ,
)
}
elsif value [ 'operator' ] == 'remove'
tags . each { | tag |
Tag . tag_remove (
object : 'Ticket' ,
o_id : id ,
item : tag ,
)
}
else
logger . error " Unknown #{ attribute } operator #{ value [ 'operator' ] } "
end
next
end
# update ticket
next if self [ attribute ] . to_s == value [ 'value' ] . to_s
changed = true
self [ attribute ] = value [ 'value' ]
logger . debug " set #{ object_name } . #{ attribute } = #{ value [ 'value' ] . inspect } "
end
return if ! changed
save
end
= begin
2016-02-20 07:14:51 +00:00
get all email references headers of a ticket , to exclude some , parse it as array into method
references = ticket . get_references
result
[ 'message-id-1234' , 'message-id-5678' ]
ignore references header ( s )
references = ticket . get_references ( [ 'message-id-5678' ] )
result
[ 'message-id-1234' ]
= end
def get_references ( ignore = [ ] )
references = [ ]
Ticket :: Article . select ( 'in_reply_to, message_id' ) . where ( ticket_id : id ) . each { | article |
if ! article . in_reply_to . empty?
references . push article . in_reply_to
end
next if ! article . message_id
next if article . message_id . empty?
references . push article . message_id
}
ignore . each { | item |
references . delete ( item )
}
references
end
2012-11-28 10:03:17 +00:00
private
2012-11-28 09:46:26 +00:00
2013-08-15 22:16:38 +00:00
def check_generate
2015-05-07 12:10:38 +00:00
return if number
2013-08-15 22:16:38 +00:00
self . number = Ticket :: Number . generate
2013-06-12 15:59:58 +00:00
end
2013-08-15 22:16:38 +00:00
2015-01-07 21:28:15 +00:00
def check_title
2015-05-07 12:10:38 +00:00
return if ! title
title . gsub! ( / \ s| \ t| \ r / , ' ' )
2015-01-07 21:28:15 +00:00
end
2013-06-12 15:59:58 +00:00
def check_defaults
2015-05-07 12:10:38 +00:00
if ! owner_id
2013-06-12 15:59:58 +00:00
self . owner_id = 1
end
2015-04-30 15:25:04 +00:00
2015-05-07 12:10:38 +00:00
return if ! customer_id
2015-04-30 15:25:04 +00:00
2016-03-18 02:04:49 +00:00
customer = User . find ( customer_id )
2015-05-07 12:10:38 +00:00
return if organization_id == customer . organization_id
2015-04-30 15:25:04 +00:00
self . organization_id = customer . organization_id
2013-06-12 15:59:58 +00:00
end
2013-06-12 14:57:29 +00:00
2015-02-11 22:05:31 +00:00
def reset_pending_time
# ignore if no state has changed
2015-05-07 12:10:38 +00:00
return if ! changes [ 'state_id' ]
2015-02-11 22:05:31 +00:00
# check if new state isn't pending*
2016-03-18 02:04:49 +00:00
current_state = Ticket :: State . lookup ( id : state_id )
current_state_type = Ticket :: StateType . lookup ( id : current_state . state_type_id )
2015-02-11 22:05:31 +00:00
# in case, set pending_time to nil
2015-04-30 15:25:04 +00:00
return if current_state_type . name =~ / ^pending /i
self . pending_time = nil
2015-02-11 22:05:31 +00:00
end
2013-08-16 14:30:51 +00:00
def destroy_dependencies
2013-06-13 15:03:08 +00:00
2013-08-16 14:30:51 +00:00
# delete articles
2015-05-07 12:10:38 +00:00
articles . destroy_all
2015-04-01 11:14:46 +00:00
# destroy online notifications
2016-03-18 02:04:49 +00:00
OnlineNotification . remove ( self . class . to_s , id )
2013-08-16 14:30:51 +00:00
end
2012-04-10 14:06:46 +00:00
2015-04-27 14:15:29 +00:00
end