application_model.rb 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147
  1. # Copyright (C) 2012-2014 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, :fill_up_user_update
  10. before_destroy :destroy_dependencies
  11. after_create :cache_delete
  12. after_update :cache_delete
  13. after_touch :cache_delete
  14. after_destroy :cache_delete
  15. after_create :attachments_buffer_check
  16. after_update :attachments_buffer_check
  17. after_create :activity_stream_create
  18. after_update :activity_stream_update
  19. before_destroy :activity_stream_destroy
  20. after_create :history_create
  21. after_update :history_update
  22. after_destroy :history_destroy
  23. after_create :search_index_update
  24. after_update :search_index_update
  25. after_destroy :search_index_destroy
  26. before_destroy :recent_view_destroy
  27. # create instance accessor
  28. class << self
  29. attr_accessor :activity_stream_support_config, :history_support_config, :search_index_support_config
  30. end
  31. attr_accessor :history_changes_last_done
  32. def check_attributes_protected
  33. import_class_list = ['Ticket', 'Ticket::Article', 'History', 'Ticket::State', 'Ticket::StateType', 'Ticket::Priority', 'Group', 'User', 'Role' ]
  34. # do noting, use id as it is
  35. return if !Setting.get('system_init_done')
  36. return if Setting.get('import_mode') && import_class_list.include?( self.class.to_s )
  37. self[:id] = nil
  38. end
  39. =begin
  40. remove all not used model attributes of params
  41. result = Model.param_cleanup(params)
  42. for object creation, ignore id's
  43. result = Model.param_cleanup(params, true)
  44. returns
  45. result = params # params with valid attributes of model
  46. =end
  47. def self.param_cleanup(params, newObject = false)
  48. if params.nil?
  49. fail "No params for #{self}!"
  50. end
  51. # ignore id for new objects
  52. if newObject && params[:id]
  53. params[:id] = nil
  54. end
  55. # only use object attributes
  56. data = {}
  57. new.attributes.each {|item|
  58. next if !params.key?(item[0])
  59. data[item[0].to_sym] = params[item[0]]
  60. }
  61. # we do want to set this via database
  62. param_validation(data)
  63. end
  64. =begin
  65. set rellations of model based on params
  66. model = Model.find(1)
  67. result = model.param_set_associations(params)
  68. returns
  69. result = true|false
  70. =end
  71. def param_set_associations(params)
  72. # set relations
  73. self.class.reflect_on_all_associations.map { |assoc|
  74. real_key = assoc.name.to_s[0, assoc.name.to_s.length - 1] + '_ids'
  75. next if !params.key?(real_key.to_sym)
  76. list_of_items = params[ real_key.to_sym ]
  77. if params[ real_key.to_sym ].class != Array
  78. list_of_items = [ params[ real_key.to_sym ] ]
  79. end
  80. list = []
  81. list_of_items.each {|item|
  82. list.push(assoc.klass.find(item))
  83. }
  84. send(assoc.name.to_s + '=', list)
  85. }
  86. end
  87. =begin
  88. get rellations of model based on params
  89. model = Model.find(1)
  90. attributes = model.attributes_with_associations
  91. returns
  92. hash with attributes and association ids
  93. =end
  94. def attributes_with_associations
  95. # set relations
  96. attributes = self.attributes
  97. self.class.reflect_on_all_associations.map { |assoc|
  98. real_key = assoc.name.to_s[0, assoc.name.to_s.length - 1] + '_ids'
  99. if self.respond_to?(real_key)
  100. attributes[ real_key ] = send(real_key)
  101. end
  102. }
  103. attributes
  104. end
  105. =begin
  106. remove all not used params of object (per default :updated_at, :created_at, :updated_by_id and :created_by_id)
  107. result = Model.param_validation(params)
  108. returns
  109. result = params # params without listed attributes
  110. =end
  111. def self.param_validation(data)
  112. # we do want to set this via database
  113. data.delete(:updated_at)
  114. data.delete(:created_at)
  115. data.delete(:updated_by_id)
  116. data.delete(:created_by_id)
  117. if data.respond_to?('permit!')
  118. data.permit!
  119. end
  120. data
  121. end
  122. =begin
  123. set created_by_id & updated_by_id if not given based on UserInfo (current session)
  124. Used as before_create callback, no own use needed
  125. result = Model.fill_up_user_create(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_create
  130. if self.class.column_names.include? 'updated_by_id'
  131. if UserInfo.current_user_id
  132. if updated_by_id && updated_by_id != UserInfo.current_user_id
  133. logger.info "NOTICE create - self.updated_by_id is different: #{updated_by_id}/#{UserInfo.current_user_id}"
  134. end
  135. self.updated_by_id = UserInfo.current_user_id
  136. end
  137. end
  138. return if !self.class.column_names.include? 'created_by_id'
  139. return if !UserInfo.current_user_id
  140. if created_by_id && created_by_id != UserInfo.current_user_id
  141. logger.info "NOTICE create - self.created_by_id is different: #{created_by_id}/#{UserInfo.current_user_id}"
  142. end
  143. self.created_by_id = UserInfo.current_user_id
  144. end
  145. =begin
  146. set updated_by_id if not given based on UserInfo (current session)
  147. Used as before_update callback, no own use needed
  148. result = Model.fill_up_user_update(params)
  149. returns
  150. result = params # params with updated_by_id & created_by_id if not given based on UserInfo (current session)
  151. =end
  152. def fill_up_user_update
  153. return if !self.class.column_names.include? 'updated_by_id'
  154. return if !UserInfo.current_user_id
  155. self.updated_by_id = UserInfo.current_user_id
  156. end
  157. def cache_update(o)
  158. cache_delete if self.respond_to?('cache_delete')
  159. o.cache_delete if o.respond_to?('cache_delete')
  160. end
  161. def cache_delete
  162. # delete id caches
  163. key = "#{self.class}::#{id}"
  164. Cache.delete(key)
  165. # delete old name / login caches
  166. if self.changed?
  167. if changes.key?('name')
  168. name = changes['name'][0]
  169. key = "#{self.class}::#{name}"
  170. Cache.delete(key.to_s)
  171. end
  172. if changes.key?('login')
  173. name = changes['login'][0]
  174. key = "#{self.class}::#{name}"
  175. Cache.delete(key)
  176. end
  177. end
  178. # delete name caches
  179. if self[:name]
  180. key = "#{self.class}::#{self.name}"
  181. Cache.delete(key)
  182. end
  183. # delete login caches
  184. return if !self[:login]
  185. Cache.delete("#{self.class}::#{login}")
  186. end
  187. def self.cache_set(data_id, data)
  188. key = "#{self}::#{data_id}"
  189. Cache.write(key, data)
  190. end
  191. def self.cache_get(data_id)
  192. key = "#{self}::#{data_id}"
  193. Cache.get(key)
  194. end
  195. =begin
  196. generate uniq name (will check name of model and generates _1 sequenze)
  197. Used as before_update callback, no own use needed
  198. name = Model.genrate_uniq_name('some name')
  199. returns
  200. result = 'some name_X'
  201. =end
  202. def self.genrate_uniq_name(name)
  203. return name if !find_by(name: name)
  204. (1..100).each {|counter|
  205. name = "#{name}_#{counter}"
  206. exists = find_by(name: name)
  207. next if exists
  208. break
  209. }
  210. name
  211. end
  212. =begin
  213. lookup model from cache (if exists) or retrieve it from db, id, name or login possible
  214. result = Model.lookup(id: 123)
  215. result = Model.lookup(name: 'some name')
  216. result = Model.lookup(login: 'some login')
  217. returns
  218. result = model # with all attributes
  219. =end
  220. def self.lookup(data)
  221. if data[:id]
  222. cache = cache_get(data[:id])
  223. return cache if cache
  224. record = find_by(id: data[:id])
  225. cache_set(data[:id], record)
  226. return record
  227. elsif data[:name]
  228. cache = cache_get(data[:name])
  229. return cache if cache
  230. # do lookup with == to handle case insensitive databases
  231. records = where(name: data[:name])
  232. records.each {|loop_record|
  233. if loop_record.name == data[:name]
  234. cache_set(data[:name], loop_record)
  235. return loop_record
  236. end
  237. }
  238. return
  239. elsif data[:login]
  240. cache = cache_get(data[:login])
  241. return cache if cache
  242. # do lookup with == to handle case insensitive databases
  243. records = where(login: data[:login])
  244. records.each {|loop_record|
  245. if loop_record.login == data[:login]
  246. cache_set( data[:login], loop_record)
  247. return loop_record
  248. end
  249. }
  250. return
  251. else
  252. fail 'Need name, id or login for lookup()'
  253. end
  254. end
  255. =begin
  256. create model if not exists (check exists based on id, name, login, email or locale)
  257. result = Model.create_if_not_exists(attributes)
  258. returns
  259. result = model # with all attributes
  260. =end
  261. def self.create_if_not_exists(data)
  262. if data[:id]
  263. record = find_by(id: data[:id])
  264. return record if record
  265. elsif data[:name]
  266. # do lookup with == to handle case insensitive databases
  267. records = where(name: data[:name])
  268. records.each {|loop_record|
  269. return loop_record if loop_record.name == data[:name]
  270. }
  271. elsif data[:login]
  272. # do lookup with == to handle case insensitive databases
  273. records = where(login: data[:login])
  274. records.each {|loop_record|
  275. return loop_record if loop_record.login == data[:login]
  276. }
  277. elsif data[:email]
  278. # do lookup with == to handle case insensitive databases
  279. records = where(email: data[:email])
  280. records.each {|loop_record|
  281. return loop_record if loop_record.email == data[:email]
  282. }
  283. elsif data[:locale] && data[:source]
  284. # do lookup with == to handle case insensitive databases
  285. records = where(locale: data[:locale], source: data[:source])
  286. records.each {|loop_record|
  287. return loop_record if loop_record.source == data[:source]
  288. }
  289. end
  290. create(data)
  291. end
  292. =begin
  293. create or update model (check exists based on id, name, login, email or locale)
  294. result = Model.create_or_update(attributes)
  295. returns
  296. result = model # with all attributes
  297. =end
  298. def self.create_or_update(data)
  299. if data[:id]
  300. record = find_by(id: data[:id])
  301. if record
  302. record.update_attributes(data)
  303. return record
  304. end
  305. record = new(data)
  306. record.save
  307. return record
  308. elsif data[:name]
  309. # do lookup with == to handle case insensitive databases
  310. records = where(name: data[:name])
  311. records.each {|loop_record|
  312. if loop_record.name == data[:name]
  313. loop_record.update_attributes(data)
  314. return loop_record
  315. end
  316. }
  317. record = new(data)
  318. record.save
  319. return record
  320. elsif data[:login]
  321. # do lookup with == to handle case insensitive databases
  322. records = where(login: data[:login])
  323. records.each {|loop_record|
  324. if loop_record.login.downcase == data[:login].downcase
  325. loop_record.update_attributes(data)
  326. return loop_record
  327. end
  328. }
  329. record = new(data)
  330. record.save
  331. return record
  332. elsif data[:email]
  333. # do lookup with == to handle case insensitive databases
  334. records = where(email: data[:email])
  335. records.each {|loop_record|
  336. if loop_record.email.downcase == data[:email].downcase
  337. loop_record.update_attributes(data)
  338. return loop_record
  339. end
  340. }
  341. record = new(data)
  342. record.save
  343. return record
  344. elsif data[:locale]
  345. # do lookup with == to handle case insensitive databases
  346. records = where(locale: data[:locale])
  347. records.each {|loop_record|
  348. if loop_record.locale.downcase == data[:locale].downcase
  349. loop_record.update_attributes(data)
  350. return loop_record
  351. end
  352. }
  353. record = new(data)
  354. record.save
  355. return record
  356. else
  357. fail 'Need name, login, email or locale for create_or_update()'
  358. end
  359. end
  360. =begin
  361. activate latest change on create, update, touch and destroy
  362. class Model < ApplicationModel
  363. latest_change_support
  364. end
  365. =end
  366. def self.latest_change_support
  367. after_create :latest_change_set_from_observer
  368. after_update :latest_change_set_from_observer
  369. after_touch :latest_change_set_from_observer
  370. after_destroy :latest_change_set_from_observer_destroy
  371. end
  372. def latest_change_set_from_observer
  373. self.class.latest_change_set(updated_at)
  374. end
  375. def latest_change_set_from_observer_destroy
  376. self.class.latest_change_set(nil)
  377. end
  378. def self.latest_change_set(updated_at)
  379. key = "#{new.class.name}_latest_change"
  380. expires_in = 31_536_000 # 1 year
  381. if updated_at.nil?
  382. Cache.delete(key)
  383. else
  384. Cache.write(key, updated_at, { expires_in: expires_in })
  385. end
  386. end
  387. =begin
  388. get latest updated_at object timestamp
  389. latest_change = Ticket.latest_change
  390. returns
  391. result = timestamp
  392. =end
  393. def self.latest_change
  394. key = "#{new.class.name}_latest_change"
  395. updated_at = Cache.get( key )
  396. # if we do not have it cached, do lookup
  397. if !updated_at
  398. o = select(:updated_at).order(updated_at: :desc).limit(1).first
  399. if o
  400. updated_at = o.updated_at
  401. latest_change_set(updated_at)
  402. end
  403. end
  404. updated_at
  405. end
  406. =begin
  407. activate client notify support on create, update, touch and destroy
  408. class Model < ApplicationModel
  409. notify_clients_support
  410. end
  411. =end
  412. def self.notify_clients_support
  413. after_create :notify_clients_after_create
  414. after_update :notify_clients_after_update
  415. after_touch :notify_clients_after_touch
  416. after_destroy :notify_clients_after_destroy
  417. end
  418. =begin
  419. notify_clients_after_create after model got created
  420. used as callback in model file
  421. class OwnModel < ApplicationModel
  422. after_create :notify_clients_after_create
  423. after_update :notify_clients_after_update
  424. after_touch :notify_clients_after_touch
  425. after_destroy :notify_clients_after_destroy
  426. [...]
  427. =end
  428. def notify_clients_after_create
  429. # return if we run import mode
  430. return if Setting.get('import_mode')
  431. logger.debug "#{self.class.name}.find(#{id}) notify created " + created_at.to_s
  432. class_name = self.class.name
  433. class_name.gsub!(/::/, '')
  434. Sessions.broadcast(
  435. event: class_name + ':create',
  436. data: { id: id, updated_at: updated_at }
  437. )
  438. end
  439. =begin
  440. notify_clients_after_update after model got updated
  441. used as callback in model file
  442. class OwnModel < ApplicationModel
  443. after_create :notify_clients_after_create
  444. after_update :notify_clients_after_update
  445. after_touch :notify_clients_after_touch
  446. after_destroy :notify_clients_after_destroy
  447. [...]
  448. =end
  449. def notify_clients_after_update
  450. # return if we run import mode
  451. return if Setting.get('import_mode')
  452. logger.debug "#{self.class.name}.find(#{id}) notify UPDATED " + updated_at.to_s
  453. class_name = self.class.name
  454. class_name.gsub!(/::/, '')
  455. Sessions.broadcast(
  456. event: class_name + ':update',
  457. data: { id: id, updated_at: updated_at }
  458. )
  459. end
  460. =begin
  461. notify_clients_after_touch after model got touched
  462. used as callback in model file
  463. class OwnModel < ApplicationModel
  464. after_create :notify_clients_after_create
  465. after_update :notify_clients_after_update
  466. after_touch :notify_clients_after_touch
  467. after_destroy :notify_clients_after_destroy
  468. [...]
  469. =end
  470. def notify_clients_after_touch
  471. # return if we run import mode
  472. return if Setting.get('import_mode')
  473. logger.debug "#{self.class.name}.find(#{id}) notify TOUCH " + updated_at.to_s
  474. class_name = self.class.name
  475. class_name.gsub!(/::/, '')
  476. Sessions.broadcast(
  477. event: class_name + ':touch',
  478. data: { id: id, updated_at: updated_at }
  479. )
  480. end
  481. =begin
  482. notify_clients_after_destroy after model got destroyed
  483. used as callback in model file
  484. class OwnModel < ApplicationModel
  485. after_create :notify_clients_after_create
  486. after_update :notify_clients_after_update
  487. after_touch :notify_clients_after_touch
  488. after_destroy :notify_clients_after_destroy
  489. [...]
  490. =end
  491. def notify_clients_after_destroy
  492. # return if we run import mode
  493. return if Setting.get('import_mode')
  494. logger.debug "#{self.class.name}.find(#{id}) notify DESTOY " + updated_at.to_s
  495. class_name = self.class.name
  496. class_name.gsub!(/::/, '')
  497. Sessions.broadcast(
  498. event: class_name + ':destroy',
  499. data: { id: id, updated_at: updated_at }
  500. )
  501. end
  502. =begin
  503. serve methode to configure and enable search index support for this model
  504. class Model < ApplicationModel
  505. search_index_support ignore_attributes: {
  506. create_article_type_id: true,
  507. create_article_sender_id: true,
  508. article_count: true,
  509. }
  510. end
  511. =end
  512. def self.search_index_support(data = {})
  513. @search_index_support_config = data
  514. end
  515. =begin
  516. update search index, if configured - will be executed automatically
  517. model = Model.find(123)
  518. model.search_index_update
  519. =end
  520. def search_index_update
  521. return if !self.class.search_index_support_config
  522. # start background job to transfer data to search index
  523. return if !SearchIndexBackend.enabled?
  524. Delayed::Job.enqueue( ApplicationModel::BackgroundJobSearchIndex.new( self.class.to_s, id ) )
  525. end
  526. =begin
  527. delete search index object, will be executed automatically
  528. model = Model.find(123)
  529. model.search_index_destroy
  530. =end
  531. def search_index_destroy
  532. return if !self.class.search_index_support_config
  533. SearchIndexBackend.remove(self.class.to_s, id)
  534. end
  535. =begin
  536. reload search index with full data
  537. Model.search_index_reload
  538. =end
  539. def self.search_index_reload
  540. return if !@search_index_support_config
  541. all_ids = select('id').all.order('created_at DESC')
  542. all_ids.each { |item_with_id|
  543. item = find( item_with_id.id )
  544. item.search_index_update_backend
  545. }
  546. end
  547. =begin
  548. serve methode to configure and enable activity stream support for this model
  549. class Model < ApplicationModel
  550. activity_stream_support role: 'Admin'
  551. end
  552. =end
  553. def self.activity_stream_support(data = {})
  554. @activity_stream_support_config = data
  555. end
  556. =begin
  557. log object create activity stream, if configured - will be executed automatically
  558. model = Model.find(123)
  559. model.activity_stream_create
  560. =end
  561. def activity_stream_create
  562. return if !self.class.activity_stream_support_config
  563. activity_stream_log('created', self['created_by_id'])
  564. end
  565. =begin
  566. log object update activity stream, if configured - will be executed automatically
  567. model = Model.find(123)
  568. model.activity_stream_update
  569. =end
  570. def activity_stream_update
  571. return if !self.class.activity_stream_support_config
  572. return if !self.changed?
  573. # default ignored attributes
  574. ignore_attributes = {
  575. created_at: true,
  576. updated_at: true,
  577. created_by_id: true,
  578. updated_by_id: true,
  579. }
  580. if self.class.activity_stream_support_config[:ignore_attributes]
  581. self.class.activity_stream_support_config[:ignore_attributes].each {|key, value|
  582. ignore_attributes[key] = value
  583. }
  584. end
  585. log = false
  586. changes.each {|key, _value|
  587. # do not log created_at and updated_at attributes
  588. next if ignore_attributes[key.to_sym] == true
  589. log = true
  590. }
  591. return if !log
  592. activity_stream_log('updated', self['updated_by_id'])
  593. end
  594. =begin
  595. delete object activity stream, will be executed automatically
  596. model = Model.find(123)
  597. model.activity_stream_destroy
  598. =end
  599. def activity_stream_destroy
  600. return if !self.class.activity_stream_support_config
  601. ActivityStream.remove(self.class.to_s, id)
  602. end
  603. =begin
  604. serve methode to configure and enable history support for this model
  605. class Model < ApplicationModel
  606. history_support
  607. end
  608. class Model < ApplicationModel
  609. history_support ignore_attributes: { article_count: true }
  610. end
  611. =end
  612. def self.history_support(data = {})
  613. @history_support_config = data
  614. end
  615. =begin
  616. log object create history, if configured - will be executed automatically
  617. model = Model.find(123)
  618. model.history_create
  619. =end
  620. def history_create
  621. return if !self.class.history_support_config
  622. #logger.debug 'create ' + self.changes.inspect
  623. history_log('created', created_by_id)
  624. end
  625. =begin
  626. log object update history with all updated attributes, if configured - will be executed automatically
  627. model = Model.find(123)
  628. model.history_update
  629. =end
  630. def history_update
  631. return if !self.class.history_support_config
  632. return if !self.changed?
  633. # return if it's no update
  634. return if self.new_record?
  635. # new record also triggers update, so ignore new records
  636. changes = self.changes
  637. if history_changes_last_done
  638. history_changes_last_done.each {|key, value|
  639. if changes.key?(key) && changes[key] == value
  640. changes.delete(key)
  641. end
  642. }
  643. end
  644. self.history_changes_last_done = changes
  645. #logger.info 'updated ' + self.changes.inspect
  646. return if changes['id'] && !changes['id'][0]
  647. # default ignored attributes
  648. ignore_attributes = {
  649. created_at: true,
  650. updated_at: true,
  651. created_by_id: true,
  652. updated_by_id: true,
  653. }
  654. if self.class.history_support_config[:ignore_attributes]
  655. self.class.history_support_config[:ignore_attributes].each {|key, value|
  656. ignore_attributes[key] = value
  657. }
  658. end
  659. changes.each {|key, value|
  660. # do not log created_at and updated_at attributes
  661. next if ignore_attributes[key.to_sym] == true
  662. # get attribute name
  663. attribute_name = key.to_s
  664. if attribute_name[-3, 3] == '_id'
  665. attribute_name = attribute_name[ 0, attribute_name.length - 3 ]
  666. end
  667. value_id = []
  668. value_str = [ value[0], value[1] ]
  669. if key.to_s[-3, 3] == '_id'
  670. value_id[0] = value[0]
  671. value_id[1] = value[1]
  672. if self.respond_to?( attribute_name ) && send(attribute_name)
  673. relation_class = send(attribute_name).class
  674. if relation_class && value_id[0]
  675. relation_model = relation_class.lookup( id: value_id[0] )
  676. if relation_model
  677. if relation_model['name']
  678. value_str[0] = relation_model['name']
  679. elsif relation_model.respond_to?('fullname')
  680. value_str[0] = relation_model.send('fullname')
  681. end
  682. end
  683. end
  684. if relation_class && value_id[1]
  685. relation_model = relation_class.lookup( id: value_id[1] )
  686. if relation_model
  687. if relation_model['name']
  688. value_str[1] = relation_model['name']
  689. elsif relation_model.respond_to?('fullname')
  690. value_str[1] = relation_model.send('fullname')
  691. end
  692. end
  693. end
  694. end
  695. end
  696. data = {
  697. history_attribute: attribute_name,
  698. value_from: value_str[0].to_s,
  699. value_to: value_str[1].to_s,
  700. id_from: value_id[0],
  701. id_to: value_id[1],
  702. }
  703. #logger.info "HIST NEW #{self.class.to_s}.find(#{self.id}) #{data.inspect}"
  704. history_log('updated', updated_by_id, data)
  705. }
  706. end
  707. =begin
  708. delete object history, will be executed automatically
  709. model = Model.find(123)
  710. model.history_destroy
  711. =end
  712. def history_destroy
  713. return if !self.class.history_support_config
  714. History.remove(self.class.to_s, id)
  715. end
  716. =begin
  717. get list of attachments of this object
  718. item = Model.find(123)
  719. list = item.attachments
  720. returns
  721. # array with Store model objects
  722. =end
  723. def attachments
  724. Store.list(object: self.class.to_s, o_id: id)
  725. end
  726. =begin
  727. store attachments for this object
  728. item = Model.find(123)
  729. item.attachments = [ Store-Object1, Store-Object2 ]
  730. =end
  731. def attachments=(attachments)
  732. self.attachments_buffer = attachments
  733. # update if object already exists
  734. return if !( id && id != 0 )
  735. attachments_buffer_check
  736. end
  737. =begin
  738. return object and assets
  739. data = Model.full(123)
  740. data = {
  741. id: 123,
  742. assets: assets,
  743. }
  744. =end
  745. def self.full(id)
  746. object = find(id)
  747. assets = object.assets({})
  748. {
  749. id: id,
  750. assets: assets,
  751. }
  752. end
  753. =begin
  754. get assets of object list
  755. list = [
  756. {
  757. object => 'Ticket',
  758. o_id => 1,
  759. },
  760. {
  761. object => 'User',
  762. o_id => 121,
  763. },
  764. ]
  765. assets = Model.assets_of_object_list(list, assets)
  766. =end
  767. def self.assets_of_object_list(list, assets = {})
  768. list.each {|item|
  769. require item['object'].to_filename
  770. record = Kernel.const_get(item['object']).find(item['o_id'])
  771. assets = record.assets(assets)
  772. if item['created_by_id']
  773. user = User.find(item['created_by_id'])
  774. assets = user.assets(assets)
  775. end
  776. if item['updated_by_id']
  777. user = User.find(item['updated_by_id'])
  778. assets = user.assets(assets)
  779. end
  780. }
  781. assets
  782. end
  783. private
  784. def attachments_buffer
  785. @attachments_buffer_data
  786. end
  787. def attachments_buffer=(attachments)
  788. @attachments_buffer_data = attachments
  789. end
  790. def attachments_buffer_check
  791. # do nothing if no attachment exists
  792. return 1 if attachments_buffer.nil?
  793. # store attachments
  794. article_store = []
  795. attachments_buffer.each do |attachment|
  796. article_store.push Store.add(
  797. object: self.class.to_s,
  798. o_id: id,
  799. data: attachment.content,
  800. filename: attachment.filename,
  801. preferences: attachment.preferences,
  802. created_by_id: created_by_id,
  803. )
  804. end
  805. attachments_buffer = nil
  806. end
  807. =begin
  808. delete object recent viewed list, will be executed automatically
  809. model = Model.find(123)
  810. model.recent_view_destroy
  811. =end
  812. def recent_view_destroy
  813. RecentView.log_destroy(self.class.to_s, id)
  814. end
  815. =begin
  816. check string/varchar size and cut them if needed
  817. =end
  818. def check_limits
  819. attributes.each {|attribute|
  820. next if !self[ attribute[0] ]
  821. next if self[ attribute[0] ].class != String
  822. next if self[ attribute[0] ].empty?
  823. column = self.class.columns_hash[ attribute[0] ]
  824. next if !column
  825. limit = column.limit
  826. if column && limit
  827. current_length = attribute[1].to_s.length
  828. if limit < current_length
  829. logger.warn "WARNING: cut string because of database length #{self.class}.#{attribute[0]}(#{limit} but is #{current_length}:#{attribute[1]})"
  830. self[ attribute[0] ] = attribute[1][ 0, limit ]
  831. end
  832. end
  833. # strip 4 bytes utf8 chars if needed
  834. if column && self[ attribute[0] ]
  835. self[attribute[0]] = self[ attribute[0] ].utf8_to_3bytesutf8
  836. end
  837. }
  838. end
  839. =begin
  840. destory object dependencies, will be executed automatically
  841. =end
  842. def destroy_dependencies
  843. end
  844. end