avatar.rb 8.5 KB

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