123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901 |
- # Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
- class Ticket < ApplicationModel
- include Ticket::Escalation
- include Ticket::Subject
- include Ticket::Permission
- load 'ticket/assets.rb'
- include Ticket::Assets
- load 'ticket/history_log.rb'
- include Ticket::HistoryLog
- load 'ticket/activity_stream_log.rb'
- include Ticket::ActivityStreamLog
- load 'ticket/search_index.rb'
- include Ticket::SearchIndex
- extend Ticket::Search
- store :preferences
- before_create :check_generate, :check_defaults, :check_title
- before_update :check_defaults, :check_title, :reset_pending_time
- before_destroy :destroy_dependencies
- notify_clients_support
- latest_change_support
- activity_stream_support ignore_attributes: {
- organization_id: true, # organization_id will channge automatically on user update
- create_article_type_id: true,
- create_article_sender_id: true,
- article_count: true,
- 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,
- update_time_sla_time: true,
- update_time_in_min: true,
- update_time_diff_in_min: true,
- last_contact: true,
- last_contact_agent: true,
- last_contact_customer: true,
- }
- history_support ignore_attributes: {
- create_article_type_id: true,
- create_article_sender_id: true,
- article_count: true,
- }
- search_index_support
- belongs_to :group, class_name: 'Group'
- has_many :articles, class_name: 'Ticket::Article', after_add: :cache_update, after_remove: :cache_update
- belongs_to :organization, class_name: 'Organization'
- 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'
- self.inheritance_column = nil
- attr_accessor :callback_loop
- =begin
- list of agents in group of ticket
- ticket = Ticket.find(123)
- result = ticket.agent_of_group
- returns
- result = [user1, user2, ...]
- =end
- def agent_of_group
- Group.find(group_id)
- .users.where(active: true)
- .joins(:roles)
- .where('roles.name' => Z_ROLENAME_AGENT, 'roles.active' => true)
- .order('users.login')
- .uniq()
- end
- =begin
- get user access conditions
- conditions = Ticket.access_condition( User.find(1) )
- returns
- result = [user1, user2, ...]
- =end
- def self.access_condition(user)
- access_condition = []
- if user.role?(Z_ROLENAME_AGENT)
- group_ids = Group.select('groups.id').joins(:users)
- .where('groups_users.user_id = ?', user.id)
- .where('groups.active = ?', true)
- .map(&:id)
- access_condition = [ 'group_id IN (?)', group_ids ]
- else
- access_condition = if !user.organization || ( !user.organization.shared || user.organization.shared == false )
- [ 'tickets.customer_id = ?', user.id ]
- else
- [ '(tickets.customer_id = ? OR tickets.organization_id = ?)', user.id, user.organization.id ]
- end
- end
- access_condition
- end
- =begin
- processes tickets which have reached their pending time and sets next state_id
- processed_tickets = Ticket.process_pending
- returns
- processed_tickets = [<Ticket>, ...]
- =end
- def self.process_pending
- result = []
- # 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
- # execute object transaction manually
- Observer::Transaction.commit
- result.push ticket
- }
- end
- # 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)
- if !ticket_states_pending_reminder.empty?
- reminder_state_map = {}
- ticket_states_pending_reminder.each { |state|
- reminder_state_map[state.id] = state.next_state_id
- }
- tickets = where(state_id: reminder_state_map.keys)
- .where('pending_time <= ?', Time.zone.now)
- tickets.each { |ticket|
- article_id = nil
- article = Ticket::Article.last_customer_agent_article(ticket.id)
- if article
- article_id = article.id
- end
- # send notification
- Transaction::BackgroundJob.run(
- object: 'Ticket',
- type: 'reminder_reached',
- object_id: ticket.id,
- article_id: article_id,
- user_id: 1,
- )
- result.push ticket
- }
- end
- result
- end
- =begin
- 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
- article_id = nil
- article = Ticket::Article.last_customer_agent_article(ticket.id)
- if article
- article_id = article.id
- end
- # send escalation
- if ticket.escalation_time < Time.zone.now
- Transaction::BackgroundJob.run(
- object: 'Ticket',
- type: 'escalation',
- object_id: ticket.id,
- article_id: article_id,
- user_id: 1,
- )
- result.push ticket
- next
- end
- # check if warning need to be sent
- Transaction::BackgroundJob.run(
- object: 'Ticket',
- type: 'escalation_warning',
- object_id: ticket.id,
- article_id: article_id,
- user_id: 1,
- )
- result.push ticket
- }
- result
- end
- =begin
- merge tickets
- ticket = Ticket.find(123)
- result = ticket.merge_to(
- ticket_id: 123,
- user_id: 123,
- )
- returns
- result = true|false
- =end
- def merge_to(data)
- # update articles
- Ticket::Article.where(ticket_id: id).each(&:touch)
- # quiet update of reassign of articles
- Ticket::Article.where(ticket_id: id).update_all(['ticket_id = ?', data[:ticket_id] ])
- # touch new ticket (to broadcast change)
- Ticket.find(data[:ticket_id]).touch
- # update history
- # create new merge article
- Ticket::Article.create(
- ticket_id: id,
- type_id: Ticket::Article::Type.lookup(name: 'note').id,
- sender_id: Ticket::Article::Sender.lookup(name: Z_ROLENAME_AGENT).id,
- body: 'merged',
- internal: false,
- created_by_id: data[:user_id],
- updated_by_id: data[:user_id],
- )
- # add history to both
- # link tickets
- Link.add(
- link_type: 'parent',
- link_object_source: 'Ticket',
- link_object_source_value: data[:ticket_id],
- link_object_target: 'Ticket',
- link_object_target_value: id
- )
- # set state to 'merged'
- self.state_id = Ticket::State.lookup(name: 'merged').id
- # rest owner
- self.owner_id = User.find_by(login: '-').id
- # save ticket
- save
- end
- =begin
- check if online notifcation should be shown in general as already seen with current state
- ticket = Ticket.find(1)
- 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)
- returns
- result = true # or false
- =end
- def online_notification_seen_state(user_id_check = nil)
- state = Ticket::State.lookup(id: state_id)
- state_type = Ticket::StateType.lookup(id: state.state_type_id)
- # 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
- # set all to seen if pending action state is a closed or merged state
- if state_type.name == 'pending action' && state.next_state_id
- state = Ticket::State.lookup(id: state.next_state_id)
- state_type = Ticket::StateType.lookup(id: state.state_type_id)
- end
- # set all to seen if new state is pending reminder state
- 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
- # set all to seen if new state is a closed or merged state
- return true if state_type.name == 'closed'
- return true if state_type.name == 'merged'
- false
- end
- =begin
- get count of tickets and tickets which match on selector
- ticket_count, tickets = Ticket.selectors(params[:condition], limit, current_user)
- =end
- def self.selectors(selectors, limit = 10, current_user = nil)
- raise 'no selectors given' if !selectors
- query, bind_params, tables = selector2sql(selectors, current_user)
- return [] if !query
- 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)
- [ticket_count, tickets]
- end
- =begin
- generate condition query to search for tickets based on condition
- query_condition, bind_condition = selector2sql(params[:condition], current_user)
- condition example
- {
- 'ticket.state_id' => {
- operator: 'is',
- value: [1,2,5]
- },
- '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',
- },
- 'ticket.owner_id' => {
- operator: 'is', # is not
- pre_condition: 'current_user.id',
- },
- 'ticket.owner_id' => {
- operator: 'is', # is not
- pre_condition: 'specific',
- value: 4711,
- },
- 'ticket.escalation_time' => {
- operator: 'is not', # not
- value: nil,
- }
- }
- =end
- def self.selector2sql(selectors, current_user = nil)
- current_user_id = UserInfo.current_user_id
- if current_user
- current_user_id = current_user.id
- end
- return if !selectors
- # remember query and bind params
- query = ''
- bind_params = []
- like = Rails.application.config.db_like
- # get tables to join
- tables = ''
- selectors.each {|attribute, selector|
- selector = attribute.split(/\./)
- next if !selector[1]
- next if selector[0] == 'ticket'
- next if tables.include?(selector[0])
- 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'
- elsif selector[0] == 'article'
- tables += ', ticket_articles articles'
- query += 'tickets.id = articles.ticket_id'
- else
- raise "invalid selector #{attribute.inspect}->#{selector.inspect}"
- end
- }
- # add conditions
- selectors.each {|attribute, selector_raw|
- # validation
- raise "Invalid selector #{selector_raw.inspect}" if !selector_raw
- raise "Invalid selector #{selector_raw.inspect}" if !selector_raw.respond_to?(:key?)
- selector = selector_raw.stringify_keys
- raise "Invalid selector, operator missing #{selector.inspect}" if !selector['operator']
- # validate value / allow empty but only if pre_condition exists
- if !selector.key?('value') || ((selector['value'].class == String || selector['value'].class == Array) && (selector['value'].respond_to?(:empty?) && selector['value'].empty?))
- return nil if selector['pre_condition'].nil? || (selector['pre_condition'].respond_to?(:empty?) && selector['pre_condition'].empty?)
- end
- # validate pre_condition values
- return nil if selector['pre_condition'] && selector['pre_condition'] !~ /^(not_set|current_user\.|specific)/
- # get attributes
- attributes = attribute.split(/\./)
- attribute = "#{attributes[0]}s.#{attributes[1]}"
- if query != ''
- query += ' AND '
- end
- if selector['operator'] == 'is'
- if selector['pre_condition'] == 'not_set'
- if attributes[1] =~ /^(created_by|updated_by|owner|customer|user)_id/
- query += "#{attribute} IN (?)"
- bind_params.push 1
- else
- query += "#{attribute} IS NOT NULL"
- end
- elsif selector['pre_condition'] == 'current_user.id'
- raise "Use current_user.id in selector, but no current_user is set #{selector.inspect}" if !current_user_id
- query += "#{attribute} IN (?)"
- bind_params.push current_user_id
- elsif selector['pre_condition'] == 'current_user.organization_id'
- raise "Use current_user.id in selector, but no current_user is set #{selector.inspect}" if !current_user_id
- query += "#{attribute} IN (?)"
- user = User.lookup(id: current_user_id)
- bind_params.push user.organization_id
- else
- # rubocop:disable Style/IfInsideElse
- if selector['value'].nil?
- query += "#{attribute} IS NOT NULL"
- else
- query += "#{attribute} IN (?)"
- bind_params.push selector['value']
- end
- # rubocop:enable Style/IfInsideElse
- end
- elsif selector['operator'] == 'is not'
- if selector['pre_condition'] == 'not_set'
- if attributes[1] =~ /^(created_by|updated_by|owner|customer|user)_id/
- query += "#{attribute} NOT IN (?)"
- 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
- # 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
- end
- elsif selector['operator'] == 'contains'
- query += "#{attribute} #{like} (?)"
- value = "%#{selector['value']}%"
- bind_params.push value
- elsif selector['operator'] == 'contains not'
- query += "#{attribute} NOT #{like} (?)"
- value = "%#{selector['value']}%"
- bind_params.push value
- elsif selector['operator'] == 'before (absolute)'
- query += "#{attribute} <= ?"
- bind_params.push selector['value']
- elsif selector['operator'] == 'after (absolute)'
- query += "#{attribute} >= ?"
- bind_params.push selector['value']
- 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
- raise "Unknown selector attributes '#{selector.inspect}'"
- end
- bind_params.push time
- elsif selector['operator'] == 'within next (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
- raise "Unknown selector attributes '#{selector.inspect}'"
- end
- bind_params.push time
- elsif selector['operator'] == 'before (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
- raise "Unknown selector attributes '#{selector.inspect}'"
- end
- bind_params.push time
- elsif selector['operator'] == 'after (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
- raise "Unknown selector attributes '#{selector.inspect}'"
- end
- bind_params.push time
- else
- raise "Invalid operator '#{selector['operator']}' for '#{selector['value'].inspect}'"
- end
- }
- [query, bind_params, tables]
- end
- =begin
- perform changes on ticket
- ticket.perform_changes({}, 'trigger', item)
- =end
- def perform_changes(perform, log, item = nil)
- logger.debug "Perform #{log} #{perform.inspect} on Ticket.find(#{id})"
- 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|
- # send notifications only to email adresses
- next if !user.email
- next if user.email !~ /@/
- # do not sent notifications to this recipients
- 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
- 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
- # 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
- objects = {
- ticket: self,
- article: articles.last,
- #recipient: user,
- #changes: changes,
- }
- # get subject
- value['subject'].gsub!(/\#\{config\.(.+?)\}/, '<%= c "\\1", false %>')
- value['subject'].gsub!(/\#\{(.+?)\}/, '<%= d "\\1", false %>')
- subject = NotificationFactory::Mailer.template(
- templateInline: value['subject'],
- locale: 'en-en',
- objects: objects,
- )
- subject = subject_build(subject)
- value['body'].gsub!(/\#\{config\.(.+?)\}/, '<%= c "\\1", true %>')
- 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'),
- 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
- 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
- private
- def check_generate
- return if number
- self.number = Ticket::Number.generate
- end
- def check_title
- return if !title
- title.gsub!(/\s|\t|\r/, ' ')
- end
- def check_defaults
- if !owner_id
- self.owner_id = 1
- end
- return if !customer_id
- customer = User.find(customer_id)
- return if organization_id == customer.organization_id
- self.organization_id = customer.organization_id
- end
- def reset_pending_time
- # ignore if no state has changed
- return if !changes['state_id']
- # check if new state isn't pending*
- current_state = Ticket::State.lookup(id: state_id)
- current_state_type = Ticket::StateType.lookup(id: current_state.state_type_id)
- # in case, set pending_time to nil
- return if current_state_type.name =~ /^pending/i
- self.pending_time = nil
- end
- def destroy_dependencies
- # delete articles
- articles.destroy_all
- # destroy online notifications
- OnlineNotification.remove(self.class.to_s, id)
- end
- end
|