changeset 812:b5474c68c433 luisf

Merge from branch "live"
author luisf <luis.figueira@eecs.qmul.ac.uk>
date Wed, 23 Nov 2011 15:29:25 +0000
parents 0bdd601c124a (current diff) b4e59a60b447 (diff)
children 0987c3565751
files
diffstat 37 files changed, 557 insertions(+), 144 deletions(-) [+]
line wrap: on
line diff
--- a/app/controllers/application_controller.rb	Mon Nov 14 12:36:17 2011 +0000
+++ b/app/controllers/application_controller.rb	Wed Nov 23 15:29:25 2011 +0000
@@ -177,14 +177,14 @@
   def find_project
     @project = Project.find(params[:id])
   rescue ActiveRecord::RecordNotFound
-    render_404
+    User.current.logged? ? render_404 : require_login
   end
 
   # Find project of id params[:project_id]
   def find_project_by_project_id
     @project = Project.find(params[:project_id])
   rescue ActiveRecord::RecordNotFound
-    render_404
+    User.current.logged? ? render_404 : require_login
   end
 
   # Find a project based on params[:project_id]
--- a/app/helpers/projects_helper.rb	Mon Nov 14 12:36:17 2011 +0000
+++ b/app/helpers/projects_helper.rb	Wed Nov 23 15:29:25 2011 +0000
@@ -150,9 +150,6 @@
 
     if s != ''
       a = ''
-      a << "<h2>"
-      a <<  l("label_my_project_plural")
-      a << "</h2>"
       a << "<ul class='projects root'>\n"
       a << s
       a << "</ul>\n"
--- a/app/helpers/repositories_helper.rb	Mon Nov 14 12:36:17 2011 +0000
+++ b/app/helpers/repositories_helper.rb	Wed Nov 23 15:29:25 2011 +0000
@@ -281,4 +281,24 @@
                         ) +
                      '<br />' + l(:text_scm_path_encoding_note))
   end
+
+  # Generates a link to a downloadable archive for a revision
+  # Options:
+  # * :text - Link text (default to the formatted revision)
+  def link_to_revision_archive(repository, revision, project, options={})
+    method = repository.class.name.demodulize.underscore + "_link_to_revision_archive"
+    if repository.is_a?(Repository) &&
+        respond_to?(method) && method != 'link_to_revision_archive'
+      send(method, repository, revision, project, options)
+    end
+  end
+
+  def mercurial_link_to_revision_archive(repository, revision, project, options={})
+    text = options.delete(:text) || format_revision(revision)
+    rev = revision.respond_to?(:identifier) ? revision.identifier : revision
+    if rev.blank? then rev = 'tip' end
+    content_tag('a', h(text),
+        { :href => "/hg/#{project.identifier}/archive/#{rev}.zip" }.merge(options));
+  end
+
 end
--- a/app/views/account/register.rhtml	Mon Nov 14 12:36:17 2011 +0000
+++ b/app/views/account/register.rhtml	Wed Nov 23 15:29:25 2011 +0000
@@ -29,9 +29,10 @@
 <p><label for="user_mail"><%=l(:field_mail)%> <span class="required">*</span></label>
 <%= text_field 'user', 'mail'  %></p>
 
+<!-- We only support English in this site 
 <p><label for="user_language"><%=l(:field_language)%></label>
 <%= select("user", "language", lang_options_for_select) %></p>
-
+-->
 
 <h3><%=l(:label_ssamr_details)%></h3>
 	
--- a/app/views/projects/show.rhtml	Mon Nov 14 12:36:17 2011 +0000
+++ b/app/views/projects/show.rhtml	Wed Nov 23 15:29:25 2011 +0000
@@ -98,6 +98,7 @@
 </div>
 
 <% content_for :sidebar do %>
+    <%= call_hook(:view_projects_show_sidebar_top, :project => @project) %>
     <% if @total_hours.present? %>
     <h3><%= l(:label_spent_time) %></h3>
     <p><span class="icon icon-time"><%= l_hours(@total_hours) %></span></p>
--- a/app/views/repositories/_navigation.rhtml	Mon Nov 14 12:36:17 2011 +0000
+++ b/app/views/repositories/_navigation.rhtml	Wed Nov 23 15:29:25 2011 +0000
@@ -2,7 +2,7 @@
   <%= javascript_include_tag 'repository_navigation' %>
 <% end %>
 
-<%= link_to l(:label_statistics), {:action => 'stats', :id => @project}, :class => 'icon icon-stats' %>
+<%= link_to_revision_archive(@repository, @changeset, @project, { :text => l(:label_download_revision), :class => 'icon icon-package' }) %>
 
 <% form_tag({:action => controller.action_name, :id => @project, :path => to_path_param(@path), :rev => ''}, {:method => :get, :id => 'revision_selector'}) do -%>
   <!-- Branches Dropdown -->
@@ -19,3 +19,5 @@
   | <%= l(:label_revision) %>: 
   <%= text_field_tag 'rev', @rev, :size => 8 %>
 <% end -%>
+
+
--- a/app/views/repositories/revision.rhtml	Mon Nov 14 12:36:17 2011 +0000
+++ b/app/views/repositories/revision.rhtml	Wed Nov 23 15:29:25 2011 +0000
@@ -1,11 +1,15 @@
 <div class="contextual">
+
+  <%= link_to_revision_archive(@repository, @changeset, @project, { :text => l(:label_download_revision), :class => 'icon icon-package' }) %>
+  &nbsp;&nbsp;
+
   &#171;
   <% unless @changeset.previous.nil? -%>
     <%= link_to_revision(@changeset.previous, @project, :text => l(:label_previous)) %>
   <% else -%>
     <%= l(:label_previous) %>
   <% end -%>
-|
+
   <% unless @changeset.next.nil? -%>
     <%= link_to_revision(@changeset.next, @project, :text => l(:label_next)) %>
   <% else -%>
@@ -21,6 +25,7 @@
     <%= text_field_tag 'rev', @rev, :size => 8 %>
     <%= submit_tag 'OK', :name => nil %>
   <% end %>
+
 </div>
 
 <h2><%= l(:label_revision) %> <%= format_revision(@changeset) %></h2>
--- a/app/views/repositories/show.rhtml	Mon Nov 14 12:36:17 2011 +0000
+++ b/app/views/repositories/show.rhtml	Wed Nov 23 15:29:25 2011 +0000
@@ -51,9 +51,14 @@
                        :id => @project, :page => nil, :key => User.current.rss_key})) %>
 <%     end %>
 
+<p class="statistics">
+<%= link_to l(:label_statistics), {:action => 'stats', :id => @project}, :class => 'icon icon-stats' %>
+</p>
+
 <%     other_formats_links do |f| %>
   <%= f.link_to 'Atom', :url => {:action => 'revisions', :id => @project, :key => User.current.rss_key} %>
 <%     end %>
+
 <%   end %>
 <% end %>
 
--- a/config/locales/en.yml	Mon Nov 14 12:36:17 2011 +0000
+++ b/config/locales/en.yml	Wed Nov 23 15:29:25 2011 +0000
@@ -169,7 +169,7 @@
   notice_failed_to_save_issues: "Failed to save %{count} issue(s) on %{total} selected: %{ids}."
   notice_failed_to_save_members: "Failed to save member(s): %{errors}."
   notice_no_issue_selected: "No issue is selected! Please, check the issues you want to edit."
-  notice_account_pending: "Your account was created and is now pending administrator approval."
+  notice_account_pending: "Your account is now awaiting administrator approval. You will receive a notification email when your account has been activated."
   notice_default_data_loaded: Default configuration successfully loaded.
   notice_unable_delete_version: Unable to delete version.
   notice_unable_delete_time_entry: Unable to delete time log entry.
@@ -698,6 +698,7 @@
   label_latest_revision_plural: Latest revisions
   label_view_revisions: View revisions
   label_view_all_revisions: View all revisions
+  label_download_revision: Download as Zip
   label_max_size: Maximum size
   label_sort_highest: Move to top
   label_sort_higher: Move up
--- a/extra/soundsoftware/SoundSoftware.pm	Mon Nov 14 12:36:17 2011 +0000
+++ b/extra/soundsoftware/SoundSoftware.pm	Wed Nov 23 15:29:25 2011 +0000
@@ -110,6 +110,11 @@
     req_override => OR_AUTHCFG,
     args_how => TAKE1,
   },
+  {
+    name => 'SoundSoftwareSslRequired',
+    req_override => OR_AUTHCFG,
+    args_how => TAKE1,
+  },
 );
 
 sub SoundSoftwareDSN { 
@@ -143,6 +148,8 @@
     }
 }
 
+sub SoundSoftwareSslRequired { set_val('SoundSoftwareSslRequired', @_); }
+
 sub trim {
     my $string = shift;
     $string =~ s/\s{2,}/ /g;
@@ -184,33 +191,80 @@
 
     my $project_id = get_project_identifier($dbh, $r);
 
-    if (!defined $read_only_methods{$method}) {
-        print STDERR "SoundSoftware.pm:$$: Method is not read-only\n";
-        if (project_repo_is_readonly($dbh, $project_id, $r)) {
-            print STDERR "SoundSoftware.pm:$$: Project repo is read-only, refusing access\n";
-	    return FORBIDDEN;
-        } else {
-	    print STDERR "SoundSoftware.pm:$$: Project repo is read-write, authentication handler required\n";
-            return OK;
-        }
-    }
+    # We want to delegate most of the work to the authentication
+    # handler (to ensure that user is asked to login even for 
+    # nonexistent projects -- so they can't tell whether a private
+    # project exists or not without authenticating). So 
+    # 
+    # * if the project is public
+    #   - if the method is read-only
+    #     + set handler to OK, no auth needed
+    #   - if the method is not read-only
+    #     + if the repo is read-only, return forbidden
+    #     + else require auth
+    # * if the project is not public or does not exist
+    #     + require auth
+    #
+    # If we are requiring auth and are not currently https, and
+    # https is required, then we must return a redirect to https
+    # instead of an OK.
 
     my $status = get_project_status($dbh, $project_id, $r);
+    my $readonly = project_repo_is_readonly($dbh, $project_id, $r);
 
     $dbh->disconnect();
     undef $dbh;
 
-    if ($status == 0) { # nonexistent
-	print STDERR "SoundSoftware.pm:$$: Project does not exist, refusing access\n";
-	return FORBIDDEN;
-    } elsif ($status == 1) { # public
-	print STDERR "SoundSoftware.pm:$$: Project is public, no restriction here\n";
-	$r->set_handlers(PerlAuthenHandler => [\&OK])
-    } else { # private
-	print STDERR "SoundSoftware.pm:$$: Project is private, authentication handler required\n";
+    my $auth_ssl_reqd = will_require_ssl_auth($r);
+
+    if ($status == 1) { # public
+
+	print STDERR "SoundSoftware.pm:$$: Project is public\n";
+
+	if (!defined $read_only_methods{$method}) {
+
+	    print STDERR "SoundSoftware.pm:$$: Method is not read-only\n";
+
+	    if ($readonly) {
+		print STDERR "SoundSoftware.pm:$$: Project repo is read-only, refusing access\n";
+		return FORBIDDEN;
+	    } else {
+		print STDERR "SoundSoftware.pm:$$: Project repo is read-write, auth required\n";
+		# fall through, this is the normal case
+	    }
+
+        } elsif ($auth_ssl_reqd and $r->unparsed_uri =~ m/cmd=branchmap/) {
+
+            # A hac^H^H^Hspecial case. We want to ensure we switch to
+            # https (if it will be necessarily for authentication) 
+            # before the first POST request, and this is what I think
+            # will give us suitable warning for Mercurial.
+
+            print STDERR "SoundSoftware.pm:$$: Switching to HTTPS in preparation\n";
+            # fall through, this is the normal case
+
+	} else {
+	    # Public project, read-only method -- this is the only
+	    # case we can decide for certain to accept in this function
+	    print STDERR "SoundSoftware.pm:$$: Method is read-only, no restriction here\n";
+	    $r->set_handlers(PerlAuthenHandler => [\&OK]);
+	    return OK;
+	}
+
+    } else { # status != 1, i.e. nonexistent or private -- equivalent here
+
+	print STDERR "SoundSoftware.pm:$$: Project is private or nonexistent, auth required\n";
+	# fall through
     }
 
-    return OK
+    if ($auth_ssl_reqd) {
+        my $redir_to = "https://" . $r->hostname() . $r->unparsed_uri();
+        print STDERR "SoundSoftware.pm:$$: Need to switch to HTTPS, redirecting to $redir_to\n";
+        $r->headers_out->add('Location' => $redir_to);
+        return REDIRECT;
+    } else {
+        return OK;
+    }
 }
 
 sub authen_handler {
@@ -237,6 +291,16 @@
     
     print STDERR "SoundSoftware.pm:$$: User is " . $r->user . ", got password\n";
 
+    my $status = get_project_status($dbh, $project_id, $r);
+    if ($status == 0) {
+	# nonexistent, behave like private project you aren't a member of
+	print STDERR "SoundSoftware.pm:$$: Project doesn't exist, not permitted\n";
+	$dbh->disconnect();
+	undef $dbh;
+	$r->note_auth_failure();
+	return AUTH_REQUIRED;
+    }
+
     my $permitted = is_permitted($dbh, $project_id, $r->user, $redmine_pass, $r);
     
     $dbh->disconnect();
@@ -279,6 +343,30 @@
     $ret;
 }
 
+sub will_require_ssl_auth {
+    my $r = shift;
+
+    my $cfg = Apache2::Module::get_config
+        (__PACKAGE__, $r->server, $r->per_dir_config);
+
+    if ($cfg->{SoundSoftwareSslRequired} eq "on") {
+        if ($r->dir_config('HTTPS') eq "on") {
+            # already have ssl
+            return 0;
+        } else {
+            # require ssl for auth, don't have it yet
+            return 1;
+        }
+    } elsif ($cfg->{SoundSoftwareSslRequired} eq "off") {
+        # don't require ssl for auth
+        return 0;
+    } else {
+        print STDERR "WARNING: SoundSoftware.pm:$$: SoundSoftwareSslRequired should be either 'on' or 'off'\n";
+        # this is safer
+        return 1;
+    }
+}
+
 sub project_repo_is_readonly {
     my $dbh = shift;
     my $project_id = shift;
@@ -368,6 +456,7 @@
 		}
 		$sthldap->finish();
 		undef $sthldap;
+                last if ($ret);
 	    }
 	} else {
 	    print STDERR "SoundSoftware.pm:$$: User $redmine_user lacks required role for this project\n";
@@ -383,14 +472,13 @@
 sub get_project_identifier {
     my $dbh = shift;
     my $r = shift;
-
     my $location = $r->location;
-    my ($repo) = $r->uri =~ m{$location/*([^/]+)};
+    my ($repo) = $r->uri =~ m{$location/*([^/]*)};
 
     return $repo if (!$repo);
 
     $repo =~ s/[^a-zA-Z0-9\._-]//g;
-
+    
     # The original Redmine.pm returns the string just calculated as
     # the project identifier.  That won't do for us -- we may have
     # (and in fact already do have, in our test instance) projects
@@ -410,7 +498,6 @@
 
     my $prefix = $cfg->{SoundSoftwareRepoPrefix};
     if (!defined $prefix) { $prefix = '%/'; }
-
     my $identifier = '';
 
     $sth->execute($prefix . $repo);
@@ -449,6 +536,18 @@
     # to project identifier if any are found
     if ($name =~ m/[^\w\d\s\._-]/) {
 	$name = $project_id;
+    } elsif ($name =~ m/^\s*$/) {
+	# empty or whitespace
+	$name = $project_id;
+    }
+    
+    if ($name =~ m/^\s*$/) {
+        # nothing even in $project_id -- probably a nonexistent project.
+        # use repo name instead (don't want to admit to user that project
+        # doesn't exist)
+        my $location = $r->location;
+        my ($repo) = $r->uri =~ m{$location/*([^/]*)};
+        $name = $repo;
     }
 
     my $realm = '"Mercurial repository for ' . "'$name'" . '"';
--- a/public/themes/soundsoftware/stylesheets/application.css	Mon Nov 14 12:36:17 2011 +0000
+++ b/public/themes/soundsoftware/stylesheets/application.css	Wed Nov 23 15:29:25 2011 +0000
@@ -134,6 +134,8 @@
 #footer { background-color: #fdfbf5; border: 0; border-top: 2px solid #a9b680; color: #3e442c; text-align: right; }
 #footer a { color: #be5700; font-weight: bold; }
 
+p.statistics { text-align: right; font-size:0.9em; color: #666; }
+
 #main { margin-top: 135px; font:95%; background: #fdfaf0; }
 #main a { font-weight: medium; color: #be5700;}
 #main a:hover { color: #be5700; text-decoration: underline; }
@@ -229,6 +231,10 @@
 	margin-top:136px;
 }
 
+#my_projects_fieldset.collapsible {
+	font-size: 1.0em;
+	font-color: green;
+}
 
 
 
--- a/vendor/plugins/redmine_bibliography/app/helpers/publications_helper.rb	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_bibliography/app/helpers/publications_helper.rb	Wed Nov 23 15:29:25 2011 +0000
@@ -12,8 +12,12 @@
   def projects_check_box_tags(name, projects)
     s = ''
     projects.sort.each do |project|
-      s << "<label>#{ check_box_tag name, project.id, false } #{link_to_project project}</label>\n"
+      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 
   end
   
@@ -43,12 +47,12 @@
     f.hidden_field(:_destroy) + link_to_function(name, "remove_fields(this)", :class => 'icon icon-del')
   end
     
-  def link_to_add_fields(name, f, association)
+  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    
-    link_to_function(name, h("add_fields(this, '#{association}', '#{escape_javascript(fields)}')"), { :class => 'icon icon-add', :id => "add_another_author" })
+    link_to_function(name, h("add_author_fields(this, '#{association}', '#{escape_javascript(fields)}', '#{action}')"), { :class => 'icon icon-add', :id => "add_another_author" })
   end  
 
   def sanitized_object_name(object_name)
--- a/vendor/plugins/redmine_bibliography/app/views/projects/_bibliography_box.html.erb	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_bibliography/app/views/projects/_bibliography_box.html.erb	Wed Nov 23 15:29:25 2011 +0000
@@ -7,18 +7,16 @@
    <dl>
      <% @project.publications.each do |publication| %>
      <dt>
-       <%= link_to publication.title, :controller => 'publications', :action => 'show', :id => publication, :project_id => @project %>
-     </dt>
-     <dd>
      <span class="authors">
-       <%= publication.authorships.map { |a| h a.name_on_paper }.join(', ') %>
+       <%= 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">
-       <%= publication.bibtex_entry.year %>
+       &nbsp;(<%= publication.bibtex_entry.year %>)
      </span>
      <% end %>
-     </dd>
+     </dt><dd></dd>
    <% end -%>
    </dl>
   </div>
--- a/vendor/plugins/redmine_bibliography/app/views/projects/show.rhtml	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_bibliography/app/views/projects/show.rhtml	Wed Nov 23 15:29:25 2011 +0000
@@ -101,6 +101,7 @@
 </div>
 
 <% content_for :sidebar do %>
+    <%= call_hook(:view_projects_show_sidebar_top, :project => @project) %>
     <% if @total_hours && User.current.allowed_to?(:view_time_entries, @project) %>
     <h3><%= l(:label_spent_time) %></h3>
     <p><span class="icon icon-time"><%= l_hours(@total_hours) %></span></p>
--- a/vendor/plugins/redmine_bibliography/app/views/publications/_authorship_fields.rhtml	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_bibliography/app/views/publications/_authorship_fields.rhtml	Wed Nov 23 15:29:25 2011 +0000
@@ -19,11 +19,11 @@
 
       <p style="margin-bottom: -2.5em; padding-bottom; 0"><label><%= l(:identify_author_question) %></label></p>
       <p class="author_identify">
-        <label class='inline'><%= radio_button_tag(:identify_author, "yes", false, :name => form_tag_name(f.object_name,:identify_author ), :id => form_tag_id( f.object_name, :identify_author_yes ), :onclick => "identify_author_status($(this).value, #{form_object_id(f.object_name) });") %> <%= l(:identify_author_yes) %> </label><br />
+        <label class='inline'><%= radio_button_tag(:identify_author, "yes", false, :name => form_tag_name(f.object_name,:identify_author ), :id => form_tag_id( f.object_name, :identify_author_yes ), :onchange => "identify_author_status($(this).value, #{form_object_id(f.object_name) });") %> <%= l(:identify_author_yes) %> </label><br />
        
-        <label class='inline'><%= radio_button_tag(:identify_author, "correct", false, :name => form_tag_name(f.object_name,:identify_author ), :id => form_tag_id( f.object_name, :identify_author_corrections ), :onclick => "identify_author_status($(this).value, #{form_object_id(f.object_name) });") %> <%= l(:identify_author_correct) %> </label><br />
+        <label class='inline'><%= radio_button_tag(:identify_author, "correct", false, :name => form_tag_name(f.object_name,:identify_author ), :id => form_tag_id( f.object_name, :identify_author_corrections ), :onchange => "identify_author_status($(this).value, #{form_object_id(f.object_name) });") %> <%= l(:identify_author_correct) %> </label><br />
         
-        <label class='inline'><%= radio_button_tag(:identify_author, "no", true, :name => form_tag_name(f.object_name,:identify_author ), :id => form_tag_id( f.object_name, :identify_author_no ), :onclick => "identify_author_status($(this).value, #{form_object_id(f.object_name) });") %> <%= l(:identify_author_no) %> </label><br />
+        <label class='inline'><%= radio_button_tag(:identify_author, "no", true, :name => form_tag_name(f.object_name,:identify_author ), :id => form_tag_id( f.object_name, :identify_author_no ), :onchange => "identify_author_status($(this).value, #{form_object_id(f.object_name) });") %> <%= l(:identify_author_no) %> </label><br />
       </p>
     </div>	
   
@@ -45,12 +45,11 @@
 
   <p>
 
-  <% if params[:action] == 'new' %>
-   <%= button_to_function l(:label_save_author), {}, { :onclick => "toggle_save_author(#{form_object_id(f.object_name)}); return false;", :id => form_tag_id( f.object_name, :edit_save_button )} %>
-  <% else %>
-<%= button_to_function l(:label_edit_author), {}, { :onclick => "toggle_save_author(#{form_object_id(f.object_name)}); return false;", :id => form_tag_id( f.object_name, :edit_save_button )} %>
-
- <% end %>
+  <%- if params[:action] == 'new' -%>
+    <%= button_to_function l(:label_save_author), {}, { :onclick => "toggle_save_author(#{form_object_id(f.object_name)}); return false;", :id => form_tag_id( f.object_name, :edit_save_button )} %>
+  <%- else -%>
+    <%= button_to_function l(:label_edit_author), {}, { :onclick => "toggle_save_author(#{form_object_id(f.object_name)}); return false;", :id => form_tag_id( f.object_name, :edit_save_button )} %>
+  <%- end -%>
 
 
   <%= link_to_remove_fields l("remove_author"), f %>
--- a/vendor/plugins/redmine_bibliography/app/views/publications/_form.html.erb	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_bibliography/app/views/publications/_form.html.erb	Wed Nov 23 15:29:25 2011 +0000
@@ -24,7 +24,7 @@
     <% f.fields_for :authorships do |builder| -%>
       <%= render "authorship_fields", :f => builder %>
     <%- end -%>
-    <%= link_to_add_fields l(:label_add_an_author), f, :authorships %>
+    <%= link_to_add_author_fields l(:label_add_an_author), f, :authorships, params[:action] %>
   </div>
 </div>
 
--- a/vendor/plugins/redmine_bibliography/app/views/users/show.rhtml	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_bibliography/app/views/users/show.rhtml	Wed Nov 23 15:29:25 2011 +0000
@@ -51,18 +51,17 @@
 
     <% @publications.each do |publication|%>    
       <dt>
-        <%= link_to publication.title, :controller => 'publications', :action => 'show', :id => publication %>
-      </dt>
-
-      <dd>
         <span class="authors">
-          <%= publication.authorships.map { |a| h a.name_on_paper }.join(', ') %>
+          <%= 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 %></span>
         <% if publication.bibtex_entry.year.to_s != "" %>
           <span class="year">
-            <%= publication.bibtex_entry.year %>
+            &nbsp;(<%= publication.bibtex_entry.year %>)
           </span>
         <% end %>
+      </dt>
+      <dd>
       </dd>
   	<% end %>
   </div>
--- a/vendor/plugins/redmine_bibliography/assets/javascripts/authors.js	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_bibliography/assets/javascripts/authors.js	Wed Nov 23 15:29:25 2011 +0000
@@ -3,12 +3,15 @@
     $(link).up(".fields").hide();
 }
 
-function add_fields(link, association, content) {
-    var new_id = new Date().getTime();
-    var regexp = new RegExp("new_" + association, "g")
-    $(link).insert({
-	before: content.replace(regexp, new_id)
-    });
+function add_author_fields(link, association, content, action) {
+	var new_id = new Date().getTime();
+  var regexp = new RegExp("new_" + association, "g");
+  $(link).insert({
+		before: content.replace(regexp, new_id)
+  });
+	if(action != "new"){
+		toggle_save_author(new_id, $(link));
+	};
 }
 
 function identify_author_status(status, object_id) {
--- a/vendor/plugins/redmine_bibliography/assets/stylesheets/bibliography.css	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_bibliography/assets/stylesheets/bibliography.css	Wed Nov 23 15:29:25 2011 +0000
@@ -39,6 +39,7 @@
 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 .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; }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/redmine_tags/app/views/auto_completes/_search_tag_list.html.erb	Wed Nov 23 15:29:25 2011 +0000
@@ -0,0 +1,5 @@
+<ul>
+	<% @tags.each do |tag| -%>
+  		<%= content_tag 'li', h('%s (%d)' % [tag.name, tag.count]), :name => tag.name %>
+	<% end -%>
+</ul>
--- a/vendor/plugins/redmine_tags/app/views/auto_completes/_tag_list.html.erb	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_tags/app/views/auto_completes/_tag_list.html.erb	Wed Nov 23 15:29:25 2011 +0000
@@ -2,5 +2,5 @@
 <% @tags.each do |tag| -%>
   <%= content_tag 'li', h('%s (%d)' % [tag.name, tag.count]), :name => tag.name %>
 <% end -%>
-<%= content_tag 'li', l(:auto_complete_new_tag) % @name, :name => @name %>
+  <%= content_tag 'li', l(:auto_complete_new_tag) % @name, :name => @name %>
 </ul>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/redmine_tags/app/views/projects/_filter_search_tags.html.erb	Wed Nov 23 15:29:25 2011 +0000
@@ -0,0 +1,12 @@
+<p class='tag'>
+  <%- fields_for @project, :builder => TabularFormBuilder do |f| -%>
+    <div>
+      <p id="project_tags">
+        <%= f.text_field :tag_list, :label => :label_tags_search, :size => 60, :class => 'hol' -%>
+      </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)" -%>
+    </div>
+  <%- end -%>
+</p>
--- a/vendor/plugins/redmine_tags/app/views/projects/_filter_tags.rhtml	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_tags/app/views/projects/_filter_tags.rhtml	Wed Nov 23 15:29:25 2011 +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')}')" %>
+      <%= javascript_tag "observeProjectTagsField('#{url_for(:controller => 'auto_completes', :action => 'project_tags', :project_id => Project.first.id)}')" %>
     </div>
   <% end -%>
 </p>
--- a/vendor/plugins/redmine_tags/app/views/projects/_filtered_projects.rhtml	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_tags/app/views/projects/_filtered_projects.rhtml	Wed Nov 23 15:29:25 2011 +0000
@@ -1,1 +1,7 @@
-<%= render_project_table_with_filtering(@filtered_projects, @question) %>
\ No newline at end of file
+<% if @projects.empty? %>
+<p><b><%= l(:project_filter_no_results) %></b></p>
+<% else %>
+<%= render_project_table_with_filtering(@projects, @question) %>
+<% end %>
+
+<p class="pagination"><%= pagination_links_full @project_pages, @project_count %></p>
--- a/vendor/plugins/redmine_tags/app/views/projects/_my_projects.rhtml	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_tags/app/views/projects/_my_projects.rhtml	Wed Nov 23 15:29:25 2011 +0000
@@ -1,8 +1,17 @@
-<fieldset id="filters" class="collapsible">
-    <legend onclick="toggleFieldset(this);"><%= l(:label_my_projects) %></legend>
-    <% if @user_projects %>  
+<% if !@user_projects.empty? %>
+<% if @myproj_status=="true" %>
+<fieldset id="my_projects_fieldset" class="collapsible">
+    <legend onclick="toggleFieldsetWithState(this);"><h2><%= l(:label_my_project_plural) %></h2></legend>
+<% else %>
+<fieldset id="my_projects_fieldset" class="collapsible collapsed">
+    <legend onclick="toggleFieldsetWithState(this);"><h2><%= l(:label_my_project_plural) %></h2></legend>
+    <div style="display: none;">    
+<% end %>
     <div>
-      <%= render_my_project_hierarchy(@user_projects)%>
+      <%= render_my_project_hierarchy_with_tags(@user_projects)%>
     </div>
-  <% end %>
-</fieldset>
\ No newline at end of file
+  <% unless @myproj_status=="true" %>
+    </div>
+  <%- end -%>
+</fieldset>
+<% end %>
--- a/vendor/plugins/redmine_tags/app/views/projects/_tags.html.erb	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_tags/app/views/projects/_tags.html.erb	Wed Nov 23 15:29:25 2011 +0000
@@ -1,6 +1,7 @@
 <% unless @project.tag_list.empty? %>
-  <tr>
-    <td><b><%=l(:tags)%>:</b></td>
-    <td><%= @project.tag_counts.collect{ |t| render_project_tag_link(t) }.join(', ') %></td>
-  </tr>
+  <%= stylesheet_link_tag 'redmine_tags', :plugin => 'redmine_tags' %>
+  <dl class="tags">
+    <dt class="tags-title"><%=l(:tags)%></dt>
+    <dd class="tags"><%= @project.tag_counts.collect{ |t| render_project_tag_link(t) }.join(', ') %></dd>
+  </dl>
 <% end %>
--- a/vendor/plugins/redmine_tags/app/views/projects/_tags_form.html.erb	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_tags/app/views/projects/_tags_form.html.erb	Wed Nov 23 15:29:25 2011 +0000
@@ -3,6 +3,6 @@
   <p id="project_tags"><%= f.text_field :tag_list, :label => :tags, :size => 60, :class => 'hol' %></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)}')" %>
+  <%= javascript_tag "observeProjectTagsField('#{url_for(:controller => 'auto_completes', :action => 'project_tags', :project_id => project)}', false)" %>
 </div>
 <% end -%>
\ No newline at end of file
--- a/vendor/plugins/redmine_tags/app/views/projects/index.rhtml	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_tags/app/views/projects/index.rhtml	Wed Nov 23 15:29:25 2011 +0000
@@ -1,6 +1,9 @@
 <% content_for :header_tags do %>
     <%= auto_discovery_link_tag(:atom, {:action => 'index', :format => 'atom', :key => User.current.rss_key}) %>
+    <%= stylesheet_link_tag 'redmine_tags', :plugin => 'redmine_tags' %>
 <% end %>
+<%= javascript_include_tag 'projects_index', :plugin => 'redmine_tags' %>
+
 
 <div class="contextual">
     <%= link_to l(:label_overall_activity), { :controller => 'activities', :action => 'index' }%>
@@ -14,36 +17,51 @@
   <%= render :partial => 'my_projects' %>
 <% end %>
 
-
-<div style="clear:both;"></div>
-<% form_tag('/projects', :method => :get, :id => :project_filtering) do %>
-  <fieldset id="filters" class="collapsible">
-    <legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
-    <div>
-      <p class='q'>
-        <%= label_tag 'q', l('project_filtering_q_label') %>
-        <%= text_field_tag 'q', @question, :size => 30, :id => 'search-input' %>
-      </p>
-
-      <div id='filter_tags'>
-        <%= render :partial => 'filter_tags' %>
-      </div>
-
-      <p class='buttons'><%= submit_tag( l('button_filter'), :id => 'filter_button') -%></p>                  
-    </div>
-  </fieldset>
-<% end %>
-
 <div style="clear:both;"></div>
 <h2>
   <%= l("label_project_all") %>
 </h2>
 
+<div style="clear:both;"></div>
+  <%- form_remote_tag(:controller => :projects, :action => :index, :method => :get, :html => {:id => :project_filtering_form}) do -%>
+
+    <% if @filter_status=="true" %>
+      <fieldset id="filters_fieldset" class="collapsible">
+      <legend onclick="toggleFieldsetWithState(this);"><%= l(:label_filter_plural) %></legend>
+    <%- else -%>
+      <fieldset id="filters_fieldset" class="collapsible collapsed">
+      <legend onclick="toggleFieldsetWithState(this);"><%= l(:label_filter_plural) %></legend>
+      <div style="display: none;">
+    <%- end -%>
+
+  <div>
+    <div id='filter_tags'>
+      <%= render :partial => 'filter_search_tags' -%>
+    </div>
+
+    <p class='q'>
+      <%= label_tag 'q', l('project_filtering_q_label') %>
+      <%= text_field_tag 'q', @question, :size => 30, :id => 'search-input' %>
+    </p>
+
+    <p style="display: none;"><%= submit_tag( l('button_filter'), :id => 'submitButton') -%></p>
+    <%= link_to l(:button_apply), {}, :onclick => "$('submitButton').click(); return false;", :class => 'icon icon-checked' -%>
+    <%= link_to l(:button_clear), {}, :class => 'icon icon-reload'  %>                         
+  </div>
+  
+    <% unless @filter_status=="true" %>
+      </div>
+    <%- end -%>
+
+
+<%- end -%>
+</fieldset>
+
 <div id="projects">
   <%= render :partial => 'filtered_projects' %>
 </div>
 
-<p class="pagination"><%= pagination_links_full @project_pages, @project_count %></p>
+
 
 
 <% other_formats_links do |f| %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/redmine_tags/assets/javascripts/projects_index.js	Wed Nov 23 15:29:25 2011 +0000
@@ -0,0 +1,37 @@
+function toggleFieldsetWithState(this_field){
+	id = Element.up(this_field, 'fieldset').id;	
+	// is the fieldset collapsed?
+	status = $(id).hasClassName("collapsed");
+	change_session(id, status);
+	
+	toggleFieldset(this_field);
+
+};
+
+	function submitForm(){
+		$('submitButton').click();		
+	};
+
+function change_session(id, nstatus) {
+	var url = "projects/set_fieldset_status";
+ 	var request = new Ajax.Request(url, {
+		method: 'post',
+	 	parameters: {field_id: id, status: nstatus},
+    	asynchronous: true
+  	});
+}
+
+function keypressHandler (event){
+  var key = event.which || event.keyCode;
+  switch (key) {
+      default:
+      break;
+      case Event.KEY_RETURN:
+          $('submitButton').click(); return false;
+      break;   
+  };
+};
+
+document.observe("dom:loaded", function() {
+	$('search-input').observe('keypress', keypressHandler);	
+});
\ No newline at end of file
--- a/vendor/plugins/redmine_tags/assets/javascripts/tags_input.js	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_tags/assets/javascripts/tags_input.js	Wed Nov 23 15:29:25 2011 +0000
@@ -21,12 +21,19 @@
 var Redmine = Redmine || {};
 
 Redmine.TagsInput = Class.create({
-  initialize: function(element) {
+  initialize: function(element, update) {
     this.element  = $(element);
     this.input    = new Element('input', { 'type': 'text', 'autocomplete': 'off', 'size': 10 });
     this.button   = new Element('span', { 'class': 'tag-add icon icon-add' });
     this.tags     = new Hash();
     
+		this.update = update;
+		
+		var uri_params = window.location.href.toQueryParams();
+		if (uri_params["project[tag_list]"] != undefined){
+			this.addTag(uri_params["project[tag_list]"].stripTags(), true);			
+		};
+		
     Event.observe(this.button, 'click', this.readTags.bind(this));
     Event.observe(this.input, 'keypress', this.onKeyPress.bindAsEventListener(this));
 
@@ -35,21 +42,26 @@
     this.addTagsList(this.element.value);
   },
 
-  readTags: function() {
+  readTags: function() {		
     this.addTagsList(this.input.value);
     this.input.value = '';
+		if(this.update){
+			submitForm();
+		};
   },
 
   onKeyPress: function(event) {
     if (Event.KEY_RETURN == event.keyCode) {
       this.readTags(event);
-      Event.stop(event);
+      Event.stop(event);			
     }
   },
 
-  addTag: function(tag) {
+  addTag: function(tag, noSubmit) {
     if (tag.blank() || this.tags.get(tag)) return;
 
+		if(noSubmit==undefined){noSubmit=false;}
+
     var button = new Element('span', { 'class': 'tag-delete icon icon-del' });
     var label  = new Element('span', { 'class': 'tag-label' }).insert(tag).insert(button);
 
@@ -57,10 +69,17 @@
     this.element.value = this.getTagsList();
     this.element.insert({ 'before': label });
 
+		if(noSubmit==false){
+			if(this.update){
+				submitForm();
+			};
+		};
+
     Event.observe(button, 'click', function(){
       this.tags.unset(tag);
       this.element.value = this.getTagsList();
       label.remove();
+		  if(this.update){submitForm();};
     }.bind(this));
   },
 
@@ -90,10 +109,13 @@
 
 
 function observeIssueTagsField(url) {
-  new Redmine.TagsInput('issue_tag_list').autocomplete('issue_tag_candidates', url);
+  new Redmine.TagsInput('issue_tag_list', false).autocomplete('issue_tag_candidates', url);
 }
 
-
-function observeProjectTagsField(url) {
-  new Redmine.TagsInput('project_tag_list').autocomplete('project_tag_candidates', url);
+function observeProjectTagsField(url, update) {
+	if(!update) { 
+			var update = false;
+		};
+	
+	new Redmine.TagsInput('project_tag_list', update).autocomplete('project_tag_candidates', url);
 }
\ No newline at end of file
--- a/vendor/plugins/redmine_tags/assets/stylesheets/redmine_tags.css	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_tags/assets/stylesheets/redmine_tags.css	Wed Nov 23 15:29:25 2011 +0000
@@ -33,3 +33,11 @@
 div.tags .tag-nube-8 { font-size: 1.5em; }
 
 .tag-count { font-size: .75em; margin-left: .5em; }
+
+ul.projects .tags, ul.projects .no-tags { padding-left: 0.5em; color: #3e442c; font-size: 0.95em }
+table.projects th.tags { color: #3e442c; }
+
+dl.tags { margin-top: 1.5em; margin-left: 0; padding-bottom: 1em; overflow: hidden; margin-bottom: 1em; border-bottom: 1px dotted #bbbbbb; }
+dt.tags-title { float: left; font-weight: bold; color: #3e442c; } 
+dd.tags { margin-left: .5em; float: left; color: #3e442c; }
+ 
--- a/vendor/plugins/redmine_tags/config/locales/en.yml	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_tags/config/locales/en.yml	Wed Nov 23 15:29:25 2011 +0000
@@ -23,6 +23,8 @@
   tags: Tags
   field_tags: Tags
   field_tag_list: Tags
+  field_no_tags: "No tags"
+  label_tags_search: "Tags: "
   setting_issue_tags: Issues Tags
   issues_sidebar: Display tags on sidebar as
   issues_show_count: Display amount of issues
@@ -34,5 +36,6 @@
 
   auto_complete_new_tag: Add new...
   
-  project_filtering_q_label: "Textual search"
+  project_filtering_q_label: "Search for text:"
+  project_filter_no_results: "No matching projects found"
   button_filter: "Filter"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/redmine_tags/config/routes.rb	Wed Nov 23 15:29:25 2011 +0000
@@ -0,0 +1,3 @@
+ActionController::Routing::Routes.draw do |map|
+  map.connect 'projects/set_fieldset_status', :controller => 'projects', :action => 'set_fieldset_status', :conditions => {:method => :post}
+end
\ No newline at end of file
--- a/vendor/plugins/redmine_tags/lib/redmine_tags/hooks/views_projects_hook.rb	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_tags/lib/redmine_tags/hooks/views_projects_hook.rb	Wed Nov 23 15:29:25 2011 +0000
@@ -2,7 +2,7 @@
   module Hooks
     class ViewsProjectsHook < Redmine::Hook::ViewListener
       render_on :view_projects_form, :partial => 'projects/tags_form'
-      render_on :view_projects_show_left, :partial => 'projects/tags'
+      render_on :view_projects_show_sidebar_top, :partial => 'projects/tags'
 #      render_on :view_issues_sidebar_planning_bottom, :partial => 'issues/tags_sidebar'
     end
   end
--- a/vendor/plugins/redmine_tags/lib/redmine_tags/patches/auto_completes_controller_patch.rb	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_tags/lib/redmine_tags/patches/auto_completes_controller_patch.rb	Wed Nov 23 15:29:25 2011 +0000
@@ -38,6 +38,12 @@
           @tags = Project.available_tags :name_like => @name
           render :layout => false, :partial => 'tag_list'
         end
+        
+        def project_search_tags
+          @name = params[:q].to_s
+          @tags = Project.available_tags :name_like => @name
+          render :layout => false, :partial => 'search_tag_list'
+        end
       end
     end
   end
--- a/vendor/plugins/redmine_tags/lib/redmine_tags/patches/projects_controller_patch.rb	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_tags/lib/redmine_tags/patches/projects_controller_patch.rb	Wed Nov 23 15:29:25 2011 +0000
@@ -2,17 +2,24 @@
 
 module RedmineTags
   module Patches
-    module ProjectsControllerPatch
+    module ProjectsControllerPatch      
       def self.included(base)
         base.send(:include, InstanceMethods)
         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]
-          before_filter :filter_projects, :only => :index                
+#          before_filter :filter_projects, :only => :index
+
+          alias :index filtered_index
         end
       end
 
       module InstanceMethods
+        
+        
+        
         def add_tags_to_project
 
           if params && params[:project] && !params[:project][:tag_list].nil?
@@ -25,35 +32,88 @@
           end
         end
 
-        # # luisf - TO BE REMOVED?
-        # def calculate_project_filtering_settings
-        #   @project_filtering_settings = Setting[:plugin_redmine_project_filtering]
-        # end
+        def paginate_projects
+          sort_init 'name'
+          sort_update %w(name lft created_on updated_on)
+          @limit = per_page_option
+          @project_count = Project.visible_roots.find(@projects).count
+          @project_pages = ActionController::Pagination::Paginator.new self, @project_count, @limit, params['page']
+          @offset ||= @project_pages.current.offset
+        end
 
-        def filter_projects
+        def set_fieldset_status
+
+          # luisf. test for missing parameters………
+          field = params[:field_id]
+          status = params[:status]
+
+          session[(field + "_status").to_sym] = status
+          render :nothing => true
+        end
+
+        # gets the status of the collabsible fieldsets
+        def get_fieldset_statuses
+          if session[:my_projects_fieldset_status].nil?
+            @myproj_status = "true"
+          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
+        # (subprojects belong to them)
+        def filtered_index
           @project = Project.new
+          filter_projects
+          get_fieldset_statuses
 
           respond_to do |format|
-            format.any(:html, :xml) {
-              calculate_filtered_projects
+            format.html { 
+              paginate_projects
+              
+              @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.rhtml', :layout => !request.xhr?
+            }
+            format.api {
+              @offset, @limit = api_offset_and_limit
+              @project_count = Project.visible.count
+              @projects = Project.visible.find(@projects, :offset => @offset, :limit => @limit, :order => 'lft')
+            }
+            format.atom {
+              projects = Project.visible.find(:all, :order => 'created_on DESC', :limit => Setting.feeds_limit.to_i)
+              render_feed(projects, :title => "#{Setting.app_title}: #{l(:label_project_latest)}")
             }
             format.js {
-              calculate_filtered_projects
+              paginate_projects
+              @projects = Project.visible_roots.find(@projects, :offset => @offset, :limit => @limit, :order => sort_clause)
               render :update do |page|
                 page.replace_html 'projects', :partial => 'filtered_projects'
               end
             }
-            format.atom {
-              projects = Project.visible.find(:all, :order => 'created_on DESC',
-              :limit => Setting.feeds_limit.to_i)
-              render_feed(projects, :title => "#{Setting.app_title}: #{l(:label_project_latest)}")
-            }
           end
         end
 
         private
 
-        def calculate_filtered_projects                  
+        def filter_projects                  
           @question = (params[:q] || "").strip     
 
           if params.has_key?(:project)
@@ -62,20 +122,20 @@
             @tag_list = []
           end
 
-          @projects = Project.visible
+          if  @question == ""
+            @projects = Project.visible
+          else
+            @projects = 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) }
+            @projects = @projects & @tagged_projects_ids
+          end
+          
+          @projects = @projects.collect{ |project| project.root }
+          @projects = @projects.uniq
 
-          @featured_projects = @projects.featured if Project.respond_to? :featured
-
-          # luisf 
-          @projects = @projects.search_by_question(@question) unless @question == ""
-          @tagged_projects_ids = Project.tagged_with(@tag_list).collect{ |project| Project.find(project.id) } unless @tag_list.empty?
-
-          debugger
-
-          # intersection of both prject groups            
-          @projects = @projects && @tagged_projects_ids unless @tag_list.empty?
-          
-          @filtered_projects = @projects
         end
       end
     end
--- a/vendor/plugins/redmine_tags/lib/redmine_tags/patches/projects_helper_patch.rb	Mon Nov 14 12:36:17 2011 +0000
+++ b/vendor/plugins/redmine_tags/lib/redmine_tags/patches/projects_helper_patch.rb	Wed Nov 23 15:29:25 2011 +0000
@@ -18,9 +18,7 @@
         # 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)
-          debugger
-          
+        def render_project_table_with_filtering(projects, question)          
           custom_fields = ""
           s = ""
           if projects.any?
@@ -31,8 +29,8 @@
             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 << "<th class='tags'>" << l("tags") << "</th>"
             s << sort_header_tag('created_on', :default_order => 'desc')
             s << sort_header_tag('updated_on', :default_order => 'desc')
         
@@ -66,7 +64,12 @@
           s << " no_description" if project.description.blank?
           s << "'>" << link_to( highlight_tokens(project.name, tokens), {:controller => 'projects', :action => 'show', :id => project}, :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}")
           s << "</div>"
-          s << render_project_short_description(project)
+          s << highlight_tokens(render_project_short_description(project), tokens)
+          s << "</td>"
+
+          # taglist
+          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
@@ -88,8 +91,6 @@
 
           s << "</td>"
           
-          # taglist
-          s << "<td class='tags' align=top>" << project.tag_counts.collect{ |t| render_project_tag_link(t) }.join(', ') << "</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>"
 
@@ -162,6 +163,86 @@
           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
+        # (eg. some intermediate nodes are private and can not be seen)
+        def render_my_project_hierarchy_with_tags(projects)
+
+          s = ''
+
+          original_project = @project
+
+          projects.each do |project|
+            if project.root? || !projects.include?(project.parent)
+              s << render_my_project_in_hierarchy_with_tags(project)
+            end
+          end
+
+          @project = original_project
+
+          if s != ''
+            a = ''
+            a << "<ul class='projects root'>\n"
+            a << s
+            a << "</ul>\n"
+            s = a
+          end
+
+          s
+
+        end
+        
+        
+        
+
+        def render_my_project_in_hierarchy_with_tags(project)
+
+          s = ''
+
+          if User.current.member_of?(project)
+
+            # set the project environment to please macros.
+            @project = project
+
+            classes = (project.root? ? 'root' : 'child')
+
+            s << "<li class='#{classes}'><div class='#{classes}'>" +
+              link_to_project(project, {}, :class => "project my-project")
+            if project.is_public?
+              s << " <span class='public'>" << l(:field_is_public) << "</span>"
+            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>"
+            else
+              s << " <span class='tags'>" << tc.collect{ |t| render_project_tag_link(t) }.join(', ') << "</span>"
+            end
+
+            s << render_project_short_description(project)
+
+            s << "</div>\n"
+
+            cs = ''
+            project.children.each do |child|
+              cs << render_my_project_in_hierarchy_with_tags(child)
+            end
+
+            if cs != ''
+              s << "<ul class='projects'>\n" << cs << "</ul>\n";
+            end
+
+          end
+
+          s
+
+        end
+
+        
+        
         private
         
         # copied from search_helper. This one doesn't escape html or limit the text length