ticket.rb 27 KB

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