application_model.rb 30 KB

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