Users can host a headshot image anywhere on the web and then assign that
to their account. Also supported are Gravatar images.
THIS PATCH REQUIRES A MIGRATION
Added a method, User.avatar?, to check if the user has defined an
avatar. Another method, User.avatar_url, returns the user's avatar URL
or else nil if no avatar was defined.
On the user's profile page, the avatar is shown in a div separate from
the user's details.
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
app/helpers/application_helper.rb | 2 -
app/models/user.rb | 21 +++++++++++++
app/views/users/_edit.html.erb | 19 +++++++++++
app/views/users/_user.html.erb | 6 ----
app/views/users/show.html.erb | 19 +++++++++--
db/migrate/028_add_avatar_url_to_users.rb | 28 +++++++++++++++++
lib/avatar.rb | 47 +++++++++++++++++++++++++++++
public/stylesheets/details.css | 31 ++++++++++++++++++-
test/fixtures/users.yml | 4 ++
test/unit/user_test.rb | 40 ++++++++++++++++++++++++
10 files changed, 204 insertions(+), 13 deletions(-)
create mode 100644 db/migrate/028_add_avatar_url_to_users.rb
create mode 100644 lib/avatar.rb
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 9e81b48..02b10ab 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -45,7 +45,6 @@ module ApplicationHelper
end
# Creates a link to a backlog item.
- #
def link_to_backlog_item(backlog_item, options = {})
@template.link_to(options[:text] ? options[:text] : backlog_item.title,
:controller => :backlog,
@@ -54,7 +53,6 @@ module ApplicationHelper
end
# Shows the hours for a given object as estimated, actual and remaining.
- #
def show_hours_as_ear(estimated)
format("%0.1f/%0.1f/%0.1f",estimated.estimated_hours,
estimated.actual_hours, estimated.remaining_hours)
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 613b7ad..862cbbf 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -15,6 +15,8 @@
# this program. If not, see <
http://www.gnu.org/licenses/>.
#
+require 'avatar'
+
# +User+ represents a single user in the system.
#
class User < ActiveRecord::Base
@@ -114,6 +116,25 @@ class User < ActiveRecord::Base
(id == project.owner.id) || privileges.admin_projects
end
+ # Returns whether the user has an avatar or not.
+ def avatar?
+ use_gravatar || (!url_for_avatar.nil? && !url_for_avatar.empty?)
+ end
+
+ # Returns the user's avatar url, or +nil+ if none was defined.
+ def avatar_url
+ result = nil
+
+ if !use_gravatar && !url_for_avatar.nil? && !url_for_avatar.empty?
+ result = url_for_avatar
+ elsif use_gravatar
+ result = (!url_for_avatar.nil? && !url_for_avatar.empty?) ? url_for_avatar
: email
+ result = gravatar_url(result)
+ end
+
+ return result
+ end
+
private
def self.encrypted_password(password, salt)
diff --git a/app/views/users/_edit.html.erb b/app/views/users/_edit.html.erb
index bfe36fd..14ab203 100644
--- a/app/views/users/_edit.html.erb
+++ b/app/views/users/_edit.html.erb
@@ -48,6 +48,25 @@
<% end %>
+ <tr>
+ <td class="label"></td>
+ <td class="value">
+ <%= form.check_box :use_gravatar %>
+ <%= form.label :use_gravatar, "Use a Gravatar-hosted image?" %>
+ </td>
+ </tr>
+ <tr>
+ <td class="label">Avatar URL/Gravatar email</td>
+ <td class="value">
+ <%= form.text_field :url_for_avatar %>
+ </td>
+ </tr>
+ <% if @this_user.avatar? %>
+ <tr>
+ <td /><td id="current_avatar"><%= image_tag
@this_user.avatar_url %></td>
+ </tr>
+ <% end %>
+
<tr>
<td class="label">Introduction</td>
<td class="value">
diff --git a/app/views/users/_user.html.erb b/app/views/users/_user.html.erb
index c1950fa..974ba3e 100644
--- a/app/views/users/_user.html.erb
+++ b/app/views/users/_user.html.erb
@@ -12,12 +12,6 @@
<tbody>
<tr>
- <td class="toolbar" colspan="2">
- <%= link_to image_tag("icons/user.png", :title => "View user's
page..."), user_path(user) %>
- </td>
- </tr>
-
- <tr>
<td class="text" colspan="2"><%=
RedCloth.new(user.introduction).to_html %></td>
</tr>
</tbody>
diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb
index 4414c78..e32ce69 100644
--- a/app/views/users/show.html.erb
+++ b/app/views/users/show.html.erb
@@ -16,15 +16,26 @@
<%= link_to(image_tag("icons/back.png", :title =>
"Back..."), users_path) %>
<%= link_to(image_tag("icons/roles.png", :title => "View this
user's roles..."), roles_user_path(@user)) %>
<%= link_to(image_tag("icons/edit.png", :title => "Edit this
user..."),
- edit_user_path(@this_user)) if @this_user.can_edit?(@user) %>
+ edit_user_path(@this_user)) if @this_user.can_edit?(@user) %>
<%= link_to(image_tag("icons/password.png", :title =>
"Change the account password..."),
- password_user_path(@this_user)) if @this_user.can_edit?(@user) %>
+ password_user_path(@this_user)) if @this_user.can_edit?(@user) %>
</td>
</tr>
<tr>
- <td class="text" colspan="2">
- <%= RedCloth.new((a)this_user.introduction).to_html %></td>
+ <td colspan="2" class="user_details">
+ <dl>
+ <dt><%= "#{(a)this_user.display_name}" %></dt>
+ <% if @this_user.avatar? %>
+ <dd>
+ <%= image_tag @this_user.avatar_url %>
+ </dd>
+ <% end %>
+ <dd>
+ <%= RedCloth.new((a)this_user.introduction).to_html %>
+ </dd>
+ </dl>
+ </td>
</tr>
</tbody>
</table>
diff --git a/db/migrate/028_add_avatar_url_to_users.rb
b/db/migrate/028_add_avatar_url_to_users.rb
new file mode 100644
index 0000000..7becc37
--- /dev/null
+++ b/db/migrate/028_add_avatar_url_to_users.rb
@@ -0,0 +1,28 @@
+# 027_create_sprint_members.rb
+# Copyright (C) 2009, Darryl L. Pierce <mcpierce(a)gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <
http://www.gnu.org/licenses/>.
+#
+
+class AddAvatarUrlToUsers < ActiveRecord::Migration
+ def self.up
+ add_column :users, :url_for_avatar, :string, :null => true, :limit => 128
+ add_column :users, :use_gravatar, :boolean, :null => false, :default => false
+ end
+
+ def self.down
+ remove_column :users, :url_for_avatar
+ remove_column :users, :use_gravatar
+ end
+end
diff --git a/lib/avatar.rb b/lib/avatar.rb
new file mode 100644
index 0000000..1b6416b
--- /dev/null
+++ b/lib/avatar.rb
@@ -0,0 +1,47 @@
+# avatar.rb
+# Copyright (C) 2008, Darryl L. Pierce <mcpierce(a)gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <
http://www.gnu.org/licenses/>.
+#
+
+# Generates a URL for Gravatar-hosted images.
+def gravatar_url(email, size="80", rating=nil, default=nil)
+ # The supported ratings from gravatar
+ allowed_ratings = ['g', 'pg', 'r', 'x']
+
+ # query will be our extra param string
+ query = "s=" + size + "&"
+ # if we have a rating ...
+ if rating != nil
+ # ... and it is allowed
+ if allowed_ratings.index(rating) != nil
+ query += "r=" + rating + "&"
+ else
+ # if it is not allowed, raise an exception
+ raise "No such gravatar rating: '" + rating + "'"
+ end
+ end
+ # if we have a default then use it
+ if default != nil
+ query += "d=" + URI.parse(URI.encode(default)).to_s() + "&"
+ end
+ # put it all together in an HTTP object
+ url = URI::HTTP.build({
+ :host => 'www.gravatar.com',
+ :path => '/avatar/' + Digest::MD5.hexdigest(email),
+ :query => query[0, query.size()-1]
+ })
+ # and return the built string
+ return url.to_s()
+end
diff --git a/public/stylesheets/details.css b/public/stylesheets/details.css
index 82bacdf..515b06e 100644
--- a/public/stylesheets/details.css
+++ b/public/stylesheets/details.css
@@ -13,7 +13,7 @@ table.detail {
table.detail th.title, table.detail td.title {
font-weight: bold;
- text-align: center;
+ text-align: center;
border-bottom: 1px solid #000000;
background-color: #f3f3f3;
}
@@ -34,3 +34,32 @@ table.detail td.text {
font-size: 90%;
padding: 5px 10px 1px 10px;
}
+
+table.detail td.user_details {
+ width: 300px;
+ padding: 10px 0;
+ border: 2px solid #ccdd2;
+}
+
+table.detail td.user_details dl {
+ margin: 10px 20px;
+ padding: 0;
+}
+
+table.detail td.user_details dt {
+ margin: 0;
+ padding: 0;
+ font-size: 130%;
+ letter-spacing: 1px;
+}
+
+table.detail td.user_details dd {
+ margin: 0;
+ padding: 0;
+ font-size: 85%;
+ line-height: 1.5em;
+}
+
+table.detail td.user_details dd img {
+ float:left;
+}
diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml
index 2738441..46dcc78 100644
--- a/test/fixtures/users.yml
+++ b/test/fixtures/users.yml
@@ -41,6 +41,7 @@ mcpierce:
introduction: How are you?
salt: <%= SALT %>
hashed_password: <%= User.encrypted_password('farkle', SALT) %>
+ use_gravatar: true
jdonuts:
email: jdonuts(a)gmail.com
@@ -48,6 +49,8 @@ jdonuts:
introduction: How's it going, eh?
salt: <%= SALT %>
hashed_password: <%= User.encrypted_password('jelly', SALT) %>
+ use_gravatar: true
+ url_for_avatar: jdonuts(a)projxp.org
celliot:
email: celliot(a)gmail.com
@@ -55,6 +58,7 @@ celliot:
introduction: Get a life.
salt: <%= SALT %>
hashed_password: <%= User.encrypted_password('loser', SALT) %>
+ url_for_avatar:
http://www.born-today.com/btpix/elliot_chris.jpg
unverified_user:
email: unverified(a)newuser.com
diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb
index 7d3cc9b..063517c 100644
--- a/test/unit/user_test.rb
+++ b/test/unit/user_test.rb
@@ -16,6 +16,7 @@
#
require File.dirname(__FILE__) + '/../test_helper'
+require 'avatar'
class UserTest < ActiveSupport::TestCase
fixtures :users
@@ -25,6 +26,16 @@ class UserTest < ActiveSupport::TestCase
@verified_user = @mcpierce
raise "User should be verified!" unless @verified_user.verified?
@unverified_user = users(:unverified_user)
+
+ @user_with_no_avatar = users(:admin)
+ raise "User should not have an avatar!" unless
!(a)user_with_no_avatar.use_gravatar && @user_with_no_avatar.url_for_avatar.nil?
+ @user_with_avatar = users(:celliot)
+ raise "User should have an avatar!" if
@user_with_avatar.url_for_avatar.nil?
+ @user_with_gravatar = @mcpierce
+ raise "User does not use gravatar!" unless
@user_with_gravatar.use_gravatar
+ @user_with_special_gravatar = users(:jdonuts)
+ raise "User is not using gravatar!" unless
@user_with_special_gravatar.use_gravatar
+ raise "User does not have a special avatar URL!" if
@user_with_special_gravatar.avatar_url.nil?
end
# Ensures that authenticating a user using the wrong password fails.
@@ -49,4 +60,33 @@ class UserTest < ActiveSupport::TestCase
def test_verified_with_unverified_user
flunk "User is not verified." if @unverified_user.verified?
end
+
+ # Ensures that a user with no avatar returns the right value when queries.
+ def test_avatar_reports_no
+ flunk "User should not say he has an avatar." if
@user_with_no_avatar.avatar?
+ end
+
+ # Ensures that a user with an avatar URL returns the right URL.
+ def test_avatar_url
+ expected = @user_with_avatar.url_for_avatar
+
+ assert_equal expected, @user_with_avatar.avatar_url,
+ "Wrong avatar URL returned."
+ end
+
+ # Ensures that a user with a gravatar image returns the right url.
+ def test_gravatar_url
+ expected = gravatar_url((a)user_with_gravatar.email)
+
+ assert_equal expected, @user_with_gravatar.avatar_url,
+ "Wrong avatar URL returned."
+ end
+
+ # Ensures that a user with a special gravatar image gets the right URL.
+ def test_gravatar_special_url
+ expected = gravatar_url((a)user_with_special_gravatar.url_for_avatar)
+
+ assert_equal expected, @user_with_special_gravatar.avatar_url,
+ "Wrong avatar URL returned."
+ end
end
--
1.6.0.6