changeset 1078:b9e34a051f82

Merge from live branch
author Chris Cannam
date Wed, 21 Nov 2012 17:56:50 +0000
parents 97a00e2e145d (current diff) 9b5b4e7970cd (diff)
children 1b1138f6f55e
files
diffstat 58 files changed, 1125 insertions(+), 419 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Tue Oct 16 15:25:01 2012 +0100
+++ b/.hgignore	Wed Nov 21 17:56:50 2012 +0000
@@ -29,9 +29,10 @@
 .svn/
 .git/
 *~
-
+public/themes/soundsoftware/stylesheets/fonts/*
 
 .bundle
 Gemfile.lock
 Gemfile.local
 
+re:^config\.ru$
--- a/app/controllers/activities_controller.rb	Tue Oct 16 15:25:01 2012 +0100
+++ b/app/controllers/activities_controller.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -40,7 +40,22 @@
 
     events = @activity.events(@date_from, @date_to)
 
-    if events.empty? || stale?(:etag => [@activity.scope, @date_to, @date_from, @with_subprojects, @author, events.first, User.current, current_language])
+    @institution_name = params[:institution]
+    if !@institution_name.blank?
+      events = events.select do |e|
+        e.respond_to?(:event_author) and e.event_author and
+	  e.event_author.respond_to?(:ssamr_user_detail) and
+          !e.event_author.ssamr_user_detail.nil? and
+          e.event_author.ssamr_user_detail.institution_name == @institution_name
+      end
+      if events.empty?
+        # We don't want to dump into the output any arbitrary string
+        # from the URL that has no matching events
+        @institution_name = ""
+      end
+    end
+
+    if events.empty? || stale?(:etag => [@activity.scope, @date_to, @date_from, @with_subprojects, @author, @institution_name, events.first, User.current, current_language])
       respond_to do |format|
         format.html {
           @events_by_day = events.group_by(&:event_date)
--- a/app/controllers/attachments_controller.rb	Tue Oct 16 15:25:01 2012 +0100
+++ b/app/controllers/attachments_controller.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -17,6 +17,9 @@
 
 class AttachmentsController < ApplicationController
 
+  include AttachmentsHelper
+  helper :attachments
+
   before_filter :find_project
   before_filter :file_readable, :read_authorize, :except => :destroy
   before_filter :delete_authorize, :only => :destroy
@@ -49,7 +52,9 @@
   end
 
   def download
-    if @attachment.container.is_a?(Version) || @attachment.container.is_a?(Project)
+    # cc: formerly this happened only if "@attachment.container.is_a?(Version)"
+    # or Project. Not good for us, we want to tally all downloads [by humans]
+    if not user_is_search_bot?
       @attachment.increment_download
     end
 
--- a/app/controllers/projects_controller.rb	Tue Oct 16 15:25:01 2012 +0100
+++ b/app/controllers/projects_controller.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -20,8 +20,8 @@
   menu_item :roadmap, :only => :roadmap
   menu_item :settings, :only => :settings
 
-  before_filter :find_project, :except => [ :index, :list, :new, :create, :copy ]
-  before_filter :authorize, :except => [ :index, :list, :new, :create, :copy, :archive, :unarchive, :destroy]
+  before_filter :find_project, :except => [ :index, :list, :explore, :new, :create, :copy ]
+  before_filter :authorize, :except => [ :index, :list, :explore, :new, :create, :copy, :archive, :unarchive, :destroy]
   before_filter :authorize_global, :only => [:new, :create]
   before_filter :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ]
   accept_rss_auth :index
@@ -43,6 +43,8 @@
   helper :repositories
   include RepositoriesHelper
   include ProjectsHelper
+  include ActivitiesHelper
+  helper :activities
 
   # Lists visible projects. Paginator is for top-level projects only
   # (subprojects belong to them)
@@ -76,6 +78,16 @@
     end
   end
 
+  # A different view of projects using explore boxes
+  def explore
+    respond_to do |format|
+      format.html {
+        @projects = Project.visible
+        render :template => 'projects/explore.html.erb', :layout => !request.xhr?
+      }
+    end
+  end
+
   def new
     @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
     @trackers = Tracker.all
@@ -99,8 +111,7 @@
     end
     # end of code to be removed
 
-
-    if validate_parent_id && @project.save
+    if validate_is_public_key && validate_parent_id && @project.save
       @project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
       # Add current user as a project member if he is not admin
       unless User.current.admin?
@@ -288,6 +299,19 @@
     render_404
   end
 
+  def validate_is_public_key
+    # Although is_public isn't mandatory in the project model (it gets
+    # defaulted), it must be present in params -- it can be true or
+    # false, but it must be there. This permits us to make forms in
+    # which the user _has_ to select public or private (rather than
+    # defaulting it) if we want to
+    if params.nil? || params[:project].nil? || !params[:project].has_key?(:is_public)
+      @project.errors.add :is_public, :public_or_private
+      return false
+    end
+    true
+  end
+
   # Validates parent_id param according to user's permissions
   # TODO: move it to Project model in a validation that depends on User.current
   def validate_parent_id
--- a/app/controllers/welcome_controller.rb	Tue Oct 16 15:25:01 2012 +0100
+++ b/app/controllers/welcome_controller.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -25,7 +25,6 @@
     @site_project = Project.find_by_identifier "soundsoftware-site"
     @site_news = []
     @site_news = News.latest_for @site_project if @site_project
-    @projects = Project.latest User.current
     
     # tests if user is logged in to generate the tips of the day list
     if User.current.logged?
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/helpers/activities_helper.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -0,0 +1,30 @@
+
+module ActivitiesHelper
+
+  def busy_projects(events, count)
+    # Transform events list into hash from project id to number of
+    # occurrences of project in list (there is surely a tidier way
+    # to do this, e.g. chunk() in Ruby 1.9 but not in 1.8)
+    phash = events.map { |e| e.project unless !e.respond_to?(:project) }.sort.group_by { |p| p.id }
+    phash = phash.merge(phash) { |k,v| v.length }
+    threshold = phash.values.sort.last(count).first
+    busy = phash.keys.select { |k| phash[k] >= threshold }.sample(count)
+    busy.map { |pid| Project.find(pid) }
+  end
+
+  def busy_institutions(events, count)
+    authors = events.map do |e|
+      e.event_author unless !e.respond_to?(:event_author) 
+    end.compact
+    institutions = authors.map do |a|
+      if a.respond_to?(:ssamr_user_detail) and !a.ssamr_user_detail.nil?
+        a.ssamr_user_detail.institution_name
+      end
+    end
+    insthash = institutions.compact.sort.group_by { |i| i }
+    insthash = insthash.merge(insthash) { |k,v| v.length }
+    threshold = insthash.values.sort.last(count).first
+    insthash.keys.select { |k| insthash[k] >= threshold }.sample(count)
+  end
+
+end
--- a/app/helpers/attachments_helper.rb	Tue Oct 16 15:25:01 2012 +0100
+++ b/app/helpers/attachments_helper.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -42,4 +42,11 @@
       api.created_on attachment.created_on
     end
   end
+
+  # Returns true if user agent appears (approximately) to be a search
+  # bot or crawler
+  def user_is_search_bot?
+    agent = request.env['HTTP_USER_AGENT']
+    agent and agent =~ /(bot|slurp|crawler|spider)\b/i
+  end
 end
--- a/app/helpers/projects_helper.rb	Tue Oct 16 15:25:01 2012 +0100
+++ b/app/helpers/projects_helper.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -162,14 +162,12 @@
     
   end
 
-  # Renders a tree of projects that the current user does not belong
-  # to, or of all projects if the current user is not logged in.  The
-  # given collection may be a subset of the whole project tree
-  # (eg. some intermediate nodes are private and can not be seen).  We
-  # are potentially interested in various things: the project name,
-  # description, manager(s), creation date, last activity date,
-  # general activity level, whether there is anything actually hosted
-  # here for the project, etc.
+  # Renders a tree of projects.  The given collection may be a subset
+  # of the whole project tree (eg. some intermediate nodes are private
+  # and can not be seen).  We are potentially interested in various
+  # things: the project name, description, manager(s), creation date,
+  # last activity date, general activity level, whether there is
+  # anything actually hosted here for the project, etc.
   def render_project_table(projects)
 
     s = ""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/activities/_busy.html.erb	Wed Nov 21 17:56:50 2012 +0000
@@ -0,0 +1,48 @@
+<% events = @events_by_day %>
+<% if (events.nil?) 
+     activity = Redmine::Activity::Fetcher.new(User.anonymous)
+     events = activity.events(Date.today - 14, Date.today + 1)
+   end
+%>
+
+<% if events.empty? %>
+
+<% else %>
+
+   <ul>
+
+   <% 
+      for project in busy_projects(events, 5)
+   %>
+
+   <li class="busy">
+     <span class="title">
+       <% if !project.root? %>
+         <% project.ancestors.each do |p| %>
+           <%= h(p) %>&nbsp;&#187;
+         <% end %>
+       <% end %>
+       <%= link_to_project project %>
+     </span>
+     <% if !project.is_public? %>
+       <span class="private"><%= l(:field_is_private) %></span>
+     <% end %>
+     <span class='managers'>
+     <%
+	u = project.users_by_role
+	if ! u.empty? %>
+     (<%=
+	   mgmt_roles = u.keys.select{ |r| r.allowed_to?(:edit_project) }
+	   managers = mgmt_roles.map{ |r| u[r] }.flatten.sort.uniq
+	   managers.map{ |m| m.name }.join(', ')
+	 %>)<%
+	end
+	%>
+	</span>
+
+     <%= render_project_short_description project %>
+   </li>
+
+    <% end %>
+  </ul>
+<% end %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/activities/_busy_institution.html.erb	Wed Nov 21 17:56:50 2012 +0000
@@ -0,0 +1,28 @@
+<% events = @events_by_day %>
+<% if (events.nil?) 
+     activity = Redmine::Activity::Fetcher.new(User.anonymous)
+     days = Setting.activity_days_default.to_i
+     events = activity.events(Date.today - days, Date.today + 1)
+   end
+%>
+
+<% if events.empty? %>
+
+<% else %>
+
+   <ul>
+
+   <% 
+      for institution in busy_institutions(events, 5)
+   %>
+
+   <li class="busy">
+     <span class="title">
+       <%= link_to h(institution), { :controller => 'activities', :institution => institution } %>
+     </span>
+   </li>
+
+    <% end %>
+  </ul>
+<% end %>
+
--- a/app/views/activities/index.html.erb	Tue Oct 16 15:25:01 2012 +0100
+++ b/app/views/activities/index.html.erb	Wed Nov 21 17:56:50 2012 +0000
@@ -1,4 +1,14 @@
-<h2><%= @author.nil? ? l(:label_activity) : l(:label_user_activity, link_to_user(@author)) %></h2>
+<h2><%=
+  if @author.nil?
+    if @institution_name.blank?
+      l(:label_activity)
+    else
+      l(:label_institution_activity, h(@institution_name))
+    end
+  else
+    l(:label_user_activity, link_to_user(@author))
+  end
+  %></h2>
 <p class="subtitle"><%= l(:label_date_from_to, :start => format_date(@date_to - @days), :end => format_date(@date_to-1)) %></p>
 
 <div id="activity">
--- a/app/views/attachments/_form.html.erb	Tue Oct 16 15:25:01 2012 +0100
+++ b/app/views/attachments/_form.html.erb	Wed Nov 21 17:56:50 2012 +0000
@@ -2,10 +2,10 @@
   <span>
     <%= file_field_tag 'attachments[1][file]', :size => 30, :id => nil, :class => 'file',
           :onchange => "checkFileSize(this, #{Setting.attachment_max_size.to_i.kilobytes}, '#{escape_javascript(l(:error_attachment_too_big, :max_size => number_to_human_size(Setting.attachment_max_size.to_i.kilobytes)))}');"  -%>
-    <label class="inline"><%= l(:label_optional_description) %><%= text_field_tag 'attachments[1][description]', '', :size => 60, :id => nil, :class => 'description' %></label>
-    <%= link_to_function(image_tag('delete.png'), 'removeFileField(this)', :title => (l(:button_delete))) %>
+    <nobr><label class="inline"><%= l(:label_optional_description) %><%= text_field_tag 'attachments[1][description]', '', :size => 60, :id => nil, :class => 'description' %></label>
+    <%= link_to_function(image_tag('delete.png'), 'removeFileField(this)', :title => (l(:button_delete))) %></nobr>
   </span>
 </span>
-<small><%= link_to l(:label_add_another_file), '#', :onclick => 'addFileField(); return false;' %>
+<br><small><%= link_to l(:label_add_another_file), '#', :onclick => 'addFileField(); return false;' %>
 (<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)
 </small>
--- a/app/views/attachments/_links.html.erb	Tue Oct 16 15:25:01 2012 +0100
+++ b/app/views/attachments/_links.html.erb	Wed Nov 21 17:56:50 2012 +0000
@@ -2,7 +2,7 @@
 <% for attachment in attachments %>
 <p><%= link_to_attachment attachment, :class => 'icon icon-attachment' -%>
 <%= h(" - #{attachment.description}") unless attachment.description.blank? %>
-  <span class="size">(<%= number_to_human_size attachment.filesize %>)</span>
+  <span class="size_and_count"><%= number_to_human_size attachment.filesize %><%= ", " + l(:label_x_downloads, :count => attachment.downloads) unless attachment.downloads == 0 %></span>
   <% if options[:deletable] %>
     <%= link_to image_tag('delete.png'), attachment_path(attachment),
                                          :confirm => l(:text_are_you_sure),
--- a/app/views/projects/_form.html.erb	Tue Oct 16 15:25:01 2012 +0100
+++ b/app/views/projects/_form.html.erb	Wed Nov 21 17:56:50 2012 +0000
@@ -21,9 +21,20 @@
 <br />
   <em> <%= l(:text_project_homepage_info) %></em>
 </p>
-<p><%= f.check_box :is_public %>
+<p>
+<%= label(:project, :is_public_1, l(:field_public_or_private) + content_tag("span", " *", :class => "required")) %>
+<%
+   # if the project hasn't been created fully yet, then we don't
+   # want to set either public or private (make the user decide)
+   initialised = !@project.id.nil?
+%>
+  <%= f.radio_button :is_public, 1, :checked => (initialised && @project.is_public?) %>
+  <%= l(:text_project_public_info) %>
 <br />
-  <em> <%= l(:text_project_visibility_info) %></em>
+  <%= f.radio_button :is_public, 0, :checked => (initialised && !@project.is_public?) %> 
+  <%= l(:text_project_private_info) %>
+<br>
+  <em><%= l(:text_project_visibility_info) %></em>
 </p>
 <%= wikitoolbar_for 'project_description' %>
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/projects/_latest.html.erb	Wed Nov 21 17:56:50 2012 +0000
@@ -0,0 +1,19 @@
+    <ul>
+    <% for project in Project.latest(User.current) %>
+	<li class="latest">
+	<span class="title">
+	  <% if !project.root? %>
+	    <% project.ancestors.each do |p| %>
+	      <%= h(p) %>&nbsp;&#187;
+	    <% end %>
+	  <% end %>
+	<%= link_to_project project %>
+	</span>
+	<% if !project.is_public? %>
+	   <span class="private"><%= l(:field_is_private) %></span>
+	<% end %>
+	<span class="time"><%= format_time(project.created_on)%></span>
+	<%= render_project_short_description project %>
+      </li>
+    <% end %>
+    </ul>
--- a/app/views/projects/_members_box.html.erb	Tue Oct 16 15:25:01 2012 +0100
+++ b/app/views/projects/_members_box.html.erb	Wed Nov 21 17:56:50 2012 +0000
@@ -1,8 +1,8 @@
   <% if @users_by_role.any? %>
-  <div class="members box">
+  <div id="memberbox"><div class="box">
     <h3><%=l(:label_member_plural)%></h3>
     <p><% @users_by_role.keys.sort.each do |role| %>
     <%=h role %>: <%= @users_by_role[role].sort.collect{|u| link_to_user u}.join(", ") %><br />
     <% end %></p>
-  </div>
+  </div></div>
   <% end %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/projects/explore.html.erb	Wed Nov 21 17:56:50 2012 +0000
@@ -0,0 +1,32 @@
+<% content_for :header_tags do %>
+    <%= stylesheet_link_tag 'redmine_tags', :plugin => 'redmine_tags' %>
+<% end %>
+
+<% cache(:action => 'explore', :action_suffix => 'tags', :expires_in => 1.hour) do %>
+<h2><%= l(:label_explore_projects) %></h2>
+  <div class="tags box">
+  <h3><%=l(:label_project_tags_all)%></h3>
+    <%= render :partial => 'projects/tagcloud' %>
+  </div>
+<% end %>
+
+<div class="splitcontentleft">
+  <% cache(:action => 'explore', :action_suffix => 'busy_institutions', :expires_in => 1.hour) do %>
+  <div class="institutions box">
+  <h3><%=l(:label_institutions_busy)%></h3>
+    <%= render :partial => 'activities/busy_institution' %>
+  </div>
+  <% end %>
+  <div class="projects box">
+  <h3><%=l(:label_project_latest)%></h3>
+    <%= render :partial => 'projects/latest' %>
+  </div>
+</div>
+<div class="splitcontentright">
+  <% cache(:action => 'explore', :action_suffix => 'busy_projects', :expires_in => 1.hour) do %>
+  <div class="projects box">
+  <h3><%=l(:label_projects_busy)%></h3>
+    <%= render :partial => 'activities/busy' %>
+  </div>
+  <% end %>
+</div>
--- a/app/views/projects/new.html.erb	Tue Oct 16 15:25:01 2012 +0100
+++ b/app/views/projects/new.html.erb	Wed Nov 21 17:56:50 2012 +0000
@@ -3,6 +3,5 @@
 <% labelled_tabular_form_for @project do |f| %>
 <%= render :partial => 'form', :locals => { :f => f } %>
 <%= submit_tag l(:button_create) %>
-<%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
 <%= javascript_tag "Form.Element.focus('project_name');" %>
 <% end %>
--- a/app/views/welcome/index.html.erb	Tue Oct 16 15:25:01 2012 +0100
+++ b/app/views/welcome/index.html.erb	Wed Nov 21 17:56:50 2012 +0000
@@ -14,40 +14,17 @@
 
 <div class="splitcontentright">
   <% if @site_news.any? %>
-  <div class="news box">
-	<h3><%=l(:label_news_site_latest)%></h3>
+    <div class="news box">
+       <h3><%=l(:label_news_site_latest)%></h3>
 	<%= render :partial => 'news/news', :locals => { :project => @site_project }, :collection => @site_news %>
-	
 	<%= link_to l(:label_news_more), { :controller => 'projects', :action => @site_project.identifier, :id => 'news' } %>
   </div>
   <% end %>
-    <% if @projects.any? %>
   <div class="projects box">
   <h3><%=l(:label_project_latest)%></h3>
-    <ul>
-    <% for project in @projects %>
-      <% @project = project %>
-	<li class="latest">
-	<span class="title">
-	  <% if !project.root? %>
-	    <% project.ancestors.each do |p| %>
-	      <%= h(p) %>&nbsp;&#187;
-	    <% end %>
-	  <% end %>
-	<%= link_to_project project %>
-	</span>
-	<% if !project.is_public? %>
-	   <span class="private"><%= l(:field_is_private) %></span>
-	<% end %>
-	<span class="time"><%= format_time(project.created_on)%></span>
-	<%= render_project_short_description project %>
-      </li>
-    <% end %>
-    <% @project = nil %>
-    </ul>
-	<%= link_to l(:label_projects_more), :controller => 'projects' %>
+    <%= render :partial => 'projects/latest' %>
+    <%= link_to l(:label_projects_more), :controller => 'projects' %>
   </div>
-  <% end %>
     <%= call_hook(:view_welcome_index_right, :projects => @projects) %>
 </div>
 
--- a/config/environment.rb	Tue Oct 16 15:25:01 2012 +0100
+++ b/config/environment.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -36,7 +36,10 @@
 
   # Enable page/fragment caching by setting a file-based store
   # (remember to create the caching directory and make it readable to the application)
-  # config.action_controller.cache_store = :file_store, "#{RAILS_ROOT}/tmp/cache"
+  config.action_controller.cache_store = :file_store, "#{RAILS_ROOT}/tmp/cache"
+
+  # And for direct uses of the cache
+  config.cache_store = :file_store, "#{RAILS_ROOT}/tmp/cache"
 
   # Activate observers that should always be running
   # config.active_record.observers = :cacher, :garbage_collector
--- a/config/locales/en.yml	Tue Oct 16 15:25:01 2012 +0100
+++ b/config/locales/en.yml	Wed Nov 21 17:56:50 2012 +0000
@@ -130,6 +130,7 @@
         circular_dependency: "This relation would create a circular dependency"
         cant_link_an_issue_with_a_descendant: "An issue cannot be linked to one of its subtasks"
         must_accept_terms_and_conditions: "You must accept the Terms and Conditions"        
+        public_or_private: "You must select either public or private"
 
   actionview_instancetag_blank_option: Please select
 
@@ -328,6 +329,7 @@
   field_root_directory: Root directory
   field_cvsroot: CVSROOT
   field_cvs_module: Module
+  field_public_or_private: "Public or Private?"
 
   setting_external_repository: "Select this if the project's main repository is hosted somewhere else"
   setting_external_repository_url: "The URL of the existing external repository. Must be publicly accessible without a password"
@@ -487,9 +489,16 @@
     zero:  no projects
     one:   1 project
     other: "%{count} projects"
+  label_x_downloads:
+    zero:  never downloaded
+    one:   downloaded once
+    other: "downloaded %{count} times"
   label_project_all: All Projects
   label_project_latest: Latest projects
   label_projects_more: More projects
+  label_project_tags_all: Popular tags
+  label_projects_busy: Busy projects
+  label_institutions_busy: Active institutions
   label_managers: Managed by
   label_issue: Issue
   label_issue_new: New issue
@@ -539,7 +548,7 @@
   label_home: Home
   label_home_heading: Welcome!
   label_my_page: My page
-  label_my_account: My account
+  label_my_account: Account
   label_my_projects: My projects
   label_my_page_block: My page block
   label_administration: Administration
@@ -556,6 +565,7 @@
   label_activity_my_recent_none: No recent activity
   label_overall_activity: Overall activity
   label_user_activity: "%{value}'s activity"
+  label_institution_activity: "Activity from %{value}"
   label_new: New
   label_logged_as: Logged in as
   label_environment: Environment
@@ -685,7 +695,7 @@
   label_repository: Repository
   label_is_external_repository: Track an external repository
   label_repository_plural: Repositories
-  label_browse: Browse
+  label_explore_projects: Explore projects
   label_modification: "%{count} change"
   label_modification_plural: "%{count} changes"
   label_branch: Branch
@@ -883,7 +893,7 @@
   button_expand_all: Expand all
   button_delete: Delete
   button_create: Create
-  button_create_and_continue: Create and continue
+  button_create_and_continue: Create and Add Another
   button_test: Test
   button_edit: Edit
   button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}"
@@ -958,7 +968,9 @@
   text_caracters_minimum: "Must be at least %{count} characters long."
   text_length_between: "Length between %{min} and %{max} characters."
   text_project_name_info: "This will be the name of your project as it appears throughout this site.<br /> You can change it at any time, in the project's settings."
-  text_project_visibility_info: "If your project is not public, it will be visible only to you and to users that you have added as project members.<br/>You can change this later if you wish."
+  text_project_public_info: "Public: visible to anybody browsing the site."
+  text_project_private_info: "Private: visible only to you, and to users you have added as project members."
+  text_project_visibility_info: "You can change whether your project is public or private later if you wish."
   text_user_ssamr_description_info: 'Please describe your current research or development interests.<br/>This information will be used at registration to determine that you are a real person &ndash; so please be descriptive, or your application may be delayed or rejected.<br/>After registration, the description is publicly visible in your profile and you can edit it at any time.'
   text_issue_parent_issue_info: 'If this is a subtask, please insert its parent task number or write the main task name.'
 
--- a/config/routes.rb	Tue Oct 16 15:25:01 2012 +0100
+++ b/config/routes.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -32,6 +32,8 @@
   # TODO: wasteful since this is also nested under issues, projects, and projects/issues
   map.resources :time_entries, :controller => 'timelog'
 
+  map.connect 'explore', :controller => 'projects', :action => 'explore'
+
   map.connect 'projects/:id/wiki', :controller => 'wikis', :action => 'edit', :conditions => {:method => :post}
   map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :get}
   map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :post}
--- a/extra/soundsoftware/extract-javadoc.sh	Tue Oct 16 15:25:01 2012 +0100
+++ b/extra/soundsoftware/extract-javadoc.sh	Wed Nov 21 17:56:50 2012 +0000
@@ -35,8 +35,8 @@
 # package declarations
 
 find "$projectdir" -type f -name \*.java \
-    -exec grep '^ *package [a-zA-Z][a-zA-Z0-9\._-]*; *$' \{\} /dev/null \; |
-    sed -e 's/\/[^\/]*: *package */:/' -e 's/; *$//' |
+    -exec egrep '^ *package +[a-zA-Z][a-zA-Z0-9\._-]*;.*$' \{\} /dev/null \; |
+    sed -e 's/\/[^\/]*: *package */:/' -e 's/;.*$//' |
     sort | uniq | (
 	current_prefix=
 	current_packages=
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/get-apache-log-stats.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -0,0 +1,179 @@
+
+# Read an Apache log file in SoundSoftware site format from stdin and
+# produce some per-project stats.
+#
+# Invoke with e.g.
+#
+# cat /var/log/apache2/code-access.log | \
+#   script/runner -e production extra/soundsoftware/get-apache-log-stats.rb
+
+
+# Use the ApacheLogRegex parser, a neat thing
+# See http://www.simonecarletti.com/blog/2009/02/apache-log-regex-a-lightweight-ruby-apache-log-parser/
+require 'apachelogregex'
+
+# This is the format defined in our httpd.conf
+vhost_combined_format = '%v:%p %h %{X-Forwarded-For}i %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"'
+
+parser = ApacheLogRegex.new(vhost_combined_format)
+
+# project name -> count of hg clones
+clones = Hash.new(0)
+
+# project name -> count of hg pulls
+pulls = Hash.new(0)
+
+# project name -> count of hg pushes
+pushes = Hash.new(0)
+
+# project name -> count of hg archive requests (i.e. Download as Zip)
+zips = Hash.new(0)
+
+# project name -> count of hits to pages under /projects/projectname
+hits = Hash.new(0)
+
+# project name -> Project object
+@projects = Hash.new
+
+parseable = 0
+unparseable = 0
+
+def is_public_project?(project)
+  if !project
+    false
+  elsif project =~ /^\d+$/
+    # ignore numerical project ids, they are only used when editing projects
+    false
+  elsif @projects.key?(project)
+    @projects[project].is_public? 
+  else
+    pobj = Project.find_by_identifier(project)
+    if pobj
+      @projects[project] = pobj
+      pobj.is_public?
+    else
+      print "Project not found: ", project, "\n"
+      false
+    end
+  end
+end
+
+def print_stats(h)
+  h.keys.sort { |a,b| h[b] <=> h[a] }.each do |p|
+    if h[p] > 0
+      print h[p], " ", @projects[p].name, " [", p, "]\n"
+    end
+  end
+end
+
+STDIN.each do |line|
+
+  record = parser.parse(line)
+
+  # most annoyingly, the parser can't handle the comma-separated list
+  # in X-Forwarded-For where it has more than one element. If it has
+  # failed, remove any IP addresses or the word "unknown" with
+  # trailing commas and try again
+  if not record
+    filtered = line.gsub(/(unknown|([0-9]+\.){3}[0-9]+),\s*/, "")
+    record = parser.parse(filtered)
+  end
+
+  # discard, but count, unparseable lines
+  if not record
+    print "Line not parseable: ", line, "\n"
+    unparseable += 1
+    next
+  end
+
+  # discard everything that isn't a 200 OK response
+  next if record["%>s"] != "200"
+
+  # discard anything apparently requested by a crawler
+  next if record["%{User-Agent}i"] =~ /(bot|slurp|crawler|spider|Redmine)\b/i
+
+  # pull out request e.g. GET / HTTP/1.0
+  request = record["%r"]
+
+  # split into method, path, protocol
+  if not request =~ /^[^\s]+ ([^\s]+) [^\s]+$/
+    print "Line not parseable (bad method, path, protocol): ", line, "\n"
+    unparseable += 1
+    next
+  end
+
+  # get the path e.g. /projects/weevilmatic and split on /
+  path = $~[1]
+  components = path.split("/")
+  
+  # should have at least two elements unless path is "/"; first should
+  # be empty (begins with /)
+  if path != "/" and (components.size < 2 or components[0] != "")
+    print "Line not parseable (degenerate path): ", line, "\n"
+    unparseable += 1
+    next
+  end
+
+  if components[1] == "hg"
+    
+    # path is /hg/project?something or /hg/project/something
+
+    project = components[2].split("?")[0]
+    if not is_public_project?(project)
+      next
+    end
+
+    if components[2] =~ /&roots=00*$/
+      clones[project] += 1
+    elsif components[2] =~ /cmd=capabilities/
+      pulls[project] += 1
+    elsif components[2] =~ /cmd=unbundle/
+      pushes[project] += 1
+    elsif components[3] == "archive"
+      zips[project] += 1
+    end
+
+  elsif components[1] == "projects"
+
+    # path is /projects/project or /projects/project/something
+
+    project = components[2]
+    project = project.split("?")[0] if project
+    if not is_public_project?(project)
+      next
+    end
+
+    hits[project] += 1
+
+  end
+
+  parseable += 1
+end
+
+# Each clone is also a pull; deduct it from the pulls hash, because we
+# want that to contain only non-clone pulls
+
+clones.keys.each do |project|
+  pulls[project] -= 1
+end
+
+print parseable, " parseable\n"
+print unparseable, " unparseable\n"
+
+
+print "\nMercurial clones:\n"
+print_stats clones
+
+print "\nMercurial pulls (excluding clones):\n"
+print_stats pulls
+
+print "\nMercurial pushes:\n"
+print_stats pushes
+
+print "\nMercurial archive (zip file) downloads:\n"
+print_stats zips
+
+print "\nProject page hits (excluding crawlers):\n"
+print_stats hits
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/get-statistics.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -0,0 +1,147 @@
+# this script will get stats from the repo and print them to stdout
+
+# USAGE: 
+
+# ./script/runner -e production extra/soundsoftware/get-statistics.rb 
+#
+
+d1 = Date.parse("20100701") # => 1 Jul 2010
+d2 = Date.today
+
+def delta_array (iarray)
+  # returns an array with the deltas
+  ## prepends a zero and drops the last element
+  deltas = [0] + iarray
+  deltas = deltas.first(deltas.size - 1)
+
+  return iarray.zip(deltas).map { |x, y| x - y }
+
+end
+
+def months_between(d1, d2)
+   months = []
+   start_date = Date.civil(d1.year, d1.month, 1)
+   end_date = Date.civil(d2.year, d2.month, 1)
+
+   raise ArgumentError unless d1 <= d2
+
+   while (start_date < end_date)
+     months << start_date
+     start_date = start_date >>1
+   end
+
+   months << end_date
+end
+
+def weeks_between(d1, d2)
+   weeks = []
+   start_date = Date.civil(d1.year, d1.month, d1.day)
+   end_date = Date.civil(d2.year, d2.month, d2.day)
+
+   raise ArgumentError unless d1 <= d2
+
+   while (start_date < end_date)
+     weeks << start_date
+     start_date = start_date + 2.week
+   end
+
+   weeks << end_date
+end
+
+def get_user_project_evol_stats()
+  # dates = months_between(d1, d2)
+  dates = months_between(d1, d2)
+  
+  # number of users 
+  n_users = []
+  n_projects = []
+  qm_users = []
+  
+  dates.each do |date|
+    users =  User.find_by_sql ["SELECT * FROM users WHERE users.status = '1' AND users.created_on <= ?;", date]
+    projects =  Project.find_by_sql ["SELECT * FROM projects WHERE projects.created_on <= ?;", date]
+    
+    qm_users_list = User.find_by_sql ["SELECT * FROM users,ssamr_user_details WHERE users.status = '1' AND ssamr_user_details.user_id = users.id AND (users.mail like '%qmul%' OR ssamr_user_details.institution_id = '99') AND users.created_on <= ?;", date ]
+    
+    qm_users << qm_users_list.count
+    n_users << users.count
+    n_projects << projects.count
+    
+    #  private_projects =  Project.find(:all, :conditions => {:created_on  => d1..date, is_public => false})
+  end
+  
+  user_deltas = delta_array(n_users)
+  proj_deltas = delta_array(n_projects)
+  qm_user_deltas = delta_array(qm_users)
+  
+  puts "Date Users D_Users QM_Users D_QM_users Projects D_Projects"
+  
+  dates.zip(n_users, user_deltas, qm_users, qm_user_deltas, n_projects, proj_deltas).each do |a, b, c, d, e, f, g|
+    puts "#{a} #{b} #{c} #{d} #{e} #{f} #{g}"
+  end
+  
+end
+
+
+def get_project_status()
+  date = "20121101"
+  
+   all_projects = Project.find(:all, :conditions => ["created_on < ?", date])
+  #  all_projects = Project.find(:all, :conditions => ["is_public = ? AND created_on < ?", true, date])
+#  all_projects = Project.find(:all, :conditions => ["is_public = ? AND created_on < ?", false, date])
+  
+  collab = []
+  users_per_proj = []
+  
+  #  puts "Public Users Institutions"
+
+  all_projects.each do |proj| 
+    insts = []
+
+    proj.users.each do |u|  
+      if u.institution == "" || u.institution == "No Institution Set"
+        if u.mail.include?("qmul.ac.uk") || u.mail.include?("andrewrobertson77")
+          insts << "Queen Mary, University of London"          
+        else
+          insts << u.mail
+        end
+      else
+        insts << u.institution
+      end
+    end
+
+    users_per_proj << proj.users.count
+    collab << insts.uniq.count
+  end
+  
+  
+  #  freq = collab.inject(Hash.new(0)) { |h,v| h[v] += 1; h }
+  #  freq = freq.sort_by {|key, value| value}
+  #  puts freq.inspect.sort
+
+  puts "Projects: #{all_projects.count} UpP: #{users_per_proj.sum / users_per_proj.size.to_f} Users1+: #{users_per_proj.count{|x| x> 1}} Users2+: #{users_per_proj.count{|x| x> 2}} Collab1+: #{collab.count{|x| x > 1}} Collab2+: #{collab.count{|x| x > 2}} IpP: #{collab.sum / collab.size.to_f}"
+end
+
+def get_user_projects_ratios()
+  user_projects = User.find(:all, :conditions=> {:status => 1})
+  pub_proj_user = user_projects.map{|u| u.projects.find(:all, :conditions=>{:is_public => true}).count}
+
+  user_projects.zip(pub_proj_user).each do |u, pub|
+      puts "#{u.projects.count} #{pub}"
+  end
+
+end
+
+def get_inst_list()
+  users = User.find(:all, :conditions => {:status => 1})
+  inst_list = users.map{|user| user.institution}
+  
+  freq = inst_list.inject(Hash.new(0)) { |h,v| h[v] += 1; h }
+  
+end
+
+
+# get_user_projects_ratios()
+# get_user_project_evol_stats()
+
+get_project_status()
--- a/public/help/wiki_syntax.html	Tue Oct 16 15:25:01 2012 +0100
+++ b/public/help/wiki_syntax.html	Wed Nov 21 17:56:50 2012 +0000
@@ -44,11 +44,15 @@
 <tr><th><img src="../images/jstoolbar/bt_h2.png" style="border: 1px solid #bbb;" alt="Heading 2" /></th><td>h2. Title 2</td><td><h2>Title 2</h2></td></tr>
 <tr><th><img src="../images/jstoolbar/bt_h3.png" style="border: 1px solid #bbb;" alt="Heading 3" /></th><td>h3. Title 3</td><td><h3>Title 3</h3></td></tr>
 
+<tr><th colspan="3">Table of contents</th></tr>
+<tr><th></th><td><pre>{{toc}}</pre></td><td>Left-aligned table of contents</td></tr>
+<tr><th></th><td><pre>{{>toc}}</pre></td><td>Right-aligned table of contents</td></tr>
+
 <tr><th colspan="3">Links</th></tr>
 <tr><th></th><td>http://foo.bar</td><td><a href="#">http://foo.bar</a></td></tr>
 <tr><th></th><td>"Foo":http://foo.bar</td><td><a href="#">Foo</a></td></tr>
 
-<tr><th colspan="3">Redmine links</th></tr>
+<tr><th colspan="3">Magic links</th></tr>
 <tr><th><img src="../images/jstoolbar/bt_link.png" style="border: 1px solid #bbb;" alt="Link to a Wiki page" /></th><td>[[Wiki page]]</td><td><a href="#">Wiki page</a></td></tr>
 <tr><th></th><td>Issue #12</td><td>Issue <a href="#">#12</a></td></tr>
 <tr><th></th><td>Revision r43</td><td>Revision <a href="#">r43</a></td></tr>
--- a/public/stylesheets/application.css	Tue Oct 16 15:25:01 2012 +0100
+++ b/public/stylesheets/application.css	Wed Nov 21 17:56:50 2012 +0000
@@ -5,7 +5,7 @@
 h1 {margin:0; padding:0; font-size: 24px;}
 h2, .wiki h1 {font-size: 20px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
 h3, .wiki h2 {font-size: 16px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
-h4, .wiki h3 {font-size: 13px;padding: 2px 10px 1px 0px;margin-bottom: 5px; border-bottom: 1px dotted #bbbbbb; color: #444;}
+h4, .wiki h3 {font-size: 14px;padding: 2px 10px 1px 0px;margin-bottom: 5px; border-bottom: 1px dotted #bbbbbb; color: #444;}
 
 /***** Layout *****/
 #wrapper {background: white;}
@@ -24,14 +24,14 @@
 
 #account {float:right;}
 
-#header {height:5.3em;margin:0;background-color:#507AAA;color:#f8f8f8; padding: 4px 8px 0px 6px; position:relative;}
+#header {height:68px;margin:0;background-color:#507AAA;color:#f8f8f8; padding: 4px 8px 0px 6px; position:relative;}
 #header a {color:#f8f8f8;}
 #header h1 a.ancestor { font-size: 80%; }
 
 #project-search-jump {float:right; }
 
 
-#main-menu {position: absolute;  bottom: 0px;  left:6px; margin-right: -500px;}
+#main-menu {position: absolute;  bottom: 0px;  left:8px; margin-right: -500px;}
 #main-menu ul {margin: 0;  padding: 0;}
 #main-menu li {
   float:left;
@@ -240,6 +240,8 @@
 div.members h3 { background: url(../images/group.png) no-repeat 0% 50%; padding-left: 20px; }
 div.news h3 { background: url(../images/news.png) no-repeat 0% 50%; padding-left: 20px; }
 div.projects h3 { background: url(../images/projects.png) no-repeat 0% 50%; padding-left: 20px; }
+div.tags h3 { background: url(../images/ticket_note.png) no-repeat 0% 50%; padding-left: 20px; }
+div.institutions h3 { background: url(../images/group.png) no-repeat 0% 50%; padding-left: 20px; }
 
 #watchers ul {margin: 0;  padding: 0;}
 #watchers li {list-style-type:none;margin: 0px 2px 0px 0px; padding: 0px 0px 0px 0px;}
@@ -254,17 +256,17 @@
 .highlight.token-3 { background-color: #aaf;}
 
 .box{
-padding:6px;
-margin-bottom: 10px;
-background-color:#f6f6f6;
-color:#505050;
-line-height:1.5em;
-border: 1px solid #e4e4e4;
+    padding:6px;
+    margin-bottom: 10px;
+    background-color:#f6f6f6;
+    color:#505050;
+    line-height:1.5em;
+    border: 1px solid #e4e4e4;
 }
 
 .box h4 {
-margin-top: 0;
-padding-top: 0;
+    margin-top: 0;
+    padding-top: 0;
 }
 
 div.square {
@@ -342,6 +344,25 @@
 div#members dt .email { color: #777; font-size: 80%; }
 div#members dd .roles { font-style: italic; }
 
+div#memberbox h3 { 
+  background: url(../images/group.png) no-repeat 0% 50%; 
+  padding-left: 20px;
+}
+div#memberbox p { 
+  padding-left: 20px;
+  margin-left: 0;
+}
+
+div.issues ul {
+  padding-left: 20px;
+  margin-left: 0;
+  list-style-type: none;
+}
+div.issues p {
+  padding-left: 20px;
+  margin-left: 0;
+}
+
 .projects .latest .title { margin-right: 0.5em; }
 .tipoftheday .tip { margin-left: 2em; margin-top: 0.5em; }
 
@@ -500,13 +521,14 @@
 .summary {font-style: italic;}
 
 #attachments_fields input[type=text] {margin-left: 8px; }
-#attachments_fields span {display:block; white-space:nowrap;}
+/*#attachments_fields span {display:block; white-space:nowrap;} */
 #attachments_fields img {vertical-align: middle;}
 
 div.attachments { margin-top: 12px; }
 div.attachments p { margin:4px 0 2px 0; }
 div.attachments img { vertical-align: middle; }
-div.attachments span.author { font-size: 0.9em; color: #888; }
+div.attachments span.author { font-size: 0.9em; color: #888; font-style: italic; padding-left: 4px }
+div.attachments span.size_and_count { font-size: 0.9em; color: #888; padding-left: 4px; }
 
 p.other-formats { text-align: right; font-size:0.9em; color: #666; }
 .other-formats span + span:before { content: "| "; }
--- a/public/themes/soundsoftware/stylesheets/application.css	Tue Oct 16 15:25:01 2012 +0100
+++ b/public/themes/soundsoftware/stylesheets/application.css	Wed Nov 21 17:56:50 2012 +0000
@@ -34,7 +34,7 @@
 
 h2,h3,h4,.wiki h1 {
   color: #3e442c;
-  font-weight: bold;
+/*  font-weight: bold; */
 }
 
 .wiki h2,.wiki h3,.wiki h4 {
@@ -119,14 +119,14 @@
 #quick-search { margin-right: 6px; margin-top: 1em; color: #000; }
 #project-jump-box { float: right;  margin-right: 6px; margin-top: 5px; color: #000; }
 #project-ancestors-title {
-    margin-bottom: 0px;
+    margin-bottom: -6px;
     margin-left: 12px;
     margin-top: 6px;
 }
 
-#main-menu { position: absolute; top: 100px; /* background-color: #be5700; */ left: 0; border-top: 0; width: 100%;/* height: 1.82em; */ padding: 0; margin: 0; border: 0; }
+#main-menu { position: absolute; top: 100px; /* background-color: #be5700; */ left: 2px; border-top: 0; width: 100%;/* height: 1.82em; */ padding: 0; margin: 0; border: 0; }
 #main-menu li { margin: 0; padding: 0; }
-#main-menu li a { background-color: #fdfbf5; color: #be5700; border-right: 1px solid #a9b680; font-size: 97%; padding: 0em 8px 0.2em 10px; font-weight: normal; }
+#main-menu li a { background-color: #fdfbf5; color: #be5700; border-right: 1px solid #a9b680; font-size: 97%; padding: 0em 8px 0em 10px; font-weight: normal; }
 #main-menu li:last-child a { border-right: 0; }
 #main-menu li a:hover { background-color: #fdfbf5; color: #be5700; text-decoration: underline; }
 #main-menu li a.selected, #main-menu li a.selected:hover { background-color: #fdfbf5; color: #3e442c; }
@@ -145,7 +145,11 @@
 h2, h3, h4, .wiki h1, .wiki h2, .wiki h3 { border-bottom: 0px; }
 /*h2, .wiki h1 { letter-spacing:-1px; }
 */
-h4 { border-bottom: dotted 1px #c0c0c0; }
+/* h4 { border-bottom: dotted 1px #c0c0c0; } */
+
+.wiki p, .wiki li { margin-left: 30px; margin-right: 3em; }
+
+.repository-info .wiki p { margin-left: 0 }
 
 div.issue { background: #fdfaf0; border: 1px solid #a9b680; border-left: 4px solid #a9b680; }
 
--- a/public/themes/soundsoftware/stylesheets/fonts-generic.css	Tue Oct 16 15:25:01 2012 +0100
+++ b/public/themes/soundsoftware/stylesheets/fonts-generic.css	Wed Nov 21 17:56:50 2012 +0000
@@ -1,12 +1,24 @@
-@import url(fonts.css);
+@import url(fonts.css?2);
 
-h1, #project-ancestors-title {
-  font-family: GilliusADFNo2, 'Gill Sans', Tahoma, sans-serif;
-  font-weight: normal;
+h1, #project-ancestors-title, #top-menu a {
+  font-family: Insider, 'Gill Sans', Tahoma, sans-serif;
+  font-weight: bold;
 }    
 
-body,p,h2,h3,h4,li,table,.wiki h1,.embedded h1 { 
-  font-family: DroidSans, 'Liberation Sans', tahoma, verdana, sans-serif; 
+#top-menu div, #top-menu li {
+  font-size: 12px;
+}
+
+body,p,li,table { 
+  font-family: Insider, DroidSans, 'Liberation Sans', tahoma, verdana, sans-serif; 
+  font-size: 14px;
+  line-height: 1.34;
+  font-weight: normal;
+}
+
+h2,h3,h4,.wiki h1,.embedded h1 { 
+  font-family: Insider, DroidSans, 'Liberation Sans', tahoma, verdana, sans-serif; 
+  font-weight: bold;
   line-height: 1.34;
 }
 
--- a/public/themes/soundsoftware/stylesheets/fonts-mac.css	Tue Oct 16 15:25:01 2012 +0100
+++ b/public/themes/soundsoftware/stylesheets/fonts-mac.css	Wed Nov 21 17:56:50 2012 +0000
@@ -1,11 +1,24 @@
-@import url(fonts.css);
+@import url(fonts.css?2);
 
-h1, #project-ancestors-title {
-  font-family: GilliusADFNo2, 'Gill Sans', Tahoma, sans-serif;
-  font-weight: normal;
+h1, #project-ancestors-title, #top-menu a {
+  font-family: Insider, "Lucida Grande", sans-serif;
+  font-weight: bold;
 }    
 
-body,p,h2,h3,h4,li,table,.wiki h1,.embedded h1 { 
-  font-family: 'Lucida Grande', 'Lucida Sans Unicode', DroidSans, 'Liberation Sans', tahoma, verdana, sans-serif; 
+#top-menu div, #top-menu li {
+  font-size: 12px;
+}
+
+body,p,li,table { 
+  font-family: Insider, "Lucida Grande", sans-serif;
+  font-size: 14px;
+  line-height: 1.34;
+  font-weight: normal;
+}
+
+h2,h3,h4,.wiki h1,.embedded h1 { 
+  font-family: Insider, "Lucida Grande", sans-serif;
+  font-weight: bold;
   line-height: 1.34;
 }
+
--- a/public/themes/soundsoftware/stylesheets/fonts-ms.css	Tue Oct 16 15:25:01 2012 +0100
+++ b/public/themes/soundsoftware/stylesheets/fonts-ms.css	Wed Nov 21 17:56:50 2012 +0000
@@ -1,11 +1,26 @@
-@import url(fonts.css);
+@import url(fonts.css?2);
 
-h1, #project-ancestors-title {
-  font-family: GilliusADFNo2, 'Gill Sans', Tahoma, sans-serif;
+/* IE likes us to separate out normal & bold into different fonts
+   rather than use the selectors on the same font */
+
+h1, #project-ancestors-title, #top-menu a {
+  font-family: Insider-Medium, Tahoma, sans-serif;
   font-weight: normal;
 }    
 
-body,p,h2,h3,h4,li,table,.wiki h1,.embedded h1 { 
-  font-family: Calibri, DroidSans, 'Liberation Sans', tahoma, verdana, sans-serif; 
+#top-menu div, #top-menu li {
+  font-size: 12px;
+}
+
+body,p,li,table { 
+  font-family: Insider-Regular, tahoma, verdana, sans-serif; 
+  font-size: 14px;
+  line-height: 1.34;
+  font-weight: normal;
+}
+
+h2,h3,h4,.wiki h1,.embedded h1 { 
+  font-family: Insider-Medium, tahoma, verdana, sans-serif; 
+  font-weight: normal;
   line-height: 1.34;
 }
--- a/public/themes/soundsoftware/stylesheets/fonts.css	Tue Oct 16 15:25:01 2012 +0100
+++ b/public/themes/soundsoftware/stylesheets/fonts.css	Wed Nov 21 17:56:50 2012 +0000
@@ -1,51 +1,32 @@
-
-/* Font pack generated by FontSquirrel */
 
 @font-face {
-	font-family: 'GilliusADFNo2';
-	src: url('fonts/gilliusadfno2-bolditalic-webfont.eot');
-	src: local('☺'), url('fonts/gilliusadfno2-bolditalic-webfont.woff') format('woff'), url('fonts/gilliusadfno2-bolditalic-webfont.ttf') format('truetype'), url('fonts/GilliusADFNo2-BoldItalic.otf') format('opentype'), url('fonts/gilliusadfno2-bolditalic-webfont.svg#webfontLmhvPwzc') format('svg');
-	font-weight: bold;
-	font-style: italic;
+    font-family: 'Insider';
+    font-weight: bold;
+    font-style: normal;
+    src: url('fonts/24BC0E_0_0.eot');
+    src: url('fonts/24BC0E_0_0.eot?#iefix') format('embedded-opentype'), url('fonts/24BC0E_0_0.woff') format('woff'), url('fonts/24BC0E_0_0.ttf') format('truetype');
+}
+ 
+@font-face {
+    font-family: 'Insider';
+    font-weight: normal;
+    font-style: normal;
+    src: url('fonts/24BC35_0_0.eot');
+    src: url('fonts/24BC35_0_0.eot?#iefix') format('embedded-opentype'), url('fonts/24BC35_0_0.woff') format('woff'), url('fonts/24BC35_0_0.ttf') format('truetype');
 }
 
 @font-face {
-	font-family: 'GilliusADFNo2';
-	src: url('fonts/gilliusadfno2-italic-webfont.eot');
-	src: local('☺'), url('fonts/gilliusadfno2-italic-webfont.woff') format('woff'), url('fonts/gilliusadfno2-italic-webfont.ttf') format('truetype'), url('fonts/GilliusADFNo2-Italic.otf') format('opentype'), url('fonts/gilliusadfno2-italic-webfont.svg#webfonteHBtzgS0') format('svg');
-	font-weight: normal;
-	font-style: italic;
+    font-family: 'Insider-Medium';
+    font-weight: normal;
+    font-style: normal;
+    src: url('fonts/24BC0E_0_0.eot');
+    src: url('fonts/24BC0E_0_0.eot?#iefix') format('embedded-opentype'), url('fonts/24BC0E_0_0.woff') format('woff'), url('fonts/24BC0E_0_0.ttf') format('truetype');
 }
-
+ 
 @font-face {
-	font-family: 'GilliusADFNo2';
-	src: url('fonts/gilliusadfno2-bold-webfont.eot');
-	src: local('☺'), url('fonts/gilliusadfno2-bold-webfont.woff') format('woff'), url('fonts/gilliusadfno2-bold-webfont.ttf') format('truetype'), url('fonts/GilliusADFNo2-Bold.otf') format('opentype'), url('fonts/gilliusadfno2-bold-webfont.svg#webfontntXmQMqk') format('svg');
-	font-weight: bold;
-	font-style: normal;
+    font-family: 'Insider-Regular';
+    font-weight: normal;
+    font-style: normal;
+    src: url('fonts/24BC35_0_0.eot');
+    src: url('fonts/24BC35_0_0.eot?#iefix') format('embedded-opentype'), url('fonts/24BC35_0_0.woff') format('woff'), url('fonts/24BC35_0_0.ttf') format('truetype');
 }
-
-@font-face {
-	font-family: 'GilliusADFNo2';
-	src: url('fonts/gilliusadfno2-regular-webfont.eot');
-	src: local('☺'), url('fonts/gilliusadfno2-regular-webfont.woff') format('woff'), url('fonts/gilliusadfno2-regular-webfont.ttf') format('truetype'), url('fonts/GilliusADFNo2-Regular.otf') format('opentype'), url('fonts/gilliusadfno2-regular-webfont.svg#webfontvJUiAdi3') format('svg');
-	font-weight: normal;
-	font-style: normal;
-}
-
-@font-face {
-	font-family: 'DroidSans';
-	src: url('fonts/DroidSans-webfont.eot');
-	src: local('☺'), url('fonts/DroidSans-webfont.woff') format('woff'), url('fonts/DroidSans-webfont.ttf') format('truetype'), url('fonts/DroidSans-webfont.svg#webfontKYIQSBQk') format('svg');
-	font-weight: normal;
-	font-style: normal;
-}
-
-@font-face {
-	font-family: 'DroidSans';
-	src: url('fonts/DroidSans-Bold-webfont.eot');
-	src: local('☺'), url('fonts/DroidSans-Bold-webfont.woff') format('woff'), url('fonts/DroidSans-Bold-webfont.ttf') format('truetype'), url('fonts/DroidSans-Bold-webfont.svg#webfontljpTCDjw') format('svg');
-	font-weight: bold;
-	font-style: normal;
-}
-
--- a/vendor/plugins/redmine_bibliography/app/controllers/publications_controller.rb	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_bibliography/app/controllers/publications_controller.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -3,36 +3,34 @@
 
 class PublicationsController < ApplicationController
   unloadable
-  
+
   model_object Publication
-  before_filter :find_model_object, :except => [:new, :create, :index, :get_bibtex_required_fields, :autocomplete_for_project, :add_author, :sort_author_order, :autocomplete_for_author, :get_user_info ]  
+  before_filter :find_model_object, :except => [:new, :create, :index, :get_bibtex_required_fields, :autocomplete_for_project, :add_author, :sort_author_order, :autocomplete_for_author, :get_user_info ]
   before_filter :find_project_by_project_id, :authorize, :only => [ :edit, :new, :update, :create ]
-    
+
   def new
     find_project_by_project_id
     @publication = Publication.new
-    
+
     # we'll always want a new publication to have its bibtex entry
     @publication.build_bibtex_entry
-    
+
     # and at least one author
-    # @publication.authorships.build.build_author        
+    # @publication.authorships.build.build_author
     @author_options = [["#{User.current.name} (@#{User.current.mail.partition('@')[2]})", "#{User.current.class.to_s}_#{User.current.id.to_s}"]]
-
-
   end
 
-  def create    
+  def create
     @project = Project.find(params[:project_id])
 
     @author_options = []
 
     @publication = Publication.new(params[:publication])
     @publication.projects << @project unless @project.nil?
-        
-    if @publication.save 
+
+    if @publication.save
       @publication.notify_authors_publication_added(@project)
-      
+
       flash[:notice] = "Successfully created publication."
       redirect_to :action => :show, :id => @publication, :project_id => @project
     else
@@ -52,25 +50,25 @@
 
   def new_from_bibfile
     @publication.current_step = session[:publication_step]
-    
+
     # contents of the paste text area
     bibtex_entry = params[:bibtex_entry]
 
     # method for creating "pasted" bibtex entries
     if bibtex_entry
-      parse_bibtex_list bibtex_entry    
+      parse_bibtex_list bibtex_entry
     end
   end
 
   def get_bibtex_required_fields
 
     unless params[:value].empty?
-      fields = BibtexEntryType.fields(params[:value]) 
+      fields = BibtexEntryType.fields(params[:value])
     end
 
     respond_to do |format|
       format.js {
-        render(:update) {|page|       
+        render(:update) {|page|
           if params[:value].empty?
             page << "hideOnLoad();"
           else
@@ -78,7 +76,7 @@
           end
         }
       }
-    
+
     end
   end
 
@@ -92,28 +90,29 @@
     end
   end
 
-  def edit   
+  def edit
     find_project_by_project_id unless params[:project_id].nil?
-    
+
     @edit_view = true;
     @publication = Publication.find(params[:id])
     @selected_bibtex_entry_type_id = @publication.bibtex_entry.entry_type
 
-    @author_options = []  
-    
-    @bibtype_fields = BibtexEntryType.fields(@selected_bibtex_entry_type_id)    
+    @author_options = []
+
+    @bibtype_fields = BibtexEntryType.fields(@selected_bibtex_entry_type_id)
   end
 
-  def update    
-    @publication = Publication.find(params[:id])        
-
+  def update
+    @publication = Publication.find(params[:id])
     @author_options = []
 
-    logger.error { "INSIDE THE UPDATE ACTION IN THE PUBLICATION CONTROLLER" }
-
     if @publication.update_attributes(params[:publication])
       flash[:notice] = "Successfully updated Publication."
 
+      # expires the previosly cached entries
+      Rails.cache.delete "publication-#{@publication.id}-ieee"
+      Rails.cache.delete "publication-#{@publication.id}-bibtex"
+
       if !params[:project_id].nil?
         redirect_to :action => :show, :id => @publication, :project_id => params[:project_id]
       else
@@ -121,12 +120,13 @@
       end
     else
       render :action => 'edit'
-    end   
+    end
   end
 
+
   def show
     find_project_by_project_id unless params[:project_id].nil?
-        
+
     if @publication.nil?
       @publications = Publication.all
       render "index", :alert => 'The publication was not found!'
@@ -142,7 +142,7 @@
     return authors_entry.split(" and ")
   end
 
-  # parses a list of bibtex 
+  # parses a list of bibtex
   def parse_bibtex_list(bibtex_list)
     bibliography = BibTeX.parse bibtex_list
 
@@ -155,11 +155,11 @@
         create_bibtex_entry d
       end
     end
-  end 
+  end
 
-  def create_bibtex_entry(d)        
+  def create_bibtex_entry(d)
     @publication = Publication.new
-    @bentry = BibtexEntry.new        
+    @bentry = BibtexEntry.new
     authors = []
     institution = ""
     email = ""
@@ -177,30 +177,31 @@
       else
         @bentry[field] = d[field]
       end
-    end 
+    end
 
     @publication.bibtex_entry = @bentry
     @publication.save
 
-    # what is this for??? 
+    # what is this for???
     # @created_publications << @publication.id
 
     # need to save all authors
-    #   and establish the author-publication association 
-    #   via the authorships table 
+    #   and establish the author-publication association
+    #   via the authorships table
     authors.each_with_index.map do |authorname, idx|
       author = Author.new(:name => authorname)
       if author.save!
+        # todo: catch the errors...
         puts "SAVED"
       else
         puts "NOT SAVED"
       end
 
       author.authorships.create!(
-        :publication => @publication,
-        :institution => institution,
-        :email => email,
-        :order => idx)
+      :publication => @publication,
+      :institution => institution,
+      :email => email,
+      :order => idx)
     end
   end
 
@@ -211,36 +212,36 @@
 
   def import
     @publication = Publication.new
-    
-    
+
+
   end
-  
+
   def autocomplete_for_project
     @publication = Publication.find(params[:id])
-        
-    @projects = Project.active.like(params[:q]).find(:all, :limit => 100) - @publication.projects            
+
+    @projects = Project.active.like(params[:q]).find(:all, :limit => 100) - @publication.projects
     logger.debug "Query for \"#{params[:q]}\" returned \"#{@projects.size}\" results"
     render :layout => false
   end
 
-  def autocomplete_for_author    
+  def autocomplete_for_author
     @results = []
-    
+
     object_id = params[:object_id]
     @object_name = "publications[authorships_attributes][#{object_id}][search_results]"
-        
+
     # cc 20110909 -- revert to like instead of like_unique -- see #289
     authorships_list = Authorship.like(params[:q]).find(:all, :limit => 100)
     users_list = User.active.like(params[:q]).find(:all, :limit => 100)
 
     logger.debug "Query for \"#{params[:q]}\" returned \"#{authorships_list.size}\" authorships and \"#{users_list.size}\" users"
-    
+
     @results = users_list
 
-    # TODO: can be optimized…    
-    authorships_list.each do |authorship|      
+    # TODO: can be optimized…
+    authorships_list.each do |authorship|
       flag = true
-      
+
       users_list.each do |user|
         if authorship.name == user.name && authorship.email == user.mail && authorship.institution == user.institution
           Rails.logger.debug { "Rejecting Authorship #{authorship.id}" }
@@ -252,10 +253,9 @@
       @results << authorship if flag
     end
 
-    render :layout => false    
+    render :layout => false
   end
-  
-  
+
   def get_user_info
     object_id = params[:object_id]
     value = params[:value]
@@ -266,12 +266,12 @@
     name_field = "publication_authorships_attributes_#{object_id}_name_on_paper".to_sym
     email_field = "publication_authorships_attributes_#{object_id}_email".to_sym
     institution_field = "publication_authorships_attributes_#{object_id}_institution".to_sym
-    
+
     yes_radio = "publication_authorships_attributes_#{object_id}_identify_author_yes".to_sym
-    
+
     respond_to do |format|
       format.js {
-        render(:update) {|page| 
+        render(:update) {|page|
           page[name_field].value = item.name
           page[email_field].value = item.mail
           page[institution_field].value = item.institution
@@ -293,23 +293,23 @@
   end
 
   def add_project
-    @projects = Project.find(params[:publication][:project_ids])    
+    @projects = Project.find(params[:publication][:project_ids])
     @publication.projects << @projects
-    @project = Project.find(params[:project_id])    
-    
-    # TODO luisf should also respond to HTML??? 
+    @project = Project.find(params[:project_id])
+
+    # TODO luisf should also respond to HTML???
     respond_to do |format|
       format.html { redirect_to :back }
-      format.js { 
-        render(:update) {|page| 
-          page[:add_project_form].reset          
+      format.js {
+        render(:update) {|page|
+          page[:add_project_form].reset
           page.replace_html :list_projects, :partial => 'list_projects'
         }
       }
     end
   end
-  
-  
+
+
   def remove_project
     @project = Project.find(params[:project_id])
     proj = Project.find(params[:remove_project_id])
@@ -319,21 +319,21 @@
         @publication.projects.delete proj if request.post?
       end
     else
-      logger.error { "Cannot remove project from publication list" }      
+      logger.error { "Cannot remove project from publication list" }
     end
-    
+
     logger.error { "CURRENT project name#{proj.name} and wanna delete #{@project.name}" }
-        
-    render(:update) {|page| 
+
+    render(:update) {|page|
       page.replace_html "list_projects", :partial => 'list_projects', :id  => @publication
-    }    
+    }
   end
-    
+
   def destroy
     find_project_by_project_id
-    
+
     @publication.destroy
-        
+
     flash[:notice] = "Successfully deleted Publication."
     redirect_to :controller => :publications, :action => 'index', :project_id => @project
   end
--- a/vendor/plugins/redmine_bibliography/app/helpers/publications_helper.rb	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_bibliography/app/helpers/publications_helper.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -12,20 +12,20 @@
   def projects_check_box_tags(name, projects)
     s = ''
     projects.sort.each do |project|
-      if User.current.allowed_to?(:edit_publication, project) 
+      if User.current.allowed_to?(:edit_publication, project)
         s << "<label>#{ check_box_tag name, project.id, false } #{link_to_project project}</label>\n"
         s << '<br />'
       end
     end
 
-    s 
+    s
   end
-  
+
   def choose_author_link(object_name, items)
     # called by autocomplete_for_author (publications' action/view)
     # creates the select list based on the results array
     # results is an array with both Users and Authorships objects
-        
+
     @author_options = []
     @results.each do |result|
       email_bit = result.mail.partition('@')[2]
@@ -34,26 +34,26 @@
       end
       @author_options << ["#{result.name} #{email_bit}", "#{result.class.to_s}_#{result.id.to_s}"]
     end
-    
+
    if @results.size > 0
      s = select_tag( form_tag_name(object_name, :author_search_results), options_for_select(@author_options), { :id => form_tag_id(object_name, :author_search_results), :size => 3} )
      s << observe_field( form_tag_id(object_name, :author_search_results), :on => 'click', :function => "alert('Element changed')", :with => 'q')
    else
      s = "<em>No Authors found that match your search… sorry!</em>"
-   end      
+   end
   end
 
   def link_to_remove_fields(name, f)
     f.hidden_field(:_destroy) + link_to_function(name, "remove_fields(this)", :class => 'icon icon-del')
   end
-    
+
   def link_to_add_author_fields(name, f, association, action)
     new_object = f.object.class.reflect_on_association(association).klass.new
     fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder|
       render(association.to_s.singularize + "_fields", :f => builder)
-    end    
+    end
     link_to_function(name, h("add_author_fields(this, '#{association}', '#{escape_javascript(fields)}', '#{action}')"), { :class => 'icon icon-add', :id => "add_another_author" })
-  end  
+  end
 
   def sanitized_object_name(object_name)
     object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/,"_").sub(/_$/,"")
@@ -62,64 +62,77 @@
   def sanitized_method_name(method_name)
     method_name.sub(/\?$/, "")
   end
-  
+
   def form_tag_name(object_name, method_name)
       str = "#{object_name.to_s}[#{sanitized_method_name(method_name.to_s)}]"
-      str.to_sym 
+      str.to_sym
   end
-  
-  def form_tag_id(object_name, method_name)    
+
+  def form_tag_id(object_name, method_name)
     str = "#{sanitized_object_name(object_name.to_s)}_#{sanitized_method_name(method_name.to_s)}"
     str.to_sym
   end
-  
+
   def form_object_id(object_name)
     str = object_name.split("\[").last().gsub("\]","")
     str.to_sym
   end
 
-  def render_authorships_list(publication)   
+  def render_authorships_list(publication)
     s = '<p>'
-    
+
     publication.authorships.each do |authorship|
       s << link_to_authorship(authorship)
       s << "<br /><em>#{authorship.institution}</em></p>"
-    end    
+    end
 
-    s   
+    s
   end
-  
-    
-  def render_projects_list(publication, show_delete_icon)    
+
+  def render_projects_list(publication, show_delete_icon)
     s= ""
-    
+
     publication.projects.visible.each do |proj|
       s << link_to_project(proj, {}, :class => 'publication_project')
-    
-      if show_delete_icon  
+
+      if show_delete_icon
         if User.current.allowed_to?(:edit_publication, @project)
           if @project == proj
+            # todo: move this message to yml file
             confirm_msg = 'Are you sure you want to remove the current project from this publication\'s projects list?'
           else
             confirm_msg = false
-          end 
-          
-          s << link_to_remote(l(:button_delete), { :url => { :controller => 'publications', :action => 'remove_project', :id => publication, :remove_project_id => proj,  :project_id => @project }, :method => :post, :confirm => confirm_msg }, :class => 'icon icon-del') 
+          end
+
+          s << link_to_remote(l(:button_delete), { :url => { :controller => 'publications', :action => 'remove_project', :id => publication, :remove_project_id => proj,  :project_id => @project }, :method => :post, :confirm => confirm_msg }, :class => 'icon icon-del')
         end
       end
-      
-      s << "<br />"      
-    end    
 
-    s  
+      s << "<br />"
+    end
+
+    s
   end
-  
+
+  def print_ieee_format(publication)
+    Rails.cache.fetch("publication-#{publication.id}-ieee") do
+      publication.print_entry(:ieee)
+    end
+  end
+
+  def print_bibtex_format(publication)
+    Rails.cache.fetch("publication-#{publication.id}-bibtex") do
+      publication.print_entry(:bibtex)
+    end
+  end
+
+
   def show_bibtex_fields(bibtex_entry)
     s = ""
     bibtex_entry.attributes.keys.sort.each do |key|
       value = bibtex_entry.attributes[key].to_s
       next if key == 'id' or key == 'publication_id' or value == ""
-      s << "<h4>" + l("field_#{key}") + "</h4>" 
+      s << "<h4>" + l("field_#{key}") + "</h4>"
       s << "<p>"
       if key == "entry_type"
         s << bibtex_entry.entry_type_label
@@ -129,6 +142,6 @@
       s << "</p>"
     end
     s
-  end 
+  end
 end
 
--- a/vendor/plugins/redmine_bibliography/app/models/author.rb	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_bibliography/app/models/author.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -1,4 +1,6 @@
 class Author < ActiveRecord::Base
+  unloadable
+  
   has_many :authorships, :dependent => :destroy
   has_many :publications, :through => :authorships
 
--- a/vendor/plugins/redmine_bibliography/app/models/bibtex_entry_type.rb	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_bibliography/app/models/bibtex_entry_type.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -1,4 +1,5 @@
 class BibtexEntryType < ActiveRecord::Base
+  unloadable
 
   @@fields = Hash['article', ['journal', 'year', 'volume', 'number', 'pages', 'month', 'note' ], 
                   'book' , [ 'editor', 'publisher', 'volume', 'series', 'address', 'edition', 'month', 'year', 'note' ],
--- a/vendor/plugins/redmine_bibliography/app/models/publication.rb	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_bibliography/app/models/publication.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -2,10 +2,10 @@
 
 class Publication < ActiveRecord::Base
   unloadable
-  
+
   has_many :authorships, :dependent => :destroy, :order => "auth_order ASC"
   has_many :authors, :through => :authorships, :uniq => true
-  
+
   has_one :bibtex_entry, :dependent => :destroy
 
   validates_presence_of :title
@@ -14,9 +14,9 @@
   accepts_nested_attributes_for :authorships
   accepts_nested_attributes_for :authors, :allow_destroy => true
   accepts_nested_attributes_for :bibtex_entry, :allow_destroy => true
-  
+
   has_and_belongs_to_many :projects, :uniq => true
-  
+
   before_save :set_initial_author_order
 
   # Ensure error message uses proper text instead of
@@ -30,34 +30,62 @@
     end
   end
 
-  def notify_authors_publication_added(project)  
+  def notify_authors_publication_added(project)
     self.authors.each do |author|
       Rails.logger.debug { "Sending mail to \"#{self.title}\" publication authors." }
       Mailer.deliver_publication_added(author.user, self, project) unless author.user.nil?
     end
   end
-  
-  def notify_authors_publication_updated(project)  
+
+  def notify_authors_publication_updated(project)
     self.authors.each do |author|
       Rails.logger.debug { "Sending mail to \"#{self.title}\" publication authors." }
       Mailer.deliver_publication_updated(author.user, self, project) unless author.user.nil?
     end
   end
-  
-  
+
+
   def set_initial_author_order
     authorships = self.authorships
-    
+
     logger.debug { "Publication \"#{self.title}\" has #{authorships.size} authors." }
-    
+
     authorships.each_with_index do |authorship, index|
       if authorship.auth_order.nil?
          authorship.auth_order = index
       end
-    end    
+    end
   end
-  
-  
-  
-  
+
+  def print_bibtex_author_names
+    # this authors are correctly sorted because the authorships model
+    # already outputs the author names ASC by auth_order
+    self.authorships.map{|a| a.name_on_paper}.join(' and ')
+  end
+
+  def print_entry(style)
+    bib = BibTeX::Entry.new
+
+    bib.author = self.print_bibtex_author_names
+    bib.title = self.title
+
+    self.bibtex_entry.attributes.keys.sort.each do |key|
+      value = self.bibtex_entry.attributes[key].to_s
+      next if key == 'id' or key == 'publication_id' or value == ""
+
+      if key == "entry_type"
+        bib.type = BibtexEntryType.find(self.bibtex_entry.entry_type).name
+      else
+        bib[key.to_sym] = value
+      end
+    end
+
+    if style == :ieee
+      CiteProc.process bib.to_citeproc, :style => :ieee, :format => :html
+    else
+      bibtex = bib.to_s :include => :meta_content
+      bibtex.strip!
+      logger.error { bibtex }
+    end
+  end
 end
--- a/vendor/plugins/redmine_bibliography/app/views/projects/_bibliography_box.html.erb	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_bibliography/app/views/projects/_bibliography_box.html.erb	Wed Nov 21 17:56:50 2012 +0000
@@ -1,24 +1,29 @@
+<% content_for :header_tags do %>
+    <%= stylesheet_link_tag 'bibliography', :plugin => 'redmine_bibliography' %>
+    <%= javascript_include_tag 'bibtex', :plugin => 'redmine_bibliography' -%>
+<% end %>
+
 <% if @project.publications.any? %>
 <%= stylesheet_link_tag 'bibliography', :plugin => 'redmine_bibliography' %>
   <div id="bibliography">
     <div class="box">
     <h3><%=l(:label_related_publication_plural)%></h3>
 
-   <dl>
+     <dl>
      <% @project.publications.each do |publication| %>
-     <dt>
-     <span class="authors">
-       <%= publication.authorships.map { |a| h a.name_on_paper }.join(', ') %><% if !publication.authorships.empty? %>.<% end %>
-     </span>
-     <span class="title"><%= link_to publication.title, :controller => 'publications', :action => 'show', :id => publication, :project_id => @project %></span>
-     <% if publication.bibtex_entry.year.to_s != "" %>
-     <span class="year">
-       &nbsp;(<%= publication.bibtex_entry.year %>)
-     </span>
-     <% end %>
-     </dt><dd></dd>
+       <dt>
+          <%= print_ieee_format(publication) %>
+       </dt>
+       <dd>
+         <%= link_to("[More Details]", {:controller => :publications, :action => :show, :id => publication.id, :project_id => @project.id}) -%>
+
+         <%= link_to_function "[B<small>IB</small>T<sub>E</sub>X]", onclick="toggleBibtex(this)" -%>
+       </dd>
+       <dd class="bibtex-textarea collapsed" style="display: none;">
+         <textarea readonly> <%= print_bibtex_format(publication) %> </textarea>
+       </dd>
    <% end -%>
-   </dl>
+     </dl>
   </div>
 </div>
-<% end %>
+<% end -%>
--- a/vendor/plugins/redmine_bibliography/app/views/publications/_form.html.erb	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_bibliography/app/views/publications/_form.html.erb	Wed Nov 21 17:56:50 2012 +0000
@@ -1,8 +1,8 @@
-<%= f.error_messages %>  
+<%= f.error_messages %>
 
 <h3><%= f.text_field :title, :required => true, :size => 70 %></h3>
 
-<div class="splitcontentleft">      
+<div class="splitcontentleft">
   <h3><%= l(:label_publication_other_details) %></h3>
   <div class="box tabular">
     <% f.fields_for :bibtex_entry do |builder| -%>
@@ -14,12 +14,17 @@
       <br />
       <em><%= l(:text_external_url) %></em>
     </p>
+    <p>
+      <%= f.text_field :doi, :size => 70 %>
+      <br />
+      <em><%= l(:text_doi) %></em>
+    </p>
 
   </div>
 </div>
 
 <div class="splitcontentright">
-  <h3><%= l(:authors) %></h3>    
+  <h3><%= l(:authors) %></h3>
   <div class="box tabular">
     <% f.fields_for :authorships do |builder| -%>
       <%= render "authorship_fields", :f => builder %>
--- a/vendor/plugins/redmine_bibliography/app/views/publications/show.html.erb	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_bibliography/app/views/publications/show.html.erb	Wed Nov 21 17:56:50 2012 +0000
@@ -1,23 +1,27 @@
-<%= stylesheet_link_tag 'bibliography', :plugin => 'redmine_bibliography' %>
-
 <h2><%=l(:label_publication_show)%></h2>
 
 <div class="box">
-<h3>
-  <%= h @publication.title %>
-</h3>
+  <h3>Publication Info</h3>
+  <p><%= print_ieee_format(@publication)%></p>
+
+  <h3>B<small>IB</small>T<sub>E</sub>X Format</h3>
+  <pre><%=h print_bibtex_format(@publication) %></pre>
+</div>
+
+<div class="box">
 
 <h4><%= l(:authors) %></h4>
 <ul id="authorships">
   <% for authorship in @publication.authorships.find(:all, :order => :auth_order) %>
     <% content_tag_for :li, authorship do %>
-      <%- if User.current.allowed_to?(:edit_publication, @project) && @publication.authorships.length > 1 -%>    
+      <%- if User.current.allowed_to?(:edit_publication, @project) && @publication.authorships.length > 1 -%>
         <span class="handle">[drag to reorder]</span>
       <%- end -%>
       <%= link_to_authorship authorship %> <em><%= h authorship.institution %></em> <br />
     <%- end -%>
   <%- end -%>
 </ul>
+
 <%- if User.current.allowed_to?(:edit_publication, @project) && @publication.authorships.length > 1 -%>
   <%= sortable_element("authorships", :url => { :controller => :publications, :action => :sort_author_order }, :handle => "handle") %>
 <%- end -%>
@@ -26,18 +30,21 @@
   <%= show_bibtex_fields(@publication.bibtex_entry) %>
 <%- end -%>
 
+<%- unless @publication.external_url.blank? -%>
+  <p>
+    <b><%= l(:field_external_url) %>:</b> <%= link_to h(@publication.external_url), @publication.external_url, {:target => "_blank"} -%>
+  </p>
+<%- end -%>
 
-<% unless @publication.external_url.blank? %>
-  <h4>
-    <%= l(:field_external_url) %>
-  </h4>
+
+<% unless @publication.doi.blank? %>
   <p>
-    <%= link_to h(@publication.external_url), @publication.external_url, {:target => "_blank"} %>
+    <b><%= l(:field_doi)-%>:</b> <%= link_to h(@publication.doi), "http://dx.doi.org/#{@publication.doi}", {:target => "_blank"} -%>
   </p>
 <% end %>
 
 <br / >
-  <% if User.current.allowed_to?(:add_publication, @project) %>	
+  <% if User.current.allowed_to?(:add_publication, @project) %>
     <%= link_to l(:label_publication_edit), { :controller => "publications", :action => "edit", :id => @publication, :project_id => @project } %> |
     <%= link_to "Delete", {:controller => 'publications', :action => 'destroy', :id => @publication, :project_id => @project },
                                                      :confirm => l(:text_are_you_sure), :method => :delete, :title => l(:button_delete) %> |
@@ -53,8 +60,8 @@
   <p id="list_projects">
     <%= render :partial => 'list_projects' %>
   </p>
-  
+
   <%- if User.current.allowed_to?(:edit_publication, @project) -%>
-    <%= render :partial => 'add_project_form' %>    
+    <%= render :partial => 'add_project_form' %>
   <%- end -%>
 <% end %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/redmine_bibliography/assets/javascripts/bibtex.js	Wed Nov 21 17:56:50 2012 +0000
@@ -0,0 +1,6 @@
+function toggleBibtex(el) {
+  var dd = Element.up(el).next('dd')
+
+  dd.toggleClassName('collapsed');
+  Effect.toggle(dd, 'slide', {duration:0.2});
+}
\ No newline at end of file
--- a/vendor/plugins/redmine_bibliography/assets/stylesheets/bibliography.css	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_bibliography/assets/stylesheets/bibliography.css	Wed Nov 21 17:56:50 2012 +0000
@@ -32,15 +32,32 @@
     min-width: 150px;
 }
 
-div#bibliography dl { margin-left: 2em; }
-div#bibliography .box dl { margin-left: 0; }
-div#bibliography dt { margin-bottom: 0px; padding-left: 20px }
-div#bibliography .box dt { margin-bottom: 0px; padding-left: 10px }
-div#bibliography dd { margin-bottom: 1em; padding-left: 18px; font-size: 0.9em; }
-div#bibliography .box dd { margin-bottom: 0.6em; padding-left: 0; }
+
+div#bibliography dd { margin-bottom: 1em; font-size: 0.9em; }
+
 div#bibliography dd .authors { font-style: italic; }
-div#bibliography dt .title { font-style: italic; }
 div#bibliography dd span.authors { color: #808080; }
 div#bibliography dd span.year { padding-left: 0.6em; }
 
-div#bibliography h3 { background: url(../../../images/table_multiple.png) no-repeat 0% 50%; padding-left: 20px; }
+div#bibliography .box dt { 
+  background: url(../../../images/document.png) no-repeat 0% 4px;
+  padding-left: 20px;
+  margin-left: 0;
+}
+div#bibliography .box dd { 
+  padding-left: 20px;
+  margin-left: 0;
+}
+
+div#bibliography h3 { 
+  background: url(../../../images/table_multiple.png) no-repeat 0% 50%; 
+  padding-left: 20px;
+}
+
+div#bibliography textarea {
+  width: 90%;
+  height: 200px;
+  font: normal 8px;
+  padding: 2px 10px;
+  border: solid 1px #ddd;  
+}
--- a/vendor/plugins/redmine_bibliography/config/locales/en.yml	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_bibliography/config/locales/en.yml	Wed Nov 21 17:56:50 2012 +0000
@@ -12,8 +12,7 @@
   label_my_publications_box: "My Publications"
   view_all_publications: "View All Project's Publications"
   publications: Publications
-  
-  
+
   identify_author_question: Is the right person selected above?
   identify_author_yes: "Yes"
   identify_author_correct: "Yes, but I need to correct some details below"
@@ -25,24 +24,25 @@
   label_all_publications_for_project: Publications associated with %{project}
   label_authors_show: "Authorships associated with this author"
   label_authors_index: "List of authors"
-  
+
   field_authorship_publication_title: "Publication"
   field_authorship_name: "Name on Paper"
   field_authorship_email: "Email"
   field_authorship_institution: "Institution"
-  
+
   field_external_url: "External URL"
+  field_doi: "DOI"
   field_publication_title: Title
   field_publication_authors: Authors
   field_publication_projects: "Associated projects"
   field_author_name: Author
   field_author_user: User Name
   field_author_username: "Associated user"
-  field_author_publications: "Publications by this Author" 
+  field_author_publications: "Publications by this Author"
   field_identify_author_yes: "Yes"
   field_identify_author_correct: "Corrections"
   field_identify_author_no: "No"
-  
+
   label_author_is_me: "(I am this author)"
   label_add_me_as_author: "Add me as an author"
   label_add_an_author: "Add an author"
@@ -54,7 +54,7 @@
   label_author_information: "Author Information"
 
   remove_author: "Remove this author"
-  
+
   label_publication_plural: "Publications"
   label_related_publication_plural: "Related publications"
   label_publication_new: "Create New Publication"
@@ -67,22 +67,23 @@
   label_publication_project_index: "Projects associated with this publication"
   label_publication_index: "View all publications"
   label_publication_other_details: "Details"
-  
+
   text_external_url: "Link to the publication or to an external page about it."
+  text_doi: "DOI (Digital Object Identifier)."
   text_author_name_on_paper: "Author's name as it appears on the paper."
   text_author_institution: "Author's institution as on the paper."
   text_author_email: "Author's email address as on the paper."
-  
+
   text_author_search: "Search existing authors"
-  
+
   # authorships model
   field_institution: "Institution"
-  field_name_on_paper: "Name" 
+  field_name_on_paper: "Name"
   field_email: "Email Address"
-      
+
   # bibtex_entries model
   field_entry_type: "Publication Type"
-  field_id: "id" 
+  field_id: "id"
   field_publication_id: "Publication_id"
   field_address: "Address"
   field_annote: "Annote"
@@ -122,7 +123,7 @@
   field_bibtex_proceedings: Conference proceedings
   field_bibtex_techreport: Technical report
   field_bibtex_unpublished: Unpublished
-  
+
   label_author_1: First author
   label_author_2: Second author
   label_author_3: Third author
@@ -146,6 +147,6 @@
 
   mail_subject_publication_added: "You have been added as an author to a new publication"
   mail_body_publication_added: "A new publication (%{publication}) has been added to the project '%{project}.'"
-  
-  
-  
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/redmine_bibliography/db/migrate/008_add_doi_and_timestamp_columns_to_publications.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -0,0 +1,12 @@
+class AddDoiAndTimestampColumnsToPublications < ActiveRecord::Migration
+  def self.up
+    add_column :publications, :doi, :string
+    add_timestamps :publications
+
+  end
+
+  def self.down
+    remove_column :publications, :doi
+    remove_timestamps :publications
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/redmine_bibliography/db/migrate/009_add_timestamp_columns_to_authors.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -0,0 +1,9 @@
+class AddTimestampColumnsToAuthors < ActiveRecord::Migration
+  def self.up
+    add_timestamps :authors
+  end
+
+  def self.down
+    remove_timestamps :authors
+  end
+end
--- a/vendor/plugins/redmine_bibliography/init.rb	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_bibliography/init.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -1,7 +1,8 @@
 require 'redmine'
 require 'dispatcher'
 
-RAILS_DEFAULT_LOGGER.info 'Starting Bibliography Plugin for RedMine'
+require 'bibtex'
+require 'citeproc'
 
 # Patches to the Redmine core.
 Dispatcher.to_prepare :redmine_model_dependencies do
@@ -21,7 +22,9 @@
     Mailer.send(:include, Bibliography::MailerPatch)
   end
 
-
+  unless ProjectsHelper.included_modules.include?(Bibliography::ProjectsHelperPatch)
+    ProjectsHelper.send(:include, Bibliography::ProjectsHelperPatch)
+  end
 end
 
 
@@ -47,5 +50,5 @@
   # extending the Project Menu
   menu :project_menu, :publications, { :controller => 'publications', :action => 'index', :path => nil }, :after => :activity, :param => :project_id, :caption => Proc.new { Setting.plugin_redmine_bibliography['menu'] },
    :if => Proc.new { !Setting.plugin_redmine_bibliography['menu'].blank? }
-    
+
 end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/redmine_bibliography/lib/bibliography/projects_helper_patch.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -0,0 +1,17 @@
+module Bibliography
+  module ProjectsHelperPatch
+
+    def self.included(base) # :nodoc:
+      base.send(:include, InstanceMethods)
+      base.send(:include, PublicationsHelper)
+
+      base.class_eval do
+        unloadable
+      end
+    end
+
+    module InstanceMethods
+    end
+  end
+end
+
--- a/vendor/plugins/redmine_bibliography/lib/bibliography/user_author_patch.rb	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_bibliography/lib/bibliography/user_author_patch.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -1,16 +1,16 @@
 require_dependency 'user'
 
 module Bibliography
-  module UserAuthorPatch    
+  module UserAuthorPatch
     def self.included(base)
-      base.send(:include, InstanceMethods) 
-      extend ClassMethods     
-          
+      base.send(:include, InstanceMethods)
+      extend ClassMethods
+
     end #self.included
-    
+
     module ClassMethods
-    end  
-    
+    end
+
     module InstanceMethods
 
       def institution
@@ -18,28 +18,28 @@
           institution_name = self.ssamr_user_detail.institution_name
         else
           institution_name = "No Institution Set"
-        end        
-        return institution_name        
+        end
+        return institution_name
       end
 
       def get_author_info
         # TODO: DELETE THIS METHOD??
-        info = { 
+        info = {
           :name_on_paper =>  self.name,
           :email => self.mail,
           :institution => "",
           :author_user_id => self.id,
-          :is_user => "1"                    
+          :is_user => "1"
         }
 
         if not self.ssamr_user_detail.nil?
           info[:institution]  = self.ssamr_user_detail.institution_name
         end
 
-        return info        
+        return info
       end
-                
+
     end #InstanceMethods
-    
+
   end #UserPublicationsPatch
 end #RedmineBibliography
--- a/vendor/plugins/redmine_tags/app/helpers/tags_helper.rb	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_tags/app/helpers/tags_helper.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -40,8 +40,11 @@
     content_tag('span', content, :class => 'tag-label')
   end
 
-  def render_project_tag_link(tag)
+  def render_project_tag_link(tag, options = {})
     content = link_to tag.name, :controller => :projects, :action => :index, :project => { :tag_list => tag.name } 
+    if options[:show_count]
+      content << content_tag('span', "(#{tag.count})", :class => 'tag-count')
+    end
     content_tag('span', content, :class => 'tag-label')
   end
 
@@ -73,7 +76,7 @@
       end
 
       tag_cloud tags, (1..8).to_a do |tag, weight|
-        content << " " + content_tag(item_el, render_tag_link(tag, options), :class => "tag-nube-#{weight}") + " "
+        content << " " + content_tag(item_el, render_project_tag_link(tag, options), :class => "tag-nube-#{weight}") + " "
       end
 
       content_tag(list_el, content, :class => 'tags')
--- a/vendor/plugins/redmine_tags/app/views/projects/_filter_search_tags.html.erb	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_tags/app/views/projects/_filter_search_tags.html.erb	Wed Nov 21 17:56:50 2012 +0000
@@ -6,7 +6,7 @@
       </p>
       <div id="project_tag_candidates" class="autocomplete"></div>
       <%= javascript_include_tag 'tags_input', :plugin => 'redmine_tags' -%>
-      <%= javascript_tag "observeProjectTagsField('#{url_for(:controller => 'auto_completes', :action => 'project_search_tags', :project_id => Project.first.id)}', true)" -%>
+      <%= javascript_tag "observeProjectTagsField('#{url_for(:controller => 'auto_completes', :action => 'project_search_tags')}', true)" -%>
     </div>
   <%- end -%>
 </p>
--- a/vendor/plugins/redmine_tags/app/views/projects/_filter_tags.html.erb	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_tags/app/views/projects/_filter_tags.html.erb	Wed Nov 21 17:56:50 2012 +0000
@@ -7,7 +7,7 @@
       <div id="project_tag_candidates" class="autocomplete"></div>
       <%= javascript_include_tag 'tags_input', :plugin => 'redmine_tags' %>
 
-      <%= javascript_tag "observeProjectTagsField('#{url_for(:controller => 'auto_completes', :action => 'project_tags', :project_id => Project.first.id)}')" %>
+      <%= javascript_tag "observeProjectTagsField('#{url_for(:controller => 'auto_completes', :action => 'project_tags')}')" %>
     </div>
   <% end -%>
 </p>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/redmine_tags/app/views/projects/_tagcloud.html.erb	Wed Nov 21 17:56:50 2012 +0000
@@ -0,0 +1,6 @@
+
+<div id="tags">
+<%= render_tags_list(Project.available_tags, :style => :cloud) %>
+</div>
+
+
--- a/vendor/plugins/redmine_tags/app/views/projects/_tags_form.html.erb	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_tags/app/views/projects/_tags_form.html.erb	Wed Nov 21 17:56:50 2012 +0000
@@ -6,6 +6,6 @@
   </p>
   <div id="project_tag_candidates" class="autocomplete"></div>
   <%= javascript_include_tag 'tags_input', :plugin => 'redmine_tags' %>
-  <%= javascript_tag "observeProjectTagsField('#{url_for(:controller => 'auto_completes', :action => 'project_tags', :project_id => Project.first.id)}', false)" %>  
+  <%= javascript_tag "observeProjectTagsField('#{url_for(:controller => 'auto_completes', :action => 'project_tags')}', false)" %>
 </div>
 <% end -%>
\ No newline at end of file
--- a/vendor/plugins/redmine_tags/app/views/projects/index.html.erb	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_tags/app/views/projects/index.html.erb	Wed Nov 21 17:56:50 2012 +0000
@@ -11,7 +11,6 @@
 </div>
 
 
-
 <div style="clear:both;"></div>
 <% if User.current.logged? %>
   <%= render :partial => 'my_projects' %>
--- a/vendor/plugins/redmine_tags/assets/stylesheets/redmine_tags.css	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_tags/assets/stylesheets/redmine_tags.css	Wed Nov 21 17:56:50 2012 +0000
@@ -22,6 +22,7 @@
 ul.tags li { margin: .25em 0px; }
 
 div.tags { text-align: center; }
+div.tags h3 { text-align: left; }
 div.tags .tag-label { margin: .25em; }
 div.tags .tag-nube-1 { font-size: .8em; }
 div.tags .tag-nube-2 { font-size: .9em; }
--- a/vendor/plugins/redmine_tags/lib/redmine_tags/patches/project_patch.rb	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_tags/lib/redmine_tags/patches/project_patch.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -13,38 +13,12 @@
           unloadable
 
           attr_accessor :tag_list
-
           acts_as_taggable
 
         end
       end
 
-      def before_save_with_save_tags()
-#        debugger
-        logger.error { "GONNA SAVE TAG LIST" }
-
-
-#        params[:tag_list]
-        
-        
-        # logger.error { @project.name }
-
-    #    if params && params[:project] && !params[:project][:tag_list].nil?
-    #      old_tags = context[:project].tag_list.to_s
-    #      context[:project].tag_list = params[:project][:tag_list]
-    #      new_tags = context[:project].tag_list.to_s
-    #
-    #      unless (old_tags == new_tags || context[:project].current_journal.blank?)
-    #        context[:project].current_journal.details << JournalDetail.new(:property => 'attr',
-    #                                                                     :prop_key => 'tag_list',
-    #                                                                     :old_value => old_tags,
-    #                                                                     :value => new_tags)
-    #      end
-    #    end
-      end
-      
       module InstanceMethods
-        
       end
 
       module ClassMethods
@@ -64,7 +38,7 @@
           name_like = options[:name_like]
           options = {}
           visible   = ARCondition.new
-                  
+
           visible << ["#{Project.table_name}.is_public = '1'"]
 
           if name_like
@@ -73,7 +47,7 @@
 
           options[:conditions] = visible.conditions
 
-          self.all_tag_counts(options)          
+          self.all_tag_counts(options)
         end
       end
     end
--- a/vendor/plugins/redmine_tags/lib/redmine_tags/patches/projects_controller_patch.rb	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_tags/lib/redmine_tags/patches/projects_controller_patch.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -1,12 +1,13 @@
+# -*- coding: utf-8 -*-
 require_dependency 'projects_controller'
 
 module RedmineTags
   module Patches
-    module ProjectsControllerPatch      
+    module ProjectsControllerPatch
       def self.included(base)
         base.send(:include, InstanceMethods)
-        base.class_eval do          
-          unloadable 
+        base.class_eval do
+          unloadable
           skip_before_filter :authorize, :only => [:set_fieldset_status]
           skip_before_filter :find_project, :only => [:set_fieldset_status]
           before_filter :add_tags_to_project, :only => [:save, :update]
@@ -16,7 +17,7 @@
       end
 
       module InstanceMethods
-                
+
         def add_tags_to_project
 
           if params && params[:project] && !params[:project][:tag_list].nil?
@@ -56,17 +57,17 @@
           else
             @myproj_status = session[:my_projects_fieldset_status]
           end
-                    
+
           if session[:filters_fieldset_status].nil?
             @filter_status = "false"
           else
             @filter_status = session[:filters_fieldset_status]
           end
-          
+
           if params && params[:project] && !params[:project][:tag_list].nil?
             @filter_status = "true"
           end
-                                      
+
         end
 
         # Lists visible projects. Paginator is for top-level projects only
@@ -77,17 +78,17 @@
           get_fieldset_statuses
 
           respond_to do |format|
-            format.html { 
+            format.html {
               paginate_projects
-              
-              @projects = Project.visible_roots.find(@projects, :offset => @offset, :limit => @limit, :order => sort_clause) 
+
+              @projects = Project.visible_roots.find(@projects, :offset => @offset, :limit => @limit, :order => sort_clause)
 
               if User.current.logged?
                 # seems sort_by gives us case-sensitive ordering, which we don't want
                 #          @user_projects = User.current.projects.sort_by(&:name)
                 @user_projects = User.current.projects.all(:order => :name)
               end
-              
+
               render :template => 'projects/index.html.erb', :layout => !request.xhr?
             }
             format.api {
@@ -111,8 +112,8 @@
 
         private
 
-        def filter_projects                  
-          @question = (params[:q] || "").strip     
+        def filter_projects
+          @question = (params[:q] || "").strip
 
           if params.has_key?(:project)
             @tag_list = (params[:project][:tag_list] || "").strip.split(",")
@@ -125,15 +126,12 @@
           else
             @projects = Project.visible_roots.find(Project.visible.search_by_question(@question))
           end
-  
+
           unless @tag_list.empty?
-            @tagged_projects_ids = Project.visible.tagged_with(@tag_list).collect{ |project| Project.find(project.id) }
+            @tagged_projects_ids = Project.visible.tagged_with(@tag_list).collect{ |project| Project.find(project.id).root }
             @projects = @projects & @tagged_projects_ids
+            @projects = @projects.uniq
           end
-          
-          @projects = @projects.collect{ |project| project.root }
-          @projects = @projects.uniq
-                    
         end
       end
     end
--- a/vendor/plugins/redmine_tags/lib/redmine_tags/patches/projects_helper_patch.rb	Tue Oct 16 15:25:01 2012 +0100
+++ b/vendor/plugins/redmine_tags/lib/redmine_tags/patches/projects_helper_patch.rb	Wed Nov 21 17:56:50 2012 +0000
@@ -4,12 +4,14 @@
 
       def self.included(base) # :nodoc:
         base.send(:include, InstanceMethods)
+        base.send(:include, TagsHelper)
+
         base.class_eval do
           unloadable
         end
       end
 
-      module InstanceMethods        
+      module InstanceMethods
         # Renders a tree of projects that the current user does not belong
         # to, or of all projects if the current user is not logged in.  The
         # given collection may be a subset of the whole project tree
@@ -18,30 +20,30 @@
         # description, manager(s), creation date, last activity date,
         # general activity level, whether there is anything actually hosted
         # here for the project, etc.
-        def render_project_table_with_filtering(projects, question)          
+        def render_project_table_with_filtering(projects, question)
           custom_fields = ""
           s = ""
           if projects.any?
             tokens = RedmineProjectFiltering.calculate_tokens(question, custom_fields)
-            
+
             s << "<div class='autoscroll'>"
             s << "<table class='list projects'>"
             s << "<thead><tr>"
-        
+
             s << sort_header_tag('name', :caption => l("field_name"))
             s << "<th class='tags'>" << l("tags") << "</th>"
             s << "<th class='managers'>" << l("label_managers") << "</th>"
             s << sort_header_tag('created_on', :default_order => 'desc')
             s << sort_header_tag('updated_on', :default_order => 'desc')
-        
+
             s << "</tr></thead><tbody>"
-        
+
             original_project = @project
-        
+
             projects.each do |project|
               s << render_project_in_table_with_filtering(project, cycle('odd', 'even'), 0, tokens)
             end
-        
+
             s << "</table>"
           else
             s << "\n"
@@ -51,7 +53,7 @@
           s
         end
 
-        def render_project_in_table_with_filtering(project, oddeven, level, tokens)          
+        def render_project_in_table_with_filtering(project, oddeven, level, tokens)
           # set the project environment to please macros.
           @project = project
 
@@ -71,7 +73,7 @@
           s << "<td class='tags' align=top>" << project.tag_counts.collect{ |t| render_project_tag_link(t) }.join(', ') << "</td>"
 
           s << "<td class='managers' align=top>"
-           
+
           u = project.users_by_role
           if u
             u.keys.each do |r|
@@ -90,7 +92,7 @@
           end
 
           s << "</td>"
-          
+
           s << "<td class='created_on' align=top>" << format_date(project.created_on) << "</td>"
           s << "<td class='updated_on' align=top>" << format_date(project.updated_on) << "</td>"
 
@@ -104,9 +106,9 @@
 
           s
         end
-        
-        
-        
+
+
+
         # Renders a tree of projects as a nested set of unordered lists
         # The given collection may be a subset of the whole project tree
         # (eg. some intermediate nodes are private and can not be seen)
@@ -115,7 +117,7 @@
           if projects.any?
             tokens = RedmineProjectFiltering.calculate_tokens(question, custom_fields)
             debugger
-            
+
 
             ancestors = []
             original_project = @project
@@ -127,14 +129,14 @@
               else
                 ancestors.pop
                 s << "</li>"
-                while (ancestors.any? && !project.is_descendant_of?(ancestors.last)) 
+                while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
                   ancestors.pop
                   s << "</ul></li>"
                 end
               end
               classes = (ancestors.empty? ? 'root' : 'child')
               s << "<li class='#{classes}'><div class='#{classes}'>" +
-                link_to( highlight_tokens(project.name, tokens), 
+                link_to( highlight_tokens(project.name, tokens),
                   {:controller => 'projects', :action => 'show', :id => project},
                   :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}"
                 )
@@ -145,7 +147,7 @@
            #    value = value_model.present? ? value_model.value : nil
            #    s << "<li><b>#{field.name.humanize}:</b> #{highlight_tokens(value, tokens)}</li>" if value.present?
            #  end
-              
+
               s << "</ul>"
               s << "<div class='clear'></div>"
               unless project.description.blank?
@@ -162,7 +164,7 @@
           end
           s.join "\n"
         end
-        
+
         # Renders a tree of projects where the current user belongs
         # as a nested set of unordered lists
         # The given collection may be a subset of the whole project tree
@@ -192,9 +194,9 @@
           s
 
         end
-        
-        
-        
+
+
+
 
         def render_my_project_in_hierarchy_with_tags(project)
 
@@ -214,7 +216,7 @@
             else
               s << " <span class='private'>" << l(:field_is_private) << "</span>"
             end
-           
+
             tc = project.tag_counts
             if tc.empty?
               s << " <span class='no-tags'>" << l(:field_no_tags) << "</span>"
@@ -241,15 +243,15 @@
 
         end
 
-        
-        
+
+
         private
-        
+
         # copied from search_helper. This one doesn't escape html or limit the text length
         def highlight_tokens(text, tokens)
           return text unless text && tokens && !tokens.empty?
           re_tokens = tokens.collect {|t| Regexp.escape(t)}
-          regexp = Regexp.new "(#{re_tokens.join('|')})", Regexp::IGNORECASE    
+          regexp = Regexp.new "(#{re_tokens.join('|')})", Regexp::IGNORECASE
           result = ''
           text.split(regexp).each_with_index do |words, i|
             words = words.mb_chars
@@ -262,7 +264,7 @@
           end
           result
         end
-      
+
       end
     end
   end