ticket.rb 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917
  1. # Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
  2. class Ticket < ApplicationModel
  3. include Ticket::Escalation
  4. include Ticket::Subject
  5. load 'ticket/permission.rb'
  6. include Ticket::Permission
  7. load 'ticket/assets.rb'
  8. include Ticket::Assets
  9. load 'ticket/history_log.rb'
  10. include Ticket::HistoryLog
  11. load 'ticket/activity_stream_log.rb'
  12. include Ticket::ActivityStreamLog
  13. load 'ticket/search_index.rb'
  14. include Ticket::SearchIndex
  15. extend Ticket::Search
  16. store :preferences
  17. before_create :check_generate, :check_defaults, :check_title, :check_escalation_update
  18. before_update :check_defaults, :check_title, :reset_pending_time, :check_escalation_update
  19. before_destroy :destroy_dependencies
  20. validates :group_id, presence: true
  21. validates :priority_id, presence: true
  22. validates :state_id, presence: true
  23. notify_clients_support
  24. latest_change_support
  25. activity_stream_support(
  26. permission: 'ticket.agent',
  27. ignore_attributes: {
  28. organization_id: true, # organization_id will channge automatically on user update
  29. create_article_type_id: true,
  30. create_article_sender_id: true,
  31. article_count: true,
  32. first_response_at: true,
  33. first_response_escalation_at: true,
  34. first_response_in_min: true,
  35. first_response_diff_in_min: true,
  36. close_at: true,
  37. close_escalation_at: true,
  38. close_in_min: true,
  39. close_diff_in_min: true,
  40. update_escalation_at: true,
  41. update_in_min: true,
  42. update_diff_in_min: true,
  43. last_contact_at: true,
  44. last_contact_agent_at: true,
  45. last_contact_customer_at: true,
  46. }
  47. )
  48. history_support(
  49. ignore_attributes: {
  50. create_article_type_id: true,
  51. create_article_sender_id: true,
  52. article_count: true,
  53. preferences: true,
  54. }
  55. )
  56. search_index_support
  57. belongs_to :group, class_name: 'Group'
  58. has_many :articles, class_name: 'Ticket::Article', after_add: :cache_update, after_remove: :cache_update
  59. belongs_to :organization, class_name: 'Organization'
  60. belongs_to :state, class_name: 'Ticket::State'
  61. belongs_to :priority, class_name: 'Ticket::Priority'
  62. belongs_to :owner, class_name: 'User'
  63. belongs_to :customer, class_name: 'User'
  64. belongs_to :created_by, class_name: 'User'
  65. belongs_to :updated_by, class_name: 'User'
  66. belongs_to :create_article_type, class_name: 'Ticket::Article::Type'
  67. belongs_to :create_article_sender, class_name: 'Ticket::Article::Sender'
  68. self.inheritance_column = nil
  69. attr_accessor :callback_loop
  70. =begin
  71. list of agents in group of ticket
  72. ticket = Ticket.find(123)
  73. result = ticket.agent_of_group
  74. returns
  75. result = [user1, user2, ...]
  76. =end
  77. def agent_of_group
  78. roles = Role.with_permissions('ticket.agent')
  79. role_ids = roles.map(&:id)
  80. Group.find(group_id)
  81. .users.where(active: true)
  82. .joins(:roles)
  83. .where('roles.id' => role_ids, 'roles.active' => true)
  84. .order('users.login')
  85. .uniq()
  86. end
  87. =begin
  88. get user access conditions
  89. conditions = Ticket.access_condition( User.find(1) )
  90. returns
  91. result = [user1, user2, ...]
  92. =end
  93. def self.access_condition(user)
  94. access_condition = []
  95. if user.permissions?('ticket.agent')
  96. group_ids = Group.select('groups.id').joins(:users)
  97. .where('groups_users.user_id = ?', user.id)
  98. .where('groups.active = ?', true)
  99. .map(&:id)
  100. access_condition = [ 'group_id IN (?)', group_ids ]
  101. else
  102. access_condition = if !user.organization || ( !user.organization.shared || user.organization.shared == false )
  103. [ 'tickets.customer_id = ?', user.id ]
  104. else
  105. [ '(tickets.customer_id = ? OR tickets.organization_id = ?)', user.id, user.organization.id ]
  106. end
  107. end
  108. access_condition
  109. end
  110. =begin
  111. processes tickets which have reached their pending time and sets next state_id
  112. processed_tickets = Ticket.process_pending
  113. returns
  114. processed_tickets = [<Ticket>, ...]
  115. =end
  116. def self.process_pending
  117. result = []
  118. # process pending action tickets
  119. pending_action = Ticket::StateType.find_by(name: 'pending action')
  120. ticket_states_pending_action = Ticket::State.where(state_type_id: pending_action)
  121. .where.not(next_state_id: nil)
  122. if !ticket_states_pending_action.empty?
  123. next_state_map = {}
  124. ticket_states_pending_action.each { |state|
  125. next_state_map[state.id] = state.next_state_id
  126. }
  127. tickets = where(state_id: next_state_map.keys)
  128. .where('pending_time <= ?', Time.zone.now)
  129. tickets.each { |ticket|
  130. Transaction.execute do
  131. ticket.state_id = next_state_map[ticket.state_id]
  132. ticket.updated_at = Time.zone.now
  133. ticket.updated_by_id = 1
  134. ticket.save!
  135. end
  136. result.push ticket
  137. }
  138. end
  139. # process pending reminder tickets
  140. pending_reminder = Ticket::StateType.find_by(name: 'pending reminder')
  141. ticket_states_pending_reminder = Ticket::State.where(state_type_id: pending_reminder)
  142. if !ticket_states_pending_reminder.empty?
  143. reminder_state_map = {}
  144. ticket_states_pending_reminder.each { |state|
  145. reminder_state_map[state.id] = state.next_state_id
  146. }
  147. tickets = where(state_id: reminder_state_map.keys)
  148. .where('pending_time <= ?', Time.zone.now)
  149. tickets.each { |ticket|
  150. article_id = nil
  151. article = Ticket::Article.last_customer_agent_article(ticket.id)
  152. if article
  153. article_id = article.id
  154. end
  155. # send notification
  156. Transaction::BackgroundJob.run(
  157. object: 'Ticket',
  158. type: 'reminder_reached',
  159. object_id: ticket.id,
  160. article_id: article_id,
  161. user_id: 1,
  162. )
  163. result.push ticket
  164. }
  165. end
  166. result
  167. end
  168. =begin
  169. processes escalated tickets
  170. processed_tickets = Ticket.process_escalation
  171. returns
  172. processed_tickets = [<Ticket>, ...]
  173. =end
  174. def self.process_escalation
  175. result = []
  176. # get max warning diff
  177. tickets = where('escalation_at <= ?', Time.zone.now + 15.minutes)
  178. tickets.each { |ticket|
  179. # get sla
  180. sla = ticket.escalation_calculation_get_sla
  181. article_id = nil
  182. article = Ticket::Article.last_customer_agent_article(ticket.id)
  183. if article
  184. article_id = article.id
  185. end
  186. # send escalation
  187. if ticket.escalation_at < Time.zone.now
  188. Transaction::BackgroundJob.run(
  189. object: 'Ticket',
  190. type: 'escalation',
  191. object_id: ticket.id,
  192. article_id: article_id,
  193. user_id: 1,
  194. )
  195. result.push ticket
  196. next
  197. end
  198. # check if warning need to be sent
  199. Transaction::BackgroundJob.run(
  200. object: 'Ticket',
  201. type: 'escalation_warning',
  202. object_id: ticket.id,
  203. article_id: article_id,
  204. user_id: 1,
  205. )
  206. result.push ticket
  207. }
  208. result
  209. end
  210. =begin
  211. merge tickets
  212. ticket = Ticket.find(123)
  213. result = ticket.merge_to(
  214. ticket_id: 123,
  215. user_id: 123,
  216. )
  217. returns
  218. result = true|false
  219. =end
  220. def merge_to(data)
  221. # update articles
  222. Transaction.execute do
  223. Ticket::Article.where(ticket_id: id).each(&:touch)
  224. # quiet update of reassign of articles
  225. Ticket::Article.where(ticket_id: id).update_all(['ticket_id = ?', data[:ticket_id]])
  226. # update history
  227. # create new merge article
  228. Ticket::Article.create(
  229. ticket_id: id,
  230. type_id: Ticket::Article::Type.lookup(name: 'note').id,
  231. sender_id: Ticket::Article::Sender.lookup(name: 'Agent').id,
  232. body: 'merged',
  233. internal: false,
  234. created_by_id: data[:user_id],
  235. updated_by_id: data[:user_id],
  236. )
  237. # add history to both
  238. # link tickets
  239. Link.add(
  240. link_type: 'parent',
  241. link_object_source: 'Ticket',
  242. link_object_source_value: data[:ticket_id],
  243. link_object_target: 'Ticket',
  244. link_object_target_value: id
  245. )
  246. # set state to 'merged'
  247. self.state_id = Ticket::State.lookup(name: 'merged').id
  248. # rest owner
  249. self.owner_id = User.find_by(login: '-').id
  250. # save ticket
  251. save!
  252. # touch new ticket (to broadcast change)
  253. Ticket.find(data[:ticket_id]).touch
  254. end
  255. true
  256. end
  257. =begin
  258. check if online notifcation should be shown in general as already seen with current state
  259. ticket = Ticket.find(1)
  260. seen = ticket.online_notification_seen_state(user_id_check)
  261. returns
  262. result = true # or false
  263. check if online notifcation should be shown for this user as already seen with current state
  264. ticket = Ticket.find(1)
  265. seen = ticket.online_notification_seen_state(check_user_id)
  266. returns
  267. result = true # or false
  268. =end
  269. def online_notification_seen_state(user_id_check = nil)
  270. state = Ticket::State.lookup(id: state_id)
  271. state_type = Ticket::StateType.lookup(id: state.state_type_id)
  272. # always to set unseen for ticket owner
  273. if state_type.name != 'merged'
  274. if user_id_check
  275. return false if user_id_check == owner_id && user_id_check != updated_by_id
  276. end
  277. end
  278. # set all to seen if pending action state is a closed or merged state
  279. if state_type.name == 'pending action' && state.next_state_id
  280. state = Ticket::State.lookup(id: state.next_state_id)
  281. state_type = Ticket::StateType.lookup(id: state.state_type_id)
  282. end
  283. # set all to seen if new state is pending reminder state
  284. if state_type.name == 'pending reminder'
  285. if user_id_check
  286. return false if owner_id == 1
  287. return false if updated_by_id != owner_id && user_id_check == owner_id
  288. return true
  289. end
  290. return true
  291. end
  292. # set all to seen if new state is a closed or merged state
  293. return true if state_type.name == 'closed'
  294. return true if state_type.name == 'merged'
  295. false
  296. end
  297. =begin
  298. get count of tickets and tickets which match on selector
  299. ticket_count, tickets = Ticket.selectors(params[:condition], limit, current_user)
  300. =end
  301. def self.selectors(selectors, limit = 10, current_user = nil)
  302. raise 'no selectors given' if !selectors
  303. query, bind_params, tables = selector2sql(selectors, current_user)
  304. return [] if !query
  305. if !current_user
  306. ticket_count = Ticket.where(query, *bind_params).joins(tables).count
  307. tickets = Ticket.where(query, *bind_params).joins(tables).limit(limit)
  308. return [ticket_count, tickets]
  309. end
  310. access_condition = Ticket.access_condition(current_user)
  311. ticket_count = Ticket.where(access_condition).where(query, *bind_params).joins(tables).count
  312. tickets = Ticket.where(access_condition).where(query, *bind_params).joins(tables).limit(limit)
  313. [ticket_count, tickets]
  314. end
  315. =begin
  316. generate condition query to search for tickets based on condition
  317. query_condition, bind_condition = selector2sql(params[:condition], current_user)
  318. condition example
  319. {
  320. 'ticket.title' => {
  321. operator: 'contains', # contains not
  322. value: 'some value',
  323. },
  324. 'ticket.state_id' => {
  325. operator: 'is',
  326. value: [1,2,5]
  327. },
  328. 'ticket.created_at' => {
  329. operator: 'after (absolute)', # after,before
  330. value: '2015-10-17T06:00:00.000Z',
  331. },
  332. 'ticket.created_at' => {
  333. operator: 'within next (relative)', # before,within,in,after
  334. range: 'day', # minute|hour|day|month|year
  335. value: '25',
  336. },
  337. 'ticket.owner_id' => {
  338. operator: 'is', # is not
  339. pre_condition: 'current_user.id',
  340. },
  341. 'ticket.owner_id' => {
  342. operator: 'is', # is not
  343. pre_condition: 'specific',
  344. value: 4711,
  345. },
  346. 'ticket.escalation_at' => {
  347. operator: 'is not', # not
  348. value: nil,
  349. }
  350. }
  351. =end
  352. def self.selector2sql(selectors, current_user = nil)
  353. current_user_id = UserInfo.current_user_id
  354. if current_user
  355. current_user_id = current_user.id
  356. end
  357. return if !selectors
  358. # remember query and bind params
  359. query = ''
  360. bind_params = []
  361. like = Rails.application.config.db_like
  362. # get tables to join
  363. tables = ''
  364. selectors.each { |attribute, selector|
  365. selector = attribute.split(/\./)
  366. next if !selector[1]
  367. next if selector[0] == 'ticket'
  368. next if tables.include?(selector[0])
  369. if query != ''
  370. query += ' AND '
  371. end
  372. if selector[0] == 'customer'
  373. tables += ', users customers'
  374. query += 'tickets.customer_id = customers.id'
  375. elsif selector[0] == 'organization'
  376. tables += ', organizations'
  377. query += 'tickets.organization_id = organizations.id'
  378. elsif selector[0] == 'owner'
  379. tables += ', users owners'
  380. query += 'tickets.owner_id = owners.id'
  381. elsif selector[0] == 'article'
  382. tables += ', ticket_articles articles'
  383. query += 'tickets.id = articles.ticket_id'
  384. else
  385. raise "invalid selector #{attribute.inspect}->#{selector.inspect}"
  386. end
  387. }
  388. # add conditions
  389. selectors.each { |attribute, selector_raw|
  390. # validation
  391. raise "Invalid selector #{selector_raw.inspect}" if !selector_raw
  392. raise "Invalid selector #{selector_raw.inspect}" if !selector_raw.respond_to?(:key?)
  393. selector = selector_raw.stringify_keys
  394. raise "Invalid selector, operator missing #{selector.inspect}" if !selector['operator']
  395. # validate value / allow empty but only if pre_condition exists and is not specific
  396. if !selector.key?('value') || ((selector['value'].class == String || selector['value'].class == Array) && (selector['value'].respond_to?(:empty?) && selector['value'].empty?))
  397. return nil if selector['pre_condition'].nil?
  398. return nil if selector['pre_condition'].respond_to?(:empty?) && selector['pre_condition'].empty?
  399. return nil if selector['pre_condition'] == 'specific'
  400. end
  401. # validate pre_condition values
  402. return nil if selector['pre_condition'] && selector['pre_condition'] !~ /^(not_set|current_user\.|specific)/
  403. # get attributes
  404. attributes = attribute.split(/\./)
  405. attribute = "#{attributes[0]}s.#{attributes[1]}"
  406. if query != ''
  407. query += ' AND '
  408. end
  409. if selector['operator'] == 'is'
  410. if selector['pre_condition'] == 'not_set'
  411. if attributes[1] =~ /^(created_by|updated_by|owner|customer|user)_id/
  412. query += "#{attribute} IN (?)"
  413. bind_params.push 1
  414. else
  415. query += "#{attribute} IS NOT NULL"
  416. end
  417. elsif selector['pre_condition'] == 'current_user.id'
  418. raise "Use current_user.id in selector, but no current_user is set #{selector.inspect}" if !current_user_id
  419. query += "#{attribute} IN (?)"
  420. bind_params.push current_user_id
  421. elsif selector['pre_condition'] == 'current_user.organization_id'
  422. raise "Use current_user.id in selector, but no current_user is set #{selector.inspect}" if !current_user_id
  423. query += "#{attribute} IN (?)"
  424. user = User.lookup(id: current_user_id)
  425. bind_params.push user.organization_id
  426. else
  427. # rubocop:disable Style/IfInsideElse
  428. if selector['value'].nil?
  429. query += "#{attribute} IS NOT NULL"
  430. else
  431. query += "#{attribute} IN (?)"
  432. bind_params.push selector['value']
  433. end
  434. # rubocop:enable Style/IfInsideElse
  435. end
  436. elsif selector['operator'] == 'is not'
  437. if selector['pre_condition'] == 'not_set'
  438. if attributes[1] =~ /^(created_by|updated_by|owner|customer|user)_id/
  439. query += "#{attribute} NOT IN (?)"
  440. bind_params.push 1
  441. else
  442. query += "#{attribute} IS NULL"
  443. end
  444. elsif selector['pre_condition'] == 'current_user.id'
  445. query += "#{attribute} NOT IN (?)"
  446. bind_params.push current_user_id
  447. elsif selector['pre_condition'] == 'current_user.organization_id'
  448. query += "#{attribute} NOT IN (?)"
  449. user = User.lookup(id: current_user_id)
  450. bind_params.push user.organization_id
  451. else
  452. # rubocop:disable Style/IfInsideElse
  453. if selector['value'].nil?
  454. query += "#{attribute} IS NOT NULL"
  455. else
  456. query += "#{attribute} NOT IN (?)"
  457. bind_params.push selector['value']
  458. end
  459. # rubocop:enable Style/IfInsideElse
  460. end
  461. elsif selector['operator'] == 'contains'
  462. query += "#{attribute} #{like} (?)"
  463. value = "%#{selector['value']}%"
  464. bind_params.push value
  465. elsif selector['operator'] == 'contains not'
  466. query += "#{attribute} NOT #{like} (?)"
  467. value = "%#{selector['value']}%"
  468. bind_params.push value
  469. elsif selector['operator'] == 'before (absolute)'
  470. query += "#{attribute} <= ?"
  471. bind_params.push selector['value']
  472. elsif selector['operator'] == 'after (absolute)'
  473. query += "#{attribute} >= ?"
  474. bind_params.push selector['value']
  475. elsif selector['operator'] == 'within last (relative)'
  476. query += "#{attribute} >= ?"
  477. time = nil
  478. if selector['range'] == 'minute'
  479. time = Time.zone.now - selector['value'].to_i.minutes
  480. elsif selector['range'] == 'hour'
  481. time = Time.zone.now - selector['value'].to_i.hours
  482. elsif selector['range'] == 'day'
  483. time = Time.zone.now - selector['value'].to_i.days
  484. elsif selector['range'] == 'month'
  485. time = Time.zone.now - selector['value'].to_i.months
  486. elsif selector['range'] == 'year'
  487. time = Time.zone.now - selector['value'].to_i.years
  488. else
  489. raise "Unknown selector attributes '#{selector.inspect}'"
  490. end
  491. bind_params.push time
  492. elsif selector['operator'] == 'within next (relative)'
  493. query += "#{attribute} <= ?"
  494. time = nil
  495. if selector['range'] == 'minute'
  496. time = Time.zone.now + selector['value'].to_i.minutes
  497. elsif selector['range'] == 'hour'
  498. time = Time.zone.now + selector['value'].to_i.hours
  499. elsif selector['range'] == 'day'
  500. time = Time.zone.now + selector['value'].to_i.days
  501. elsif selector['range'] == 'month'
  502. time = Time.zone.now + selector['value'].to_i.months
  503. elsif selector['range'] == 'year'
  504. time = Time.zone.now + selector['value'].to_i.years
  505. else
  506. raise "Unknown selector attributes '#{selector.inspect}'"
  507. end
  508. bind_params.push time
  509. elsif selector['operator'] == 'before (relative)'
  510. query += "#{attribute} <= ?"
  511. time = nil
  512. if selector['range'] == 'minute'
  513. time = Time.zone.now - selector['value'].to_i.minutes
  514. elsif selector['range'] == 'hour'
  515. time = Time.zone.now - selector['value'].to_i.hours
  516. elsif selector['range'] == 'day'
  517. time = Time.zone.now - selector['value'].to_i.days
  518. elsif selector['range'] == 'month'
  519. time = Time.zone.now - selector['value'].to_i.months
  520. elsif selector['range'] == 'year'
  521. time = Time.zone.now - selector['value'].to_i.years
  522. else
  523. raise "Unknown selector attributes '#{selector.inspect}'"
  524. end
  525. bind_params.push time
  526. elsif selector['operator'] == 'after (relative)'
  527. query += "#{attribute} >= ?"
  528. time = nil
  529. if selector['range'] == 'minute'
  530. time = Time.zone.now + selector['value'].to_i.minutes
  531. elsif selector['range'] == 'hour'
  532. time = Time.zone.now + selector['value'].to_i.hours
  533. elsif selector['range'] == 'day'
  534. time = Time.zone.now + selector['value'].to_i.days
  535. elsif selector['range'] == 'month'
  536. time = Time.zone.now + selector['value'].to_i.months
  537. elsif selector['range'] == 'year'
  538. time = Time.zone.now + selector['value'].to_i.years
  539. else
  540. raise "Unknown selector attributes '#{selector.inspect}'"
  541. end
  542. bind_params.push time
  543. else
  544. raise "Invalid operator '#{selector['operator']}' for '#{selector['value'].inspect}'"
  545. end
  546. }
  547. [query, bind_params, tables]
  548. end
  549. =begin
  550. perform changes on ticket
  551. ticket.perform_changes({}, 'trigger', item)
  552. =end
  553. def perform_changes(perform, perform_origin, item = nil)
  554. logger.debug "Perform #{perform_origin} #{perform.inspect} on Ticket.find(#{id})"
  555. changed = false
  556. perform.each do |key, value|
  557. (object_name, attribute) = key.split('.', 2)
  558. raise "Unable to update object #{object_name}.#{attribute}, only can update tickets and send notifications!" if object_name != 'ticket' && object_name != 'notification'
  559. # send notification
  560. if object_name == 'notification'
  561. recipients = []
  562. if value['recipient'] == 'ticket_customer'
  563. recipients.push User.lookup(id: customer_id)
  564. elsif value['recipient'] == 'ticket_owner'
  565. recipients.push User.lookup(id: owner_id)
  566. elsif value['recipient'] == 'ticket_agents'
  567. recipients = recipients.concat(agent_of_group)
  568. else
  569. logger.error "Unknown email notification recipient '#{value['recipient']}'"
  570. next
  571. end
  572. recipient_string = ''
  573. recipient_already = {}
  574. recipients.each { |user|
  575. # send notifications only to email adresses
  576. next if !user.email
  577. next if user.email !~ /@/
  578. # do not sent notifications to this recipients
  579. send_no_auto_response_reg_exp = Setting.get('send_no_auto_response_reg_exp')
  580. begin
  581. next if user.email =~ /#{send_no_auto_response_reg_exp}/i
  582. rescue => e
  583. logger.error "ERROR: Invalid regex '#{send_no_auto_response_reg_exp}' in setting send_no_auto_response_reg_exp"
  584. logger.error 'ERROR: ' + e.inspect
  585. next if user.email =~ /(mailer-daemon|postmaster|abuse|root)@.+?\..+?/i
  586. end
  587. # check if notification should be send because of customer emails
  588. if item && item[:article_id]
  589. article = Ticket::Article.lookup(id: item[:article_id])
  590. if article && article.preferences['is-auto-response'] == true && article.from && article.from =~ /#{Regexp.quote(user.email)}/i
  591. logger.info "Send not trigger based notification to #{user.email} because of auto response tagged incoming email"
  592. next
  593. end
  594. end
  595. email = user.email.downcase.strip
  596. next if recipient_already[email]
  597. recipient_already[email] = true
  598. if recipient_string != ''
  599. recipient_string += ', '
  600. end
  601. recipient_string += email
  602. }
  603. next if recipient_string == ''
  604. group = self.group
  605. next if !group
  606. email_address = group.email_address
  607. next if !email_address
  608. next if !email_address.channel_id
  609. objects = {
  610. ticket: self,
  611. article: articles.last,
  612. #recipient: user,
  613. #changes: changes,
  614. }
  615. # get subject
  616. subject = NotificationFactory::Mailer.template(
  617. templateInline: value['subject'],
  618. locale: 'en-en',
  619. objects: objects,
  620. quote: false,
  621. )
  622. subject = subject_build(subject)
  623. body = NotificationFactory::Mailer.template(
  624. templateInline: value['body'],
  625. locale: 'en-en',
  626. objects: objects,
  627. quote: true,
  628. )
  629. Ticket::Article.create(
  630. ticket_id: id,
  631. to: recipient_string,
  632. subject: subject,
  633. content_type: 'text/html',
  634. body: body,
  635. internal: false,
  636. sender: Ticket::Article::Sender.find_by(name: 'System'),
  637. type: Ticket::Article::Type.find_by(name: 'email'),
  638. preferences: {
  639. perform_origin: perform_origin,
  640. },
  641. updated_by_id: 1,
  642. created_by_id: 1,
  643. )
  644. next
  645. end
  646. # update tags
  647. if key == 'ticket.tags'
  648. next if value['value'].empty?
  649. tags = value['value'].split(/,/)
  650. if value['operator'] == 'add'
  651. tags.each { |tag|
  652. Tag.tag_add(
  653. object: 'Ticket',
  654. o_id: id,
  655. item: tag,
  656. )
  657. }
  658. elsif value['operator'] == 'remove'
  659. tags.each { |tag|
  660. Tag.tag_remove(
  661. object: 'Ticket',
  662. o_id: id,
  663. item: tag,
  664. )
  665. }
  666. else
  667. logger.error "Unknown #{attribute} operator #{value['operator']}"
  668. end
  669. next
  670. end
  671. # update ticket
  672. next if self[attribute].to_s == value['value'].to_s
  673. changed = true
  674. self[attribute] = value['value']
  675. logger.debug "set #{object_name}.#{attribute} = #{value['value'].inspect}"
  676. end
  677. return if !changed
  678. save
  679. end
  680. =begin
  681. get all email references headers of a ticket, to exclude some, parse it as array into method
  682. references = ticket.get_references
  683. result
  684. ['message-id-1234', 'message-id-5678']
  685. ignore references header(s)
  686. references = ticket.get_references(['message-id-5678'])
  687. result
  688. ['message-id-1234']
  689. =end
  690. def get_references(ignore = [])
  691. references = []
  692. Ticket::Article.select('in_reply_to, message_id').where(ticket_id: id).each { |article|
  693. if !article.in_reply_to.empty?
  694. references.push article.in_reply_to
  695. end
  696. next if !article.message_id
  697. next if article.message_id.empty?
  698. references.push article.message_id
  699. }
  700. ignore.each { |item|
  701. references.delete(item)
  702. }
  703. references
  704. end
  705. =begin
  706. get all articles of a ticket in correct order (overwrite active record default method)
  707. artilces = ticket.articles
  708. result
  709. [article1, articl2]
  710. =end
  711. def articles
  712. Ticket::Article.where(ticket_id: id).order(:created_at, :id)
  713. end
  714. private
  715. def check_generate
  716. return if number
  717. self.number = Ticket::Number.generate
  718. end
  719. def check_title
  720. return if !title
  721. title.gsub!(/\s|\t|\r/, ' ')
  722. end
  723. def check_defaults
  724. if !owner_id
  725. self.owner_id = 1
  726. end
  727. return if !customer_id
  728. customer = User.find(customer_id)
  729. return if organization_id == customer.organization_id
  730. self.organization_id = customer.organization_id
  731. end
  732. def reset_pending_time
  733. # ignore if no state has changed
  734. return if !changes['state_id']
  735. # check if new state isn't pending*
  736. current_state = Ticket::State.lookup(id: state_id)
  737. current_state_type = Ticket::StateType.lookup(id: current_state.state_type_id)
  738. # in case, set pending_time to nil
  739. return if current_state_type.name =~ /^pending/i
  740. self.pending_time = nil
  741. end
  742. def check_escalation_update
  743. escalation_calculation_int
  744. true
  745. end
  746. def destroy_dependencies
  747. # delete articles
  748. articles.destroy_all
  749. # destroy online notifications
  750. OnlineNotification.remove(self.class.to_s, id)
  751. end
  752. end