application_model.rb 33 KB

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