]> source.dussan.org Git - redmine.git/commitdiff
Gravatar support for issue detai, user grid, and activity stream
authorEric Davis <edavis@littlestreamsoftware.com>
Thu, 30 Oct 2008 02:58:04 +0000 (02:58 +0000)
committerEric Davis <edavis@littlestreamsoftware.com>
Thu, 30 Oct 2008 02:58:04 +0000 (02:58 +0000)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1962 e93f8b46-1217-0410-a6f0-8f06a7374b81

12 files changed:
app/views/issues/_history.rhtml
app/views/issues/show.rhtml
app/views/projects/activity.rhtml
app/views/users/list.rhtml
public/stylesheets/application.css
vendor/plugins/gravatar/MIT-LICENSE [new file with mode: 0644]
vendor/plugins/gravatar/README [new file with mode: 0644]
vendor/plugins/gravatar/Rakefile [new file with mode: 0644]
vendor/plugins/gravatar/about.yml [new file with mode: 0644]
vendor/plugins/gravatar/init.rb [new file with mode: 0644]
vendor/plugins/gravatar/lib/gravatar.rb [new file with mode: 0644]
vendor/plugins/gravatar/spec/gravatar_spec.rb [new file with mode: 0644]

index b8efdb400c4f61554ee5f935d622086d3c7662e2..896e2802711c3919581217b44c1dba92915e7a98 100644 (file)
@@ -1,14 +1,15 @@
 <% reply_links = authorize_for('issues', 'edit') -%>
 <% for journal in journals %>
-    <div id="change-<%= journal.id %>" class="journal">
-       <h4><div style="float:right;"><%= link_to "##{journal.indice}", :anchor => "note-#{journal.indice}" %></div>
-       <%= content_tag('a', '', :name => "note-#{journal.indice}")%>
-       <%= format_time(journal.created_on) %> - <%= journal.user.name %></h4>
-       <ul>
-       <% for detail in journal.details %>
-          <li><%= show_detail(detail) %></li>
-       <% end %>
-       </ul>
-       <%= render_notes(journal, :reply_links => reply_links) unless journal.notes.blank? %>
-       </div>
+  <div id="change-<%= journal.id %>" class="journal">
+    <%= gravatar(journal.user.mail.blank? ? "" : journal.user.mail, :size => "48") %>
+    <h4><div style="float:right;"><%= link_to "##{journal.indice}", :anchor => "note-#{journal.indice}" %></div>
+    <%= content_tag('a', '', :name => "note-#{journal.indice}")%>
+    <%= format_time(journal.created_on) %> - <%= journal.user.name %></h4>
+    <ul>
+    <% for detail in journal.details %>
+       <li><%= show_detail(detail) %></li>
+    <% end %>
+    </ul>
+    <%= render_notes(journal, :reply_links => reply_links) unless journal.notes.blank? %>
+  </div>
 <% end %>
index 463fa696094c9c3c37c01776f12f740dc78e7400..88fa1df3da698858a17a48e51fbe7b40cb7be866 100644 (file)
@@ -10,6 +10,7 @@
 <h2><%= @issue.tracker.name %> #<%= @issue.id %></h2>
 
 <div class="issue <%= "status-#{@issue.status.position} priority-#{@issue.priority.position}" %>">
+        <%= gravatar(@issue.author.mail, :size => "64") %>
         <h3><%=h @issue.subject %></h3>
         <p class="author">
         <%= authoring @issue.created_on, @issue.author %>.
 
 <table width="100%">
 <tr>
-    <td style="width:15%"><b><%=l(:field_status)%>:</b></td><td style="width:35%"><%= @issue.status.name %></td>
-    <td style="width:15%"><b><%=l(:field_start_date)%>:</b></td><td style="width:35%"><%= format_date(@issue.start_date) %></td>
+    <td style="width:15%" class="status"><b><%=l(:field_status)%>:</b></td><td style="width:35%" class="status status-<%= @issue.status.name %>"><%= @issue.status.name %></td>
+    <td style="width:15%" class="start-date"><b><%=l(:field_start_date)%>:</b></td><td style="width:35%"><%= format_date(@issue.start_date) %></td>
 </tr>
 <tr>
-    <td><b><%=l(:field_priority)%>:</b></td><td><%= @issue.priority.name %></td>
-    <td><b><%=l(:field_due_date)%>:</b></td><td><%= format_date(@issue.due_date) %></td>
+    <td class="priority"><b><%=l(:field_priority)%>:</b></td><td class="priority priority-<%= @issue.priority.name %>"><%= @issue.priority.name %></td>
+    <td class="due-date"><b><%=l(:field_due_date)%>:</b></td><td class="due-date"><%= format_date(@issue.due_date) %></td>
 </tr>
 <tr>
-    <td><b><%=l(:field_assigned_to)%>:</b></td><td><%= @issue.assigned_to ? link_to_user(@issue.assigned_to) : "-" %></td>
-    <td><b><%=l(:field_done_ratio)%>:</b></td><td><%= progress_bar @issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%" %></td>
+    <td class="assigned-to"><b><%=l(:field_assigned_to)%>:</b></td><td><%= gravatar(@issue.assigned_to.mail, :size => "24") %><%= @issue.assigned_to ? link_to_user(@issue.assigned_to) : "-" %></td>
+    <td class="progress"><b><%=l(:field_done_ratio)%>:</b></td><td class="progress"><%= progress_bar @issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%" %></td>
 </tr>
 <tr>
-    <td><b><%=l(:field_category)%>:</b></td><td><%=h @issue.category ? @issue.category.name : "-" %></td>
+    <td class="category"><b><%=l(:field_category)%>:</b></td><td><%=h @issue.category ? @issue.category.name : "-" %></td>
     <% if User.current.allowed_to?(:view_time_entries, @project) %>
-    <td><b><%=l(:label_spent_time)%>:</b></td>
-    <td><%= @issue.spent_hours > 0 ? (link_to lwr(:label_f_hour, @issue.spent_hours), {:controller => 'timelog', :action => 'details', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time') : "-" %></td>
+    <td class="spent-time"><b><%=l(:label_spent_time)%>:</b></td>
+    <td class="spent-hours"><%= @issue.spent_hours > 0 ? (link_to lwr(:label_f_hour, @issue.spent_hours), {:controller => 'timelog', :action => 'details', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time') : "-" %></td>
     <% end %>
 </tr>
 <tr>
-    <td><b><%=l(:field_fixed_version)%>:</b></td><td><%= @issue.fixed_version ? link_to_version(@issue.fixed_version) : "-" %></td>
+    <td class="fixed-version"><b><%=l(:field_fixed_version)%>:</b></td><td><%= @issue.fixed_version ? link_to_version(@issue.fixed_version) : "-" %></td>
     <% if @issue.estimated_hours %>
-    <td><b><%=l(:field_estimated_hours)%>:</b></td><td><%= lwr(:label_f_hour, @issue.estimated_hours) %></td>
+    <td class="estimated-hours"><b><%=l(:field_estimated_hours)%>:</b></td><td><%= lwr(:label_f_hour, @issue.estimated_hours) %></td>
     <% end %>
 </tr>
 <tr>
index fa25812acb06d8daeece4821098d49dc2ba14466..47d32e6a390e9a7a856e779bfdb1968b6e4bf90b 100644 (file)
@@ -6,7 +6,10 @@
 <h3><%= format_activity_day(day) %></h3>
 <dl>
 <% @events_by_day[day].sort {|x,y| y.event_datetime <=> x.event_datetime }.each do |e| -%>
-  <dt class="<%= e.event_type %> <%= User.current.logged? && e.respond_to?(:event_author) && User.current == e.event_author ? 'me' : nil %>">
+  <dt class="<%= e.event_type %>  <%= User.current.logged? && e.respond_to?(:event_author) && User.current == e.event_author ? 'me' : nil %>">
+       <%= gravatar(e.user.mail, :size => "24") if e.respond_to?(:user) rescue nil%>
+       <%= gravatar(e.author.mail, :size => "24") if e.respond_to?(:author) rescue nil%>
+       <%= gravatar(e.committer.match('\\<.+?\\>')[0].gsub(/[<>]/, ''), :size => "24") if e.respond_to?(:committer) rescue nil%>
   <span class="time"><%= format_time(e.event_datetime, false) %></span>
   <%= content_tag('span', h(e.project), :class => 'project') if @project.nil? || @project != e.project %>
   <%= link_to format_activity_title(e.event_title), e.event_url %></dt>
index c41563759ba710fda3c5e842aba14704b44500b3..945c9926ce2a4ecd0ac4b83ff703df6dc64e4c9a 100644 (file)
@@ -29,7 +29,7 @@
   <tbody>
 <% for user in @users -%>
   <tr class="user <%= cycle("odd", "even") %> <%= %w(anon active registered locked)[user.status] %>">
-       <td class="username"><%= link_to h(user.login), :action => 'edit', :id => user %></td>
+       <td class="username"><%= gravatar(user.mail, :size => "24") %><%= link_to h(user.login), :action => 'edit', :id => user %></td>
        <td class="firstname"><%= h(user.firstname) %></td>
        <td class="lastname"><%= h(user.lastname) %></td>
        <td class="email"><%= mail_to(h(user.mail)) %></td>
index 6517fdaabf0dae855c23ce4d6f5eba7a9b11203c..f29e9e6fe4e2fada563a2ce0b845f84fab8186a4 100644 (file)
@@ -615,6 +615,42 @@ vertical-align: middle;
 .icon22-settings { background-image: url(../images/22x22/settings.png); }
 .icon22-plugin { background-image: url(../images/22x22/plugin.png); }
 
+img.gravatar { 
+       padding: 2px;
+  border: solid 1px #d5d5d5;
+  background: #fff;
+}
+
+div.issue img.gravatar {
+       float: right;
+       margin: 0 0 1em 1em;
+       padding: 5px;
+}
+
+div.issue table img.gravatar {
+       height: 24px;
+       width: 24px;
+       padding: 2px;
+       float: left;
+       margin: 0 1em 0 0;
+}
+
+#history img.gravatar {
+       padding: 3px;
+       margin: 0 2em 1em 0;
+       float: left;
+}
+
+td.username img.gravatar {
+       float: left;
+       margin: 0 1em 0 0;
+}
+
+#activity dt img.gravatar {
+       float: left;
+       margin: 0 1em 1em 0;
+}
+
 /***** Media print specific styles *****/
 @media print {
   #top-menu, #header, #main-menu, #sidebar, #footer, .contextual, .other-formats { display:none; }
diff --git a/vendor/plugins/gravatar/MIT-LICENSE b/vendor/plugins/gravatar/MIT-LICENSE
new file mode 100644 (file)
index 0000000..6a222ac
--- /dev/null
@@ -0,0 +1,20 @@
+Copyright (c) 2007 West Arete Computing, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/vendor/plugins/gravatar/README b/vendor/plugins/gravatar/README
new file mode 100644 (file)
index 0000000..dc516d3
--- /dev/null
@@ -0,0 +1,52 @@
+== Gravatar Plugin
+
+This plugin provides a handful of view helpers for displaying gravatars 
+(globally-recognized avatars). 
+
+Gravatars allow users to configure an avatar to go with their email address at
+a central location: http://gravatar.com. Gravatar-aware websites (such
+as yours) can then look up and display each user's preferred avatar, without
+having to handle avatar management. The user gets the benefit of not having to
+set up an avatar for each site that they post on.
+
+== Installation
+
+  cd ~/myapp
+  ruby script/plugin install svn://rubyforge.org//var/svn/gravatarplugin/plugins/gravatar
+
+or, if you're using piston[http://piston.rubyforge.org] (worth it!):
+
+  cd ~/myapp/vendor/plugins
+  piston import svn://rubyforge.org//var/svn/gravatarplugin/plugins/gravatar
+
+== Example
+
+If you represent your users with a model that has an +email+ method (typical
+for most rails authentication setups), then you can simply use this method
+in your views:
+
+  <%= gravatar_for @user %>
+  
+This will be replaced with the full HTML +img+ tag necessary for displaying 
+that user's gravatar.
+
+Other helpers are documented under GravatarHelper::PublicMethods.
+
+== Acknowledgments
+
+The following people have also written gravatar-related Ruby libraries:
+* Seth Rasmussen created the gravatar gem[http://gravatar.rubyforge.org]
+* Matt McCray has also created a gravatar 
+  plugin[http://mattmccray.com/svn/rails/plugins/gravatar_helper]
+
+== Author
+
+  Scott A. Woods
+  West Arete Computing, Inc.
+  http://westarete.com
+  scott at westarete dot com
+
+== TODO
+
+* Get full spec coverage
+* Finish rdoc documentation
\ No newline at end of file
diff --git a/vendor/plugins/gravatar/Rakefile b/vendor/plugins/gravatar/Rakefile
new file mode 100644 (file)
index 0000000..5a8d92a
--- /dev/null
@@ -0,0 +1,33 @@
+require 'spec/rake/spectask'
+require 'rake/rdoctask'
+
+desc 'Default: run all specs'
+task :default => :spec
+
+desc 'Run all application-specific specs'
+Spec::Rake::SpecTask.new(:spec) do |t|
+  t.warning = true
+  t.rcov = true
+end
+
+desc "Report code statistics (KLOCs, etc) from the application"
+task :stats do
+  RAILS_ROOT = File.dirname(__FILE__)
+  STATS_DIRECTORIES = [
+    %w(Libraries  lib/),
+    %w(Specs      spec/),
+  ].collect { |name, dir| [ name, "#{RAILS_ROOT}/#{dir}" ] }.select { |name, dir| File.directory?(dir) }
+  require 'code_statistics'
+  CodeStatistics.new(*STATS_DIRECTORIES).to_s
+end
+
+namespace :doc do
+  desc 'Generate documentation for the assert_request plugin.'
+  Rake::RDocTask.new(:plugin) do |rdoc|
+    rdoc.rdoc_dir = 'rdoc'
+    rdoc.title    = 'Gravatar Rails Plugin'
+    rdoc.options << '--line-numbers' << '--inline-source' << '--accessor' << 'cattr_accessor=rw'
+    rdoc.rdoc_files.include('README')
+    rdoc.rdoc_files.include('lib/**/*.rb')
+  end
+end
diff --git a/vendor/plugins/gravatar/about.yml b/vendor/plugins/gravatar/about.yml
new file mode 100644 (file)
index 0000000..a801d25
--- /dev/null
@@ -0,0 +1,7 @@
+author: Scott Woods, West Arete Computing
+summary: View helpers for displaying gravatars.
+homepage: http://gravatarplugin.rubyforge.org/
+plugin: svn://rubyforge.org//var/svn/gravatarplugin/plugins/gravatar
+license: MIT
+version: 0.1
+rails_version: 1.0+
diff --git a/vendor/plugins/gravatar/init.rb b/vendor/plugins/gravatar/init.rb
new file mode 100644 (file)
index 0000000..aab3f75
--- /dev/null
@@ -0,0 +1,2 @@
+require 'gravatar'
+ActionView::Base.send :include, GravatarHelper::PublicMethods
diff --git a/vendor/plugins/gravatar/lib/gravatar.rb b/vendor/plugins/gravatar/lib/gravatar.rb
new file mode 100644 (file)
index 0000000..58cb4ca
--- /dev/null
@@ -0,0 +1,67 @@
+require 'digest/md5'
+require 'cgi'
+
+module GravatarHelper
+
+  # These are the options that control the default behavior of the public
+  # methods. They can be overridden during the actual call to the helper,
+  # or you can set them in your environment.rb as such:
+  #
+  #   # Allow racier gravatars
+  #   GravatarHelper::DEFAULT_OPTIONS[:rating] = 'R'
+  #
+  DEFAULT_OPTIONS = {
+    # The URL of a default image to display if the given email address does
+    # not have a gravatar.
+    :default => nil,
+    
+    # The default size in pixels for the gravatar image (they're square).
+    :size => 50,
+    
+    # The maximum allowed MPAA rating for gravatars. This allows you to 
+    # exclude gravatars that may be out of character for your site.
+    :rating => 'PG',
+    
+    # The alt text to use in the img tag for the gravatar.
+    :alt => 'avatar',
+    
+    # The class to assign to the img tag for the gravatar.
+    :class => 'gravatar',
+  }
+  
+  # The methods that will be made available to your views.
+  module PublicMethods
+  
+    # Return the HTML img tag for the given user's gravatar. Presumes that 
+    # the given user object will respond_to "email", and return the user's
+    # email address.
+    def gravatar_for(user, options={})
+      gravatar(user.email, options)
+    end
+
+    # Return the HTML img tag for the given email address's gravatar.
+    def gravatar(email, options={})
+      src = h(gravatar_url(email, options))
+      options = DEFAULT_OPTIONS.merge(options)
+      [:class, :alt, :size].each { |opt| options[opt] = h(options[opt]) }
+      "<img class=\"#{options[:class]}\" alt=\"#{options[:alt]}\" width=\"#{options[:size]}\" height=\"#{options[:size]}\" src=\"#{src}\" />"      
+    end
+
+    # Return the gravatar URL for the given email address.
+    def gravatar_url(email, options={})
+      email_hash = Digest::MD5.hexdigest(email)
+      options = DEFAULT_OPTIONS.merge(options)
+      options[:default] = CGI::escape(options[:default]) unless options[:default].nil?
+      returning "http://www.gravatar.com/avatar.php?gravatar_id=#{email_hash}" do |url| 
+        [:rating, :size, :default].each do |opt|
+          unless options[opt].nil?
+            value = h(options[opt])
+            url << "&#{opt}=#{value}" 
+          end
+        end
+      end
+    end
+
+  end
+  
+end
\ No newline at end of file
diff --git a/vendor/plugins/gravatar/spec/gravatar_spec.rb b/vendor/plugins/gravatar/spec/gravatar_spec.rb
new file mode 100644 (file)
index 0000000..a11d268
--- /dev/null
@@ -0,0 +1,37 @@
+require 'rubygems'
+require 'erb'  # to get "h"
+require 'active_support'  # to get "returning"
+require File.dirname(__FILE__) + '/../lib/gravatar'
+include GravatarHelper, GravatarHelper::PublicMethods, ERB::Util
+
+context "gravatar_url with a custom default URL" do
+  setup do
+    @original_options = DEFAULT_OPTIONS.dup
+    DEFAULT_OPTIONS[:default] = "no_avatar.png"
+    @url = gravatar_url("somewhere")
+  end
+  
+  specify "should include the \"default\" argument in the result" do
+    @url.should match(/&default=no_avatar.png/)
+  end
+  
+  teardown do
+    DEFAULT_OPTIONS.merge!(@original_options)
+  end
+  
+end
+
+context "gravatar_url with default settings" do
+  setup do
+    @url = gravatar_url("somewhere")
+  end
+  
+  specify "should have a nil default URL" do
+    DEFAULT_OPTIONS[:default].should be_nil
+  end
+  
+  specify "should not include the \"default\" argument in the result" do
+    @url.should_not match(/&default=/)
+  end  
+  
+end
\ No newline at end of file