ticket.rb 27 KB

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