application_model.rb 26 KB

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