application_model.rb 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786
  1. # Copyright (C) 2013-2013 Zammad Foundation, http://zammad-foundation.org/
  2. class ApplicationModel < ActiveRecord::Base
  3. include ApplicationModel::Assets
  4. include ApplicationModel::HistoryLogBase
  5. include ApplicationModel::ActivityStreamBase
  6. include ApplicationModel::SearchIndexBase
  7. self.abstract_class = true
  8. before_create :check_attributes_protected, :check_limits, :cache_delete, :fill_up_user_create
  9. before_update :check_limits, :cache_delete_before, :fill_up_user_update
  10. before_destroy :cache_delete_before, :destroy_dependencies
  11. after_create :cache_delete
  12. after_update :cache_delete
  13. after_destroy :cache_delete
  14. after_create :activity_stream_create
  15. after_update :activity_stream_update
  16. after_destroy :activity_stream_destroy
  17. after_create :history_create
  18. after_update :history_update
  19. after_destroy :history_destroy
  20. after_create :search_index_update
  21. after_update :search_index_update
  22. after_destroy :search_index_destroy
  23. # create instance accessor
  24. class << self
  25. attr_accessor :activity_stream_support_config, :history_support_config, :search_index_support_config
  26. end
  27. attr_accessor :history_changes_last_done
  28. @@import_class_list = ['Ticket', 'Ticket::Article', 'History', 'Ticket::State', 'Ticket::Priority', 'Group', 'User' ]
  29. def check_attributes_protected
  30. if Setting.get('import_mode') && @@import_class_list.include?( self.name.to_s )
  31. # do noting, use id as it is
  32. else
  33. self[:id] = nil
  34. end
  35. end
  36. =begin
  37. remove all not used model attributes of params
  38. result = Model.param_cleanup(params)
  39. returns
  40. result = params # params with valid attributes of model
  41. =end
  42. def self.param_cleanup(params)
  43. if params == nil
  44. raise "No params for #{self.to_s}!"
  45. end
  46. # only use object attributes
  47. data = {}
  48. self.new.attributes.each {|item|
  49. if params.has_key?(item[0])
  50. # puts 'use ' + item[0].to_s + '-' + params[item[0]].to_s
  51. data[item[0].to_sym] = params[item[0]]
  52. end
  53. }
  54. # we do want to set this via database
  55. self.param_validation(data)
  56. end
  57. =begin
  58. set rellations of model based on params
  59. result = Model.param_set_associations(params)
  60. returns
  61. result = true|false
  62. =end
  63. def param_set_associations(params)
  64. # set relations
  65. self.class.reflect_on_all_associations.map { |assoc|
  66. real_key = assoc.name.to_s[0,assoc.name.to_s.length-1] + '_ids'
  67. if params.has_key?( real_key.to_sym )
  68. list_of_items = params[ real_key.to_sym ]
  69. if params[ real_key.to_sym ].class != Array
  70. list_of_items = [ params[ real_key.to_sym ] ]
  71. end
  72. list = []
  73. list_of_items.each {|item|
  74. list.push( assoc.klass.find(item) )
  75. }
  76. self.send( assoc.name.to_s + '=', list )
  77. end
  78. }
  79. end
  80. =begin
  81. remove all not used params of object (per default :updated_at, :created_at, :updated_by_id and :created_by_id)
  82. result = Model.param_validation(params)
  83. returns
  84. result = params # params without listed attributes
  85. =end
  86. def self.param_validation(data)
  87. # we do want to set this via database
  88. data.delete( :updated_at )
  89. data.delete( :created_at )
  90. data.delete( :updated_by_id )
  91. data.delete( :created_by_id )
  92. if data.respond_to?('permit!')
  93. data.permit!
  94. end
  95. data
  96. end
  97. =begin
  98. set created_by_id & updated_by_id if not given based on UserInfo (current session)
  99. Used as before_create callback, no own use needed
  100. result = Model.fill_up_user_create(params)
  101. returns
  102. result = params # params with updated_by_id & created_by_id if not given based on UserInfo (current session)
  103. =end
  104. def fill_up_user_create
  105. if self.class.column_names.include? 'updated_by_id'
  106. if UserInfo.current_user_id
  107. if self.updated_by_id && self.updated_by_id != UserInfo.current_user_id
  108. puts "NOTICE create - self.updated_by_id is different: #{self.updated_by_id.to_s}/#{UserInfo.current_user_id.to_s}"
  109. end
  110. self.updated_by_id = UserInfo.current_user_id
  111. end
  112. end
  113. if self.class.column_names.include? 'created_by_id'
  114. if UserInfo.current_user_id
  115. if self.created_by_id && self.created_by_id != UserInfo.current_user_id
  116. puts "NOTICE create - self.created_by_id is different: #{self.created_by_id.to_s}/#{UserInfo.current_user_id.to_s}"
  117. end
  118. self.created_by_id = UserInfo.current_user_id
  119. end
  120. end
  121. end
  122. =begin
  123. set updated_by_id if not given based on UserInfo (current session)
  124. Used as before_update callback, no own use needed
  125. result = Model.fill_up_user_update(params)
  126. returns
  127. result = params # params with updated_by_id & created_by_id if not given based on UserInfo (current session)
  128. =end
  129. def fill_up_user_update
  130. return if !self.class.column_names.include? 'updated_by_id'
  131. if UserInfo.current_user_id
  132. self.updated_by_id = UserInfo.current_user_id
  133. end
  134. end
  135. def cache_update(o)
  136. # puts 'u ' + self.class.to_s
  137. if self.respond_to?('cache_delete') then self.cache_delete end
  138. # puts 'g ' + group.class.to_s
  139. if o.respond_to?('cache_delete') then o.cache_delete end
  140. end
  141. def cache_delete_before
  142. old_object = self.class.where( :id => self.id ).first
  143. if old_object
  144. old_object.cache_delete
  145. end
  146. self.cache_delete
  147. end
  148. def cache_delete
  149. key = self.class.to_s + '::' + self.id.to_s
  150. Cache.delete( key.to_s )
  151. key = self.class.to_s + ':f:' + self.id.to_s
  152. Cache.delete( key.to_s )
  153. if self[:name]
  154. key = self.class.to_s + '::' + self.name.to_s
  155. Cache.delete( key.to_s )
  156. key = self.class.to_s + ':f:' + self.name.to_s
  157. Cache.delete( key.to_s )
  158. end
  159. if self[:login]
  160. key = self.class.to_s + '::' + self.login.to_s
  161. Cache.delete( key.to_s )
  162. key = self.class.to_s + ':f:' + self.login.to_s
  163. Cache.delete( key.to_s )
  164. end
  165. end
  166. def self.cache_set(data_id, data, full = false)
  167. if !full
  168. key = self.to_s + '::' + data_id.to_s
  169. else
  170. key = self.to_s + ':f:' + data_id.to_s
  171. end
  172. Cache.write( key.to_s, data )
  173. end
  174. def self.cache_get(data_id, full = false)
  175. if !full
  176. key = self.to_s + '::' + data_id.to_s
  177. else
  178. key = self.to_s + ':f:' + data_id.to_s
  179. end
  180. Cache.get( key.to_s )
  181. end
  182. =begin
  183. lookup model from cache (if exists) or retrieve it from db, id, name or login possible
  184. result = Model.lookup( :id => 123 )
  185. result = Model.lookup( :name => 'some name' )
  186. result = Model.lookup( :login => 'some login' )
  187. returns
  188. result = model # with all attributes
  189. =end
  190. def self.lookup(data)
  191. if data[:id]
  192. # puts "GET- + #{self.to_s}.#{data[:id].to_s}"
  193. cache = self.cache_get( data[:id] )
  194. return cache if cache
  195. # puts "Fillup- + #{self.to_s}.#{data[:id].to_s}"
  196. record = self.where( :id => data[:id] ).first
  197. self.cache_set( data[:id], record )
  198. return record
  199. elsif data[:name]
  200. cache = self.cache_get( data[:name] )
  201. return cache if cache
  202. records = self.where( :name => data[:name] )
  203. records.each {|record|
  204. if record.name == data[:name]
  205. self.cache_set( data[:name], record )
  206. return record
  207. end
  208. }
  209. return
  210. elsif data[:login]
  211. cache = self.cache_get( data[:login] )
  212. return cache if cache
  213. records = self.where( :login => data[:login] )
  214. records.each {|record|
  215. if record.login == data[:login]
  216. self.cache_set( data[:login], record )
  217. return record
  218. end
  219. }
  220. return
  221. else
  222. raise "Need name, id or login for lookup()"
  223. end
  224. end
  225. =begin
  226. create model if not exists (check exists based on id, name, login or locale)
  227. result = Model.create_if_not_exists( attributes )
  228. returns
  229. result = model # with all attributes
  230. =end
  231. def self.create_if_not_exists(data)
  232. if data[:id]
  233. record = self.where( :id => data[:id] ).first
  234. return record if record
  235. elsif data[:name]
  236. records = self.where( :name => data[:name] )
  237. records.each {|record|
  238. return record if record.name == data[:name]
  239. }
  240. elsif data[:login]
  241. records = self.where( :login => data[:login] )
  242. records.each {|record|
  243. return record if record.login == data[:login]
  244. }
  245. elsif data[:locale] && data[:source]
  246. records = self.where( :locale => data[:locale], :source => data[:source] )
  247. records.each {|record|
  248. return record if record.source == data[:source]
  249. }
  250. end
  251. self.create(data)
  252. end
  253. =begin
  254. create or update model (check exists based on name, login or locale)
  255. result = Model.create_or_update( attributes )
  256. returns
  257. result = model # with all attributes
  258. =end
  259. def self.create_or_update(data)
  260. if data[:name]
  261. records = self.where( :name => data[:name] )
  262. records.each {|record|
  263. if record.name == data[:name]
  264. record.update_attributes( data )
  265. return record
  266. end
  267. }
  268. record = self.new( data )
  269. record.save
  270. return record
  271. elsif data[:login]
  272. records = self.where( :login => data[:login] )
  273. records.each {|record|
  274. if record.login.downcase == data[:login].downcase
  275. record.update_attributes( data )
  276. return record
  277. end
  278. }
  279. record = self.new( data )
  280. record.save
  281. return record
  282. elsif data[:locale]
  283. records = self.where( :locale => data[:locale] )
  284. records.each {|record|
  285. if record.locale.downcase == data[:locale].downcase
  286. record.update_attributes( data )
  287. return record
  288. end
  289. }
  290. record = self.new( data )
  291. record.save
  292. return record
  293. else
  294. raise "Need name, login or locale for create_or_update()"
  295. end
  296. end
  297. =begin
  298. notify_clients_after_create after model got created
  299. used as callback in model file
  300. class OwnModel < ApplicationModel
  301. after_create :notify_clients_after_create
  302. after_update :notify_clients_after_update
  303. after_destroy :notify_clients_after_destroy
  304. [...]
  305. =end
  306. def notify_clients_after_create
  307. # return if we run import mode
  308. return if Setting.get('import_mode')
  309. class_name = self.class.name
  310. class_name.gsub!(/::/, '')
  311. Sessions.broadcast(
  312. :event => class_name + ':created',
  313. :data => { :id => self.id, :updated_at => self.updated_at }
  314. )
  315. end
  316. =begin
  317. notify_clients_after_update after model got updated
  318. used as callback in model file
  319. class OwnModel < ApplicationModel
  320. after_create :notify_clients_after_create
  321. after_update :notify_clients_after_update
  322. after_destroy :notify_clients_after_destroy
  323. [...]
  324. =end
  325. def notify_clients_after_update
  326. # return if we run import mode
  327. return if Setting.get('import_mode')
  328. puts "#{self.class.name.downcase} UPDATED " + self.updated_at.to_s
  329. class_name = self.class.name
  330. class_name.gsub!(/::/, '')
  331. Sessions.broadcast(
  332. :event => class_name + ':updated',
  333. :data => { :id => self.id, :updated_at => self.updated_at }
  334. )
  335. end
  336. =begin
  337. notify_clients_after_destroy after model got destroyed
  338. used as callback in model file
  339. class OwnModel < ApplicationModel
  340. after_create :notify_clients_after_create
  341. after_update :notify_clients_after_update
  342. after_destroy :notify_clients_after_destroy
  343. [...]
  344. =end
  345. def notify_clients_after_destroy
  346. # return if we run import mode
  347. return if Setting.get('import_mode')
  348. puts "#{self.class.name.downcase} DESTOY " + self.updated_at.to_s
  349. class_name = self.class.name
  350. class_name.gsub!(/::/, '')
  351. Sessions.broadcast(
  352. :event => class_name + ':destroy',
  353. :data => { :id => self.id, :updated_at => self.updated_at }
  354. )
  355. end
  356. =begin
  357. serve methode to configure and enable search index support for this model
  358. class Model < ApplicationModel
  359. search_index_support :ignore_attributes => {
  360. :create_article_type_id => true,
  361. :create_article_sender_id => true,
  362. :article_count => true,
  363. }
  364. end
  365. =end
  366. def self.search_index_support(data = {})
  367. @search_index_support_config = data
  368. end
  369. =begin
  370. update search index, if configured - will be executed automatically
  371. model = Model.find(123)
  372. model.search_index_update
  373. =end
  374. def search_index_update
  375. return if !self.class.search_index_support_config
  376. search_index_update_backend
  377. end
  378. =begin
  379. delete search index object, will be executed automatically
  380. model = Model.find(123)
  381. model.search_index_destroy
  382. =end
  383. def search_index_destroy
  384. return if !self.class.search_index_support_config
  385. SearchIndexBackend.remove( self.class.to_s, self.id )
  386. end
  387. =begin
  388. reload search index with full data
  389. Model.search_index_reload
  390. =end
  391. def self.search_index_reload
  392. return if !@search_index_support_config
  393. self.all.order('created_at DESC').each { |item|
  394. item.search_index_update_backend
  395. }
  396. end
  397. =begin
  398. serve methode to configure and enable activity stream support for this model
  399. class Model < ApplicationModel
  400. activity_stream_support :role => 'Admin'
  401. end
  402. =end
  403. def self.activity_stream_support(data = {})
  404. @activity_stream_support_config = data
  405. end
  406. =begin
  407. log object create activity stream, if configured - will be executed automatically
  408. model = Model.find(123)
  409. model.activity_stream_create
  410. =end
  411. def activity_stream_create
  412. return if !self.class.activity_stream_support_config
  413. activity_stream_log( 'created', self['created_by_id'] )
  414. end
  415. =begin
  416. log object update activity stream, if configured - will be executed automatically
  417. model = Model.find(123)
  418. model.activity_stream_update
  419. =end
  420. def activity_stream_update
  421. return if !self.class.activity_stream_support_config
  422. return if !self.changed?
  423. # default ignored attributes
  424. ignore_attributes = {
  425. :created_at => true,
  426. :updated_at => true,
  427. :created_by_id => true,
  428. :updated_by_id => true,
  429. }
  430. if self.class.activity_stream_support_config[:ignore_attributes]
  431. self.class.activity_stream_support_config[:ignore_attributes].each {|key, value|
  432. ignore_attributes[key] = value
  433. }
  434. end
  435. log = false
  436. self.changes.each {|key, value|
  437. # do not log created_at and updated_at attributes
  438. next if ignore_attributes[key.to_sym] == true
  439. log = true
  440. }
  441. return if !log
  442. activity_stream_log( 'updated', self['updated_by_id'] )
  443. end
  444. =begin
  445. delete object activity stream, will be executed automatically
  446. model = Model.find(123)
  447. model.activity_stream_destroy
  448. =end
  449. def activity_stream_destroy
  450. return if !self.class.activity_stream_support_config
  451. ActivityStream.remove( self.class.to_s, self.id )
  452. end
  453. =begin
  454. serve methode to configure and enable history support for this model
  455. class Model < ApplicationModel
  456. history_support
  457. end
  458. class Model < ApplicationModel
  459. history_support :ignore_attributes => { :article_count => true }
  460. end
  461. =end
  462. def self.history_support(data = {})
  463. @history_support_config = data
  464. end
  465. =begin
  466. log object create history, if configured - will be executed automatically
  467. model = Model.find(123)
  468. model.history_create
  469. =end
  470. def history_create
  471. return if !self.class.history_support_config
  472. #puts 'create ' + self.changes.inspect
  473. self.history_log( 'created', self.created_by_id )
  474. end
  475. =begin
  476. log object update history with all updated attributes, if configured - will be executed automatically
  477. model = Model.find(123)
  478. model.history_update
  479. =end
  480. def history_update
  481. return if !self.class.history_support_config
  482. return if !self.changed?
  483. # return if it's no update
  484. return if self.new_record?
  485. # new record also triggers update, so ignore new records
  486. changes = self.changes
  487. if self.history_changes_last_done
  488. self.history_changes_last_done.each {|key, value|
  489. if changes.has_key?(key) && changes[key] == value
  490. changes.delete(key)
  491. end
  492. }
  493. end
  494. self.history_changes_last_done = changes
  495. #puts 'updated ' + self.changes.inspect
  496. return if changes['id'] && !changes['id'][0]
  497. # default ignored attributes
  498. ignore_attributes = {
  499. :created_at => true,
  500. :updated_at => true,
  501. :created_by_id => true,
  502. :updated_by_id => true,
  503. }
  504. if self.class.history_support_config[:ignore_attributes]
  505. self.class.history_support_config[:ignore_attributes].each {|key, value|
  506. ignore_attributes[key] = value
  507. }
  508. end
  509. changes.each {|key, value|
  510. # do not log created_at and updated_at attributes
  511. next if ignore_attributes[key.to_sym] == true
  512. # get attribute name
  513. attribute_name = key.to_s
  514. if attribute_name[-3,3] == '_id'
  515. attribute_name = attribute_name[ 0, attribute_name.length-3 ]
  516. end
  517. value_id = []
  518. value_str = [ value[0], value[1] ]
  519. if key.to_s[-3,3] == '_id'
  520. value_id[0] = value[0]
  521. value_id[1] = value[1]
  522. if self.respond_to?( attribute_name )
  523. relation_class = self.send(attribute_name).class
  524. if relation_class && value_id[0]
  525. relation_model = relation_class.lookup( :id => value_id[0] )
  526. if relation_model
  527. if relation_model['name']
  528. value_str[0] = relation_model['name']
  529. elsif relation_model.respond_to?('fullname')
  530. value_str[0] = relation_model.send('fullname')
  531. end
  532. end
  533. end
  534. if relation_class && value_id[1]
  535. relation_model = relation_class.lookup( :id => value_id[1] )
  536. if relation_model
  537. if relation_model['name']
  538. value_str[1] = relation_model['name']
  539. elsif relation_model.respond_to?('fullname')
  540. value_str[1] = relation_model.send('fullname')
  541. end
  542. end
  543. end
  544. end
  545. end
  546. data = {
  547. :history_attribute => attribute_name,
  548. :value_from => value_str[0].to_s,
  549. :value_to => value_str[1].to_s,
  550. :id_from => value_id[0],
  551. :id_to => value_id[1],
  552. }
  553. #puts "HIST NEW #{self.class.to_s}.find(#{self.id}) #{data.inspect}"
  554. self.history_log( 'updated', self.updated_by_id, data )
  555. }
  556. end
  557. =begin
  558. delete object history, will be executed automatically
  559. model = Model.find(123)
  560. model.history_destroy
  561. =end
  562. def history_destroy
  563. return if !self.class.history_support_config
  564. History.remove( self.class.to_s, self.id )
  565. end
  566. private
  567. =begin
  568. check string/varchar size and cut them if needed
  569. =end
  570. def check_limits
  571. self.attributes.each {|attribute|
  572. next if !self[ attribute[0] ]
  573. next if self[ attribute[0] ].class != String
  574. next if self[ attribute[0] ].empty?
  575. column = self.class.columns_hash[ attribute[0] ]
  576. limit = column.limit
  577. if column && limit
  578. current_length = attribute[1].to_s.length
  579. if limit < current_length
  580. puts "WARNING: cut string because of database length #{self.class.to_s}.#{attribute[0]}(#{limit} but is #{current_length}:#{attribute[1].to_s})"
  581. self[attribute[0]] = attribute[1][ 0, limit ]
  582. end
  583. end
  584. }
  585. end
  586. =begin
  587. destory object dependencies, will be executed automatically
  588. =end
  589. def destroy_dependencies
  590. end
  591. end