avatar.rb 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. # Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
  2. class Avatar < ApplicationModel
  3. belongs_to :object_lookup, class_name: 'ObjectLookup'
  4. =begin
  5. add an avatar based on auto detection (email address)
  6. Avatar.auto_detection(
  7. object: 'User',
  8. o_id: user.id,
  9. url: 'somebody@example.com',
  10. updated_by_id: 1,
  11. created_by_id: 1,
  12. )
  13. =end
  14. def self.auto_detection(data)
  15. # return if we run import mode
  16. return if Setting.get('import_mode')
  17. return if data[:url].blank?
  18. Avatar.add(
  19. object: data[:object],
  20. o_id: data[:o_id],
  21. url: data[:url],
  22. source: 'zammad.com',
  23. deletable: false,
  24. updated_by_id: 1,
  25. created_by_id: 1,
  26. )
  27. end
  28. =begin
  29. add avatar by upload
  30. Avatar.add(
  31. object: 'User',
  32. o_id: user.id,
  33. default: true,
  34. full: {
  35. content: '...',
  36. mime_type: 'image/png',
  37. },
  38. resize: {
  39. content: '...',
  40. mime_type: 'image/png',
  41. },
  42. source: 'web',
  43. deletable: true,
  44. updated_by_id: 1,
  45. created_by_id: 1,
  46. )
  47. add avatar by url
  48. Avatar.add(
  49. object: 'User',
  50. o_id: user.id,
  51. default: true,
  52. url: ...,
  53. source: 'web',
  54. deletable: true,
  55. updated_by_id: 1,
  56. created_by_id: 1,
  57. )
  58. =end
  59. def self.add(data)
  60. # lookups
  61. if data[:object]
  62. object_id = ObjectLookup.by_name(data[:object])
  63. end
  64. # add initial avatar
  65. add_init_avatar(object_id, data[:o_id])
  66. record = {
  67. o_id: data[:o_id],
  68. object_lookup_id: object_id,
  69. default: true,
  70. deletable: data[:deletable],
  71. initial: false,
  72. source: data[:source],
  73. source_url: data[:url],
  74. updated_by_id: data[:updated_by_id],
  75. created_by_id: data[:created_by_id],
  76. }
  77. # check if avatar with url already exists
  78. avatar_already_exists = nil
  79. if data[:source].present?
  80. avatar_already_exists = Avatar.find_by(
  81. object_lookup_id: object_id,
  82. o_id: data[:o_id],
  83. source: data[:source],
  84. )
  85. end
  86. # fetch image based on http url
  87. if data[:url].present?
  88. if data[:url] =~ /^http/
  89. # check if source ist already updated within last 2 minutes
  90. if avatar_already_exists && avatar_already_exists.source_url == data[:url]
  91. return if avatar_already_exists.updated_at > 2.minutes.ago
  92. end
  93. # twitter workaround to get bigger avatar images
  94. # see also https://dev.twitter.com/overview/general/user-profile-images-and-banners
  95. if data[:url] =~ %r{//pbs.twimg.com/}i
  96. data[:url].sub!(/normal\.(png|jpg|gif)$/, 'bigger.\1')
  97. end
  98. # fetch image
  99. response = UserAgent.get(
  100. data[:url],
  101. {},
  102. {
  103. open_timeout: 4,
  104. read_timeout: 6,
  105. total_timeout: 6,
  106. },
  107. )
  108. if !response.success?
  109. logger.info "Can't fetch '#{data[:url]}' (maybe no avatar available), http code: #{response.code}"
  110. return
  111. end
  112. logger.info "Fetchd image '#{data[:url]}', http code: #{response.code}"
  113. mime_type = 'image'
  114. if data[:url] =~ /\.png/i
  115. mime_type = 'image/png'
  116. end
  117. if data[:url] =~ /\.(jpg|jpeg)/i
  118. mime_type = 'image/jpeg'
  119. end
  120. if !data[:resize]
  121. data[:resize] = {}
  122. end
  123. data[:resize][:content] = response.body
  124. data[:resize][:mime_type] = mime_type
  125. data[:full] ||= {}
  126. data[:full][:content] = response.body
  127. data[:full][:mime_type] = mime_type
  128. # try zammad backend to find image based on email
  129. elsif data[:url] =~ /@/
  130. # check if source ist already updated within last 3 minutes
  131. if avatar_already_exists && avatar_already_exists.source_url == data[:url]
  132. return if avatar_already_exists.updated_at > 2.minutes.ago
  133. end
  134. # fetch image
  135. image = Service::Image.user(data[:url])
  136. return if !image
  137. data[:resize] ||= {}
  138. data[:resize] = image
  139. data[:full] ||= {}
  140. data[:full] = image
  141. end
  142. end
  143. # check if avatar need to be updated
  144. if data[:resize].present? && data[:resize][:content].present?
  145. record[:store_hash] = Digest::MD5.hexdigest(data[:resize][:content])
  146. if avatar_already_exists && avatar_already_exists.store_hash == record[:store_hash]
  147. avatar_already_exists.touch
  148. return avatar_already_exists
  149. end
  150. end
  151. # store images
  152. object_name = "Avatar::#{data[:object]}"
  153. if data[:full].present?
  154. store_full = Store.add(
  155. object: "#{object_name}::Full",
  156. o_id: data[:o_id],
  157. data: data[:full][:content],
  158. filename: 'avatar_full',
  159. preferences: {
  160. 'Mime-Type' => data[:full][:mime_type]
  161. },
  162. created_by_id: data[:created_by_id],
  163. )
  164. record[:store_full_id] = store_full.id
  165. record[:store_hash] = Digest::MD5.hexdigest(data[:full][:content])
  166. end
  167. if data[:resize].present?
  168. store_resize = Store.add(
  169. object: "#{object_name}::Resize",
  170. o_id: data[:o_id],
  171. data: data[:resize][:content],
  172. filename: 'avatar',
  173. preferences: {
  174. 'Mime-Type' => data[:resize][:mime_type]
  175. },
  176. created_by_id: data[:created_by_id],
  177. )
  178. record[:store_resize_id] = store_resize.id
  179. record[:store_hash] = Digest::MD5.hexdigest(data[:resize][:content])
  180. end
  181. return if record[:store_resize_id].blank? || record[:store_hash].blank?
  182. # update existing
  183. if avatar_already_exists
  184. avatar_already_exists.update!(record)
  185. avatar = avatar_already_exists
  186. # add new one and set it as default
  187. else
  188. avatar = Avatar.create(record)
  189. set_default_items(object_id, data[:o_id], avatar.id)
  190. end
  191. avatar
  192. end
  193. =begin
  194. set avatars as default
  195. Avatar.set_default('User', 123, avatar_id)
  196. =end
  197. def self.set_default(object_name, o_id, avatar_id)
  198. object_id = ObjectLookup.by_name(object_name)
  199. avatar = Avatar.find_by(
  200. object_lookup_id: object_id,
  201. o_id: o_id,
  202. id: avatar_id,
  203. )
  204. avatar.default = true
  205. avatar.save!
  206. # set all other to default false
  207. set_default_items(object_id, o_id, avatar_id)
  208. avatar
  209. end
  210. =begin
  211. remove all avatars of an object
  212. Avatar.remove('User', 123)
  213. =end
  214. def self.remove(object_name, o_id)
  215. object_id = ObjectLookup.by_name(object_name)
  216. Avatar.where(
  217. object_lookup_id: object_id,
  218. o_id: o_id,
  219. ).destroy_all
  220. object_name_store = "Avatar::#{object_name}"
  221. Store.remove(
  222. object: "#{object_name_store}::Full",
  223. o_id: o_id,
  224. )
  225. Store.remove(
  226. object: "#{object_name_store}::Resize",
  227. o_id: o_id,
  228. )
  229. end
  230. =begin
  231. remove one avatars of an object
  232. Avatar.remove_one('User', 123, avatar_id)
  233. =end
  234. def self.remove_one(object_name, o_id, avatar_id)
  235. object_id = ObjectLookup.by_name(object_name)
  236. Avatar.where(
  237. object_lookup_id: object_id,
  238. o_id: o_id,
  239. id: avatar_id,
  240. ).destroy_all
  241. end
  242. =begin
  243. return all avatars of an user
  244. avatars = Avatar.list('User', 123)
  245. =end
  246. def self.list(object_name, o_id)
  247. object_id = ObjectLookup.by_name(object_name)
  248. avatars = Avatar.where(
  249. object_lookup_id: object_id,
  250. o_id: o_id,
  251. ).order('initial DESC, deletable ASC, created_at ASC, id DESC')
  252. # add initial avatar
  253. add_init_avatar(object_id, o_id)
  254. avatar_list = []
  255. avatars.each do |avatar|
  256. data = avatar.attributes
  257. if avatar.store_resize_id
  258. file = Store.find(avatar.store_resize_id)
  259. data['content'] = "data:#{file.preferences['Mime-Type']};base64,#{Base64.strict_encode64(file.content)}"
  260. end
  261. avatar_list.push data
  262. end
  263. avatar_list
  264. end
  265. =begin
  266. get default avatar image of user by hash
  267. store = Avatar.get_by_hash(hash)
  268. returns:
  269. store object
  270. =end
  271. def self.get_by_hash(hash)
  272. avatar = Avatar.find_by(
  273. store_hash: hash,
  274. )
  275. return if !avatar
  276. file = Store.find(avatar.store_resize_id)
  277. end
  278. =begin
  279. get default avatar of user by user id
  280. avatar = Avatar.get_default('User', user_id)
  281. returns:
  282. avatar object
  283. =end
  284. def self.get_default(object_name, o_id)
  285. object_id = ObjectLookup.by_name(object_name)
  286. Avatar.find_by(
  287. object_lookup_id: object_id,
  288. o_id: o_id,
  289. default: true,
  290. )
  291. end
  292. def self.set_default_items(object_id, o_id, avatar_id)
  293. avatars = Avatar.where(
  294. object_lookup_id: object_id,
  295. o_id: o_id,
  296. ).order('created_at ASC, id DESC')
  297. avatars.each do |avatar|
  298. next if avatar.id == avatar_id
  299. avatar.default = false
  300. avatar.save!
  301. end
  302. end
  303. def self.add_init_avatar(object_id, o_id)
  304. count = Avatar.where(
  305. object_lookup_id: object_id,
  306. o_id: o_id,
  307. ).count
  308. return if count.positive?
  309. Avatar.create(
  310. o_id: o_id,
  311. object_lookup_id: object_id,
  312. default: true,
  313. source: 'init',
  314. initial: true,
  315. deletable: false,
  316. updated_by_id: 1,
  317. created_by_id: 1,
  318. )
  319. end
  320. end