Improved fetching of records (race conditions).
This commit is contained in:
parent
3fefd7918e
commit
3b405562cc
11 changed files with 130 additions and 25 deletions
|
@ -42,7 +42,8 @@ class App.ControllerGenericNew extends App.ControllerModal
|
||||||
object.save(
|
object.save(
|
||||||
success: ->
|
success: ->
|
||||||
if ui.callback
|
if ui.callback
|
||||||
ui.callback( @ )
|
item = App[ ui.genericObject ].retrieve(@id)
|
||||||
|
ui.callback( item )
|
||||||
ui.modalHide()
|
ui.modalHide()
|
||||||
|
|
||||||
error: ->
|
error: ->
|
||||||
|
@ -88,7 +89,8 @@ class App.ControllerGenericEdit extends App.ControllerModal
|
||||||
@item.save(
|
@item.save(
|
||||||
success: ->
|
success: ->
|
||||||
if ui.callback
|
if ui.callback
|
||||||
ui.callback( @ )
|
item = App[ ui.genericObject ].retrieve(@id)
|
||||||
|
ui.callback( item )
|
||||||
ui.modalHide()
|
ui.modalHide()
|
||||||
|
|
||||||
error: =>
|
error: =>
|
||||||
|
|
|
@ -21,8 +21,6 @@ class App.WidgetUser extends App.ControllerDrox
|
||||||
App.User.unsubscribe(@subscribeId)
|
App.User.unsubscribe(@subscribeId)
|
||||||
|
|
||||||
render: (user) =>
|
render: (user) =>
|
||||||
if !user
|
|
||||||
user = @u
|
|
||||||
|
|
||||||
# get display data
|
# get display data
|
||||||
userData = []
|
userData = []
|
||||||
|
|
|
@ -72,8 +72,7 @@ class App.Auth
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
# set avatar
|
# set avatar
|
||||||
if !data.session.image
|
data.session.image = App.Config.get('api_path') + '/users/image/' + data.session.image
|
||||||
data.session.image = 'http://placehold.it/48x48'
|
|
||||||
|
|
||||||
# update config
|
# update config
|
||||||
for key, value of data.config
|
for key, value of data.config
|
||||||
|
|
|
@ -131,6 +131,7 @@ class App.Model extends Spine.Model
|
||||||
if @RETRIEVE_CALLBACK[ record.id ]
|
if @RETRIEVE_CALLBACK[ record.id ]
|
||||||
for key, callback of @RETRIEVE_CALLBACK[ record.id ]
|
for key, callback of @RETRIEVE_CALLBACK[ record.id ]
|
||||||
data = App[ @className ].find( record.id )
|
data = App[ @className ].find( record.id )
|
||||||
|
data = @_fillUp( data )
|
||||||
callback( data )
|
callback( data )
|
||||||
delete @RETRIEVE_CALLBACK[ record.id ][ key ]
|
delete @RETRIEVE_CALLBACK[ record.id ][ key ]
|
||||||
if _.isEmpty @RETRIEVE_CALLBACK[ record.id ]
|
if _.isEmpty @RETRIEVE_CALLBACK[ record.id ]
|
||||||
|
|
|
@ -24,9 +24,9 @@ class App.TicketArticle extends App.Model
|
||||||
|
|
||||||
# add created & updated
|
# add created & updated
|
||||||
if data.created_by_id
|
if data.created_by_id
|
||||||
data.created_by = App.User.find( data.created_by_id )
|
data.created_by = App.User.retrieve( data.created_by_id )
|
||||||
if data.updated_by_id
|
if data.updated_by_id
|
||||||
data.updated_by = App.User.find( data.updated_by_id )
|
data.updated_by = App.User.retrieve( data.updated_by_id )
|
||||||
|
|
||||||
# add relations
|
# add relations
|
||||||
data.article_type = App.TicketArticleType.find( data.ticket_article_type_id )
|
data.article_type = App.TicketArticleType.find( data.ticket_article_type_id )
|
||||||
|
|
|
@ -44,8 +44,7 @@ class App.User extends App.Model
|
||||||
data['accounts'][account]['link'] = 'https://www.facebook.com/profile.php?id=' + data['accounts'][account]['uid']
|
data['accounts'][account]['link'] = 'https://www.facebook.com/profile.php?id=' + data['accounts'][account]['uid']
|
||||||
|
|
||||||
# set image url
|
# set image url
|
||||||
if !data.image
|
data.image = @apiPath + '/users/image/' + data.image
|
||||||
data.image = 'http://placehold.it/48x48'
|
|
||||||
|
|
||||||
data
|
data
|
||||||
|
|
||||||
|
|
|
@ -570,4 +570,51 @@ curl http://localhost/api/v1/users/account.json -v -u #{login}:#{password} -H "C
|
||||||
render :json => { :message => 'ok' }, :status => :ok
|
render :json => { :message => 'ok' }, :status => :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
Resource:
|
||||||
|
GET /api/v1/users/image/8d6cca1c6bdc226cf2ba131e264ca2c7
|
||||||
|
|
||||||
|
Response:
|
||||||
|
<IMAGE>
|
||||||
|
|
||||||
|
Test:
|
||||||
|
curl http://localhost/api/v1/users/image/8d6cca1c6bdc226cf2ba131e264ca2c7 -v -u #{login}:#{password}
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def image
|
||||||
|
|
||||||
|
# cache image
|
||||||
|
response.headers['Expires'] = 1.year.from_now.httpdate
|
||||||
|
response.headers["Cache-Control"] = "cache, store, max-age=31536000, must-revalidate"
|
||||||
|
response.headers["Pragma"] = "cache"
|
||||||
|
|
||||||
|
# serve user image
|
||||||
|
user = User.where( :image => params[:hash] ).first
|
||||||
|
if user
|
||||||
|
# find file
|
||||||
|
list = Store.list( :object => 'User::Image', :o_id => user.id )
|
||||||
|
if list && list[0]
|
||||||
|
file = Store.find( list[0] )
|
||||||
|
send_data(
|
||||||
|
file.store_file.data,
|
||||||
|
:filename => file.filename,
|
||||||
|
:type => file.preferences['Content-Type'] || file.preferences['Mime-Type'],
|
||||||
|
:disposition => 'inline'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# serve defalt image
|
||||||
|
image = 'R0lGODdhMAAwAOMAAMzMzJaWlr6+vqqqqqOjo8XFxbe3t7GxsZycnAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAMAAwAAAEcxDISau9OOvNu/9gKI5kaZ5oqq5s675wLM90bd94ru98TwuAA+KQAQqJK8EAgBAgMEqmkzUgBIeSwWGZtR5XhSqAULACCoGCJGwlm1MGQrq9RqgB8fm4ZTUgDBIEcRR9fz6HiImKi4yNjo+QkZKTlJWWkBEAOw=='
|
||||||
|
send_data(
|
||||||
|
Base64.decode64(image),
|
||||||
|
:filename => 'image.gif',
|
||||||
|
:type => 'image/gif',
|
||||||
|
:disposition => 'inline',
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
# Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/
|
# Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
require 'digest/md5'
|
||||||
|
|
||||||
class User < ApplicationModel
|
class User < ApplicationModel
|
||||||
include User::Assets
|
include User::Assets
|
||||||
extend User::Search
|
extend User::Search
|
||||||
|
|
||||||
before_create :check_name, :check_email, :check_login, :check_image, :check_password
|
before_create :check_name, :check_email, :check_login, :check_image, :check_password
|
||||||
before_update :check_password, :check_image, :check_email, :check_login_update
|
before_update :check_password, :check_image, :check_email, :check_login_update
|
||||||
after_create :notify_clients_after_create
|
after_create :check_image_load, :notify_clients_after_create
|
||||||
after_update :notify_clients_after_update
|
after_update :check_image_load, :notify_clients_after_update
|
||||||
after_destroy :notify_clients_after_destroy
|
after_destroy :notify_clients_after_destroy
|
||||||
|
|
||||||
has_and_belongs_to_many :groups, :after_add => :cache_update, :after_remove => :cache_update
|
has_and_belongs_to_many :groups, :after_add => :cache_update, :after_remove => :cache_update
|
||||||
|
@ -22,12 +24,15 @@ class User < ApplicationModel
|
||||||
activity_stream_support(
|
activity_stream_support(
|
||||||
:role => 'Admin',
|
:role => 'Admin',
|
||||||
:ignore_attributes => {
|
:ignore_attributes => {
|
||||||
:last_login => true,
|
:last_login => true,
|
||||||
}
|
:image => true,
|
||||||
|
:image_source => true, }
|
||||||
)
|
)
|
||||||
history_support(
|
history_support(
|
||||||
:ignore_attributes => {
|
:ignore_attributes => {
|
||||||
:password => true,
|
:password => true,
|
||||||
|
:image => true,
|
||||||
|
:image_source => true,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -530,15 +535,49 @@ returns
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_image
|
def check_image
|
||||||
require 'digest/md5'
|
if !self.image_source || self.image_source == '' || self.image_source =~ /gravatar.com/i
|
||||||
if !self.image || self.image == ''
|
|
||||||
if self.email
|
if self.email
|
||||||
hash = Digest::MD5.hexdigest(self.email)
|
hash = Digest::MD5.hexdigest(self.email)
|
||||||
self.image = "http://www.gravatar.com/avatar/#{hash}?s=48"
|
self.image_source = "http://www.gravatar.com/avatar/#{hash}?s=48&d=404"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def check_image_load
|
||||||
|
|
||||||
|
return if !self.image_source
|
||||||
|
return if self.image_source !~ /http/i
|
||||||
|
|
||||||
|
# download image
|
||||||
|
response = UserAgent.request( self.image_source )
|
||||||
|
if !response.success?
|
||||||
|
self.update_column( :image, 'none' )
|
||||||
|
puts "WARNING: Can't fetch '#{url}', http code: #{response.code.to_s}"
|
||||||
|
#raise "Can't fetch '#{url}', http code: #{response.code.to_s}"
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
# store image local
|
||||||
|
hash = Digest::MD5.hexdigest( response.body )
|
||||||
|
|
||||||
|
# check if image has changed
|
||||||
|
return if self.image != hash
|
||||||
|
|
||||||
|
# save new image
|
||||||
|
self.update_column( :image, hash )
|
||||||
|
Store.remove( :object => 'User::Image', :o_id => self.id )
|
||||||
|
Store.add(
|
||||||
|
:object => 'User::Image',
|
||||||
|
:o_id => self.id,
|
||||||
|
:data => response.body,
|
||||||
|
:filename => 'image',
|
||||||
|
:preferences => {
|
||||||
|
'Content-Type' => response.content_type
|
||||||
|
},
|
||||||
|
:created_by_id => self.updated_by_id,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def check_password
|
def check_password
|
||||||
|
|
||||||
# set old password again if not given
|
# set old password again if not given
|
||||||
|
|
|
@ -13,5 +13,6 @@ Zammad::Application.routes.draw do
|
||||||
match api_path + '/users/history/:id', :to => 'users#history', :via => :get
|
match api_path + '/users/history/:id', :to => 'users#history', :via => :get
|
||||||
match api_path + '/users', :to => 'users#create', :via => :post
|
match api_path + '/users', :to => 'users#create', :via => :post
|
||||||
match api_path + '/users/:id', :to => 'users#update', :via => :put
|
match api_path + '/users/:id', :to => 'users#update', :via => :put
|
||||||
|
match api_path + '/users/image/:hash', :to => 'users#image', :via => :get
|
||||||
|
|
||||||
end
|
end
|
14
db/migrate/20131102000000_change_user2.rb
Normal file
14
db/migrate/20131102000000_change_user2.rb
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
class ChangeUser2 < ActiveRecord::Migration
|
||||||
|
def up
|
||||||
|
add_column :users, :image_source, :string, :limit => 200, :null => true
|
||||||
|
add_index :users, [:image]
|
||||||
|
User.all.each {|user|
|
||||||
|
puts "Update user #{user.login}"
|
||||||
|
user.image_source = user.image
|
||||||
|
user.save
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
end
|
||||||
|
end
|
|
@ -116,9 +116,10 @@ returns
|
||||||
|
|
||||||
when Net::HTTPOK
|
when Net::HTTPOK
|
||||||
return Result.new(
|
return Result.new(
|
||||||
:body => response.body,
|
:body => response.body,
|
||||||
:success => true,
|
:content_type => response['Content-Type'],
|
||||||
:code => response.code,
|
:success => true,
|
||||||
|
:code => response.code,
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -171,10 +172,11 @@ returns
|
||||||
|
|
||||||
class Result
|
class Result
|
||||||
def initialize(options)
|
def initialize(options)
|
||||||
@success = options[:success]
|
@success = options[:success]
|
||||||
@body = options[:body]
|
@body = options[:body]
|
||||||
@code = options[:code]
|
@code = options[:code]
|
||||||
@error = options[:error]
|
@content_type = options[:content_type]
|
||||||
|
@error = options[:error]
|
||||||
end
|
end
|
||||||
def error
|
def error
|
||||||
@error
|
@error
|
||||||
|
@ -188,5 +190,8 @@ returns
|
||||||
def code
|
def code
|
||||||
@code
|
@code
|
||||||
end
|
end
|
||||||
|
def content_type
|
||||||
|
@content_type
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue