Skip to content

Commit

Permalink
Avatar url field added to user model
Browse files Browse the repository at this point in the history
External avatar url can now be set from account settings.
Test added for external avatar url field.
  • Loading branch information
Artem committed Aug 7, 2013
1 parent 89ef4d9 commit a4acf62
Show file tree
Hide file tree
Showing 11 changed files with 138 additions and 14 deletions.
10 changes: 7 additions & 3 deletions app/assets/javascripts/backbone/helpers/avatars.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ class Kandan.Helpers.Avatars
size = options.size || 30
fallback = options.fallback || Kandan.options().avatar_fallback || 'mm'
avatarHash = a.gravatar_hash || a.get('user').gravatar_hash || a.get('gravatarHash')
Kandan.options().avatar_url.replace(/%{hash}/, avatarHash).
replace(/%{size}/, size).
replace(/%{fallback}/, fallback)
try
avatarUrl = a.avatar_url || a.get('user').avatar_url || a.get('avatarUrl')
catch error
console.log "Error occuried while resolving user avatar: #{error}"
avatarUrl or Kandan.options().avatar_url.replace(/%{hash}/, avatarHash).
replace(/%{size}/, size).
replace(/%{fallback}/, fallback)
2 changes: 1 addition & 1 deletion app/controllers/channels_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def index
more_activities = (channel.activities.count > Kandan::Config.options[:per_page])
channel.activities.order('id DESC').includes(:user).page.each do |activity|
activities.push activity.attributes.merge({
:user => activity.user_or_deleted_user.as_json(:only => [:id, :email, :first_name, :last_name, :gravatar_hash, :active, :locale, :username])
:user => activity.user_or_deleted_user.as_json(:only => [:id, :email, :first_name, :last_name, :gravatar_hash, :active, :locale, :username, :avatar_url])
})
end

Expand Down
3 changes: 2 additions & 1 deletion app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ def current_user_data
:email => current_user.email,
:username => current_user.username,
:auth_token => current_user.authentication_token,
:gravatar_hash => current_user.gravatar_hash
:gravatar_hash => current_user.gravatar_hash,
:avatar_url => current_user.avatar_url
}
end
end
10 changes: 7 additions & 3 deletions app/helpers/avatar_helper.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
module AvatarHelper
def avatar_url_for(user, options = {})
Kandan::Config.options[:avatar_url].gsub(/%{hash}/, user.gravatar_hash).
gsub(/%{size}/, (options[:size] || 30).to_s).
gsub(/%{fallback}/, options[:fallback] || Kandan::Config.options[:avatar_fallback] || 'mm')
if user.avatar_url and user.avatar_url.length > 0
user.avatar_url
else
Kandan::Config.options[:avatar_url].gsub(/%{hash}/, user.gravatar_hash).
gsub(/%{size}/, (options[:size] || 30).to_s).
gsub(/%{fallback}/, options[:fallback] || Kandan::Config.options[:avatar_fallback] || 'mm')
end
end
end
4 changes: 2 additions & 2 deletions app/models/activity_observer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def after_save(activity)
def message_broadcast_data(activity)
faye_channel = "/channels/#{activity.channel.to_param}"
broadcast_data = activity.attributes.merge({
:user => activity.user.as_json(:only => [:id, :email, :first_name, :last_name, :gravatar_hash, :active, :locale, :username, :is_admin]),
:user => activity.user.as_json(:only => [:id, :email, :first_name, :last_name, :gravatar_hash, :active, :locale, :username, :is_admin, :avatar_url]),
:channel => activity.channel.attributes
})
[faye_channel, broadcast_data]
Expand All @@ -22,7 +22,7 @@ def upload_broadcast_data(activity)
broadcast_data = {
:event => "attachment#upload",
:entity => activity.attributes.merge({
:user => activity.user.as_json(:only => [:id, :email, :first_name, :last_name, :gravatar_hash, :active, :locale, :username, :is_admin]),
:user => activity.user.as_json(:only => [:id, :email, :first_name, :last_name, :gravatar_hash, :active, :locale, :username, :is_admin, :avatar_url]),
:channel => activity.channel.attributes
}),
:extra => {
Expand Down
56 changes: 55 additions & 1 deletion app/models/user.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
require 'net/http'
require 'uri'

class User < ActiveRecord::Base
extend Enumerize

Expand All @@ -13,12 +16,13 @@ class User < ActiveRecord::Base
after_destroy :ensure_at_least_one_admin

validates :username, :presence => true, :uniqueness => true
validate :check_external_avatar

# Kandan.devise_modules is defined in config/initializers/kandan.rb
devise devise *Kandan.devise_modules

# Setup accessible (or protected) attributes for your model
attr_accessible :id, :username, :email, :password, :password_confirmation, :remember_me, :first_name, :last_name, :locale, :gravatar_hash, :registration_status
attr_accessible :id, :username, :email, :password, :password_confirmation, :remember_me, :first_name, :last_name, :locale, :gravatar_hash, :registration_status, :avatar_url

def full_name
"#{self.first_name.to_s} #{self.last_name.to_s}".titlecase
Expand Down Expand Up @@ -74,4 +78,54 @@ def suspend!
self.suspend && self.save!
end

# Check if avatar size does not exceed setting paramater :external_avatar_max_size
# and if image extension is allowed
def check_external_avatar
# avatar url is not required
if self.avatar_url.nil?
return
end

if self.avatar_url.empty?
errors.add(:avatar_url, "cannot be empty")
return
end

uri = URI(avatar_url)

# Check for file extension
extension = File.extname(uri.path)
unless Kandan::Config.options[:external_avatar_formats].include? extension.downcase
errors.add(:avatar_url, "extension is invalid")
return
end

# Check protocol
unless ['http', 'https'].include?(uri.scheme)
errors.add(:avatar_url, "protocol is invalid")
return
end

# Check for file size
Net::HTTP.start(uri.host, uri.port,
:use_ssl => uri.scheme == 'https') do |http|
begin
response = http.request_head(uri.to_s)
file_size = response['content-length']

if file_size.nil?
file_size = 0
end

size_in_bounds = Integer(file_size).between?(1, Kandan::Config.options[:external_avatar_max_size])
unless size_in_bounds
errors.add(:avatar_url, "image size is out of bounds (maximum %{max_size} bytes)" % {:max_size => Kandan::Config.options[:external_avatar_max_size]})
end

rescue
errors.add(:avatar_url, "is invalid")
end
end
end

end
8 changes: 6 additions & 2 deletions app/views/devise/registrations/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
}
.form-signin input[type="text"],
.form-signin input[type="email"],
.form-signin input[type="password"] {
.form-signin input[type="password"],
.form-signin input[type="url"] {
font-size: 16px;
height: auto;
margin-bottom: 15px;
Expand All @@ -52,7 +53,10 @@
<div class="note"><i>(leave blank if you don't want to change it)</i>
<%= f.password_field :password , :placeholder => ' New Password' %>
<%= f.password_field :password_confirmation, :placeholder => ' New Password Confirm' %>
</div>
</div>
<div class="note"><i>(Avatar URL replaces your gravatar image)</i>
<%= f.url_field :avatar_url, :placeholder => ' Avatar URL' %>
</div>
<div class="note"><i>(Enter your current password to confirm your changes)</i>
<%= f.password_field :current_password, :placeholder => ' Current Password' %>
</div>
Expand Down
4 changes: 4 additions & 0 deletions config/kandan_settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@

:now_threshold: 3000
:timestamp_refresh_interval: 2000

# Max external avatar size in bytes
:external_avatar_max_size: 102400
:external_avatar_formats: [".jpeg", ".jpg", ".png", ".gif"]
5 changes: 5 additions & 0 deletions db/migrate/20130623202749_add_avatar_url_to_user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddAvatarUrlToUser < ActiveRecord::Migration
def change
add_column :users, :avatar_url, :string
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.

ActiveRecord::Schema.define(:version => 20130315214129) do
ActiveRecord::Schema.define(:version => 20130623202749) do

create_table "activities", :force => true do |t|
t.text "content"
Expand Down Expand Up @@ -81,6 +81,7 @@
t.string "username"
t.boolean "is_admin"
t.string "registration_status", :default => "active"
t.string "avatar_url"
end

add_index "users", ["authentication_token"], :name => "index_users_on_authentication_token", :unique => true
Expand Down
47 changes: 47 additions & 0 deletions spec/models/user_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,51 @@
subject { @user.gravatar_hash }
expect(subject).to_not eq(nil)
end

describe "external avatar" do
it "should have no avatar url on creation" do
subject { User.new() }
expect(subject.avatar_url).to be_nil
end

context "check url" do
subject { FactoryGirl.build(:user) }

it "should accept non-empty small image with allowed extension" do
subject.avatar_url = "https://github.global.ssl.fastly.net/images/icons/emoji/+1.png"
expect(subject.save).to be_true
expect(subject).to be_valid
end

it "should not accept extension that is not allowed in config" do
# a 35KB midi sound
subject.avatar_url = "http://saya.pianomidi.org/musica/lfals/a-asturias.mid"
expect(subject.save).to be_false
expect(subject).to_not be_valid
expect(subject.errors).to have_key(:avatar_url)
end

it "should not validate nonexistent url as avatar" do
subject.avatar_url = "unknownProtocol://some-url-that-does-not/realy/exist.png"
expect(subject.save).to be_false
expect(subject).to_not be_valid
expect(subject.errors).to have_key(:avatar_url)
end

it "should not validate big images as avatar" do
# a really big Hubble image of a pair of interacting galaxies
subject.avatar_url = "http://www.spacetelescope.org/static/archives/images/publicationjpg/heic1107a.jpg"
expect(subject.save).to be_false
expect(subject).to_not be_valid
expect(subject.errors).to have_key(:avatar_url)
end

it "should not validate empty url as avatar" do
subject.avatar_url = ""
expect(subject.save).to be_false
expect(subject).to_not be_valid
expect(subject.errors).to have_key(:avatar_url)
end
end
end
end

0 comments on commit a4acf62

Please sign in to comment.