ticket.rb 29 KB

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