changeset 136:bf38a7365edf live

Merge fixes for bug #51 and bug #35 from branch "cannam-pre-20110113-merge"
author Chris Cannam
date Wed, 19 Jan 2011 15:34:59 +0000
parents cede720e8f53 (current diff) 6a2f8e88344e (diff)
children 637d05516475
files public/themes/soundsoftware/stylesheets/application.css
diffstat 12 files changed, 141 insertions(+), 68 deletions(-) [+]
line wrap: on
line diff
--- a/app/controllers/members_controller.rb	Mon Dec 20 22:15:06 2010 +0000
+++ b/app/controllers/members_controller.rb	Wed Jan 19 15:34:59 2011 +0000
@@ -94,6 +94,7 @@
   
   def autocomplete_for_member
     @principals = Principal.active.like(params[:q]).find(:all, :limit => 100) - @project.principals
+    logger.debug "Query for #{params[:q]} returned #{@principals.size} results"
     render :layout => false
   end
 
--- a/app/controllers/projects_controller.rb	Mon Dec 20 22:15:06 2010 +0000
+++ b/app/controllers/projects_controller.rb	Wed Jan 19 15:34:59 2011 +0000
@@ -45,12 +45,22 @@
   helper :repositories
   include RepositoriesHelper
   include ProjectsHelper
-  
+
   # Lists visible projects
   def index
     respond_to do |format|
       format.html { 
-        @projects = Project.visible.find(:all, :order => 'lft') 
+        sort_init 'lft'
+        sort_update %w(lft title created_on updated_on)
+        @limit = per_page_option
+        @project_count = Project.visible.count
+        @project_pages = Paginator.new self, @project_count, @limit, params['page']
+        @offset ||= @project_pages.current.offset
+        @projects = Project.visible.all(:offset => @offset, :limit => @limit, :order => sort_clause) 
+        if User.current.logged?
+          @user_projects = User.current.projects.sort_by(&:lft)
+        end
+        render :template => 'projects/index.rhtml', :layout => !request.xhr?
       }
       format.xml  {
         @projects = Project.visible.find(:all, :order => 'lft')
--- a/app/helpers/application_helper.rb	Mon Dec 20 22:15:06 2010 +0000
+++ b/app/helpers/application_helper.rb	Wed Jan 19 15:34:59 2011 +0000
@@ -273,7 +273,7 @@
   def principals_check_box_tags(name, principals)
     s = ''
     principals.sort.each do |principal|
-      s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
+      s << "<label>#{ check_box_tag name, principal.id, false } #{link_to_user principal}</label>\n"
     end
     s 
   end
--- a/app/helpers/projects_helper.rb	Mon Dec 20 22:15:06 2010 +0000
+++ b/app/helpers/projects_helper.rb	Wed Jan 19 15:34:59 2011 +0000
@@ -121,7 +121,12 @@
 
           classes = (ancestors.empty? ? 'root' : 'child')
           s << "<li class='#{classes}'><div class='#{classes}'>" +
-                 link_to_project(project, {}, :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}")
+                 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
           s << "<div class='wiki description'>#{textilizable(project.short_description, :project => project)}</div>" unless project.description.blank?
           s << "</div>\n"
           ancestors << project
@@ -143,66 +148,93 @@
     a
   end
 
-  # Renders a tree of projects where the current DOES NOT belong
-  # 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_other_project_hierarchy(projects)
-    a = ''
-    s = ''
+  # 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.
+  def render_project_table(projects)
 
-    # True if user has any projects (affects the heading used)
-    t = FALSE
+    s = ""
+    s << "<div class='autoscroll'>"
+    s << "<table class='list projects'>"
+    s << "<thead><tr>"
+    
+    s << sort_header_tag('lft', :caption => l("field_name"), :default_order => 'desc')
+    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')
 
-    if projects.any?
-      ancestors = []
-      original_project = @project
-      projects.each do |project|
-        # set the project environment to please macros.
+    s << "</tr></thead><tbody>"
 
-        @project = project
+    ancestors = []
+    original_project = @project
+    oddeven = 'even'
+    level = 0
 
-        if not User.current.member_of?(project):
+    projects.each do |project|
 
-          if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
-            s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>\n"
-          else
-            ancestors.pop
-            s << "</li>"
-            while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
-              ancestors.pop
-              s << "</ul></li>\n"
+      # set the project environment to please macros.
+
+      @project = project
+
+      if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
+        level = level + 1
+      else
+        level = 0
+        oddeven = cycle('odd','even')
+        ancestors.pop
+        while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
+          ancestors.pop
+        end
+      end
+      
+      classes = (ancestors.empty? ? 'root' : 'child')
+
+      s << "<tr class='#{oddeven} #{classes} level#{level}'>"
+      s << "<td class='firstcol name hosted_here'>" << link_to_project(project, {}, :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}") << "</td>"
+      s << "<td class='managers'>"
+
+      u = project.users_by_role
+      if u
+        u.keys.each do |r|
+          if r.allowed_to?(:edit_project)
+            mgrs = []
+            u[r].sort.each do |m|
+              mgrs << link_to_user(m)
+            end
+            if mgrs.size < 3
+              s << '<nobr>' << mgrs.join(', ') << '</nobr>'
+            else
+              s << mgrs.join(', ')
             end
           end
+        end
+      end
 
-          classes = (ancestors.empty? ? 'root' : 'child')
-          s << "<li class='#{classes}'><div class='#{classes}'>" +
-                 link_to_project(project, {}, :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}")
-          s << "<div class='wiki description'>#{textilizable(project.short_description, :project => project)}</div>" unless project.description.blank?
-          s << "</div>\n"
-          ancestors << project          
-        else
-          t = TRUE
-        end
-       end
+      s << "</td>"
+      s << "<td class='created_on'>" << format_date(project.created_on) << "</td>"
+      s << "<td class='updated_on'>" << format_date(project.updated_on) << "</td>"
 
-      s << ("</li></ul>\n" * ancestors.size)
-      @project = original_project
+      s << "</tr>"
+      s << "<tr class='#{oddeven} #{classes}'>"
+      s << "<td class='firstcol wiki description'>"
+      s << textilizable(project.short_description, :project => project) unless project.description.blank?
+      s << "</td>"
+      s << "<td colspan=3>&nbsp;</td>"
+      s << "</tr>"
+
+      ancestors << project          
     end
 
-    if t == TRUE
-      a << "<h2>"
-      a <<  l("label_other_project_plural")
-      a << "</h2>"
-      a << s
-    else
-      a << "<h2>"
-      a << l("label_project_all")
-      a << "</h2>"
-      a << s
-    end
+    s << "</table>"
 
-    a
+    @project = original_project
+
+    s
   end
 
 
--- a/app/models/project.rb	Mon Dec 20 22:15:06 2010 +0000
+++ b/app/models/project.rb	Wed Jan 19 15:34:59 2011 +0000
@@ -418,7 +418,14 @@
   
   # Returns a short description of the projects (first lines)
   def short_description(length = 255)
-    description.gsub(/^(.{#{length}}[^\n\r]*).*$/m, '\1...').strip if description
+    ## Original Redmine code: this truncates to the CR that is more
+    ## than "length" characters from the start.
+    # description.gsub(/^(.{#{length}}[^\n\r]*).*$/m, '\1...').strip if description
+    ## That's too much for us, and also we want to omit images and the
+    ## like.  Truncate instead to the first CR that follows _any_
+    ## non-blank text, and to the next word break beyond "length"
+    ## characters if the result is still longer than that.
+    description.gsub(/![^\s]+!/, '').gsub(/^(\s*[^\n\r]*).*$/m, '\1').gsub(/^(.{#{length}}\b).*$/m, '\1 ...').strip if description
   end
 
   def css_classes
--- a/app/views/members/autocomplete_for_member.rhtml	Mon Dec 20 22:15:06 2010 +0000
+++ b/app/views/members/autocomplete_for_member.rhtml	Wed Jan 19 15:34:59 2011 +0000
@@ -1,1 +1,3 @@
-<%= principals_check_box_tags 'member[user_ids][]', @principals %>
\ No newline at end of file
+<% if params[:q] && params[:q].length > 1 %>
+<%= principals_check_box_tags 'member[user_ids][]', @principals %>
+<% end %>
--- a/app/views/projects/index.rhtml	Mon Dec 20 22:15:06 2010 +0000
+++ b/app/views/projects/index.rhtml	Wed Jan 19 15:34:59 2011 +0000
@@ -8,18 +8,19 @@
     <%= '| ' + link_to(l(:label_project_new), {:controller => 'projects', :action => 'new'}, :class => 'icon icon-add') if User.current.allowed_to?(:add_project, nil, :global => true) %>
 </div>
 
-<% if User.current.logged? %>
+<% if @user_projects %>
   
-  <%= render_my_project_hierarchy(@projects)%>
+  <%= render_my_project_hierarchy(@user_projects)%>
 
-  <%= render_other_project_hierarchy(@projects)%>
-
-<% else %>
-
-  <h2><%=l(:label_project_plural)%></h2>
-  <%= render_project_hierarchy(@projects)%>
 <% end %>
 
+<h2>
+<%= l("label_project_all") %>
+</h2>
+
+<%= render_project_table(@projects) %>
+
+<p class="pagination"><%= pagination_links_full @project_pages, @project_count %></p>
 
 
 <% other_formats_links do |f| %>
--- a/app/views/projects/settings/_members.rhtml	Mon Dec 20 22:15:06 2010 +0000
+++ b/app/views/projects/settings/_members.rhtml	Wed Jan 19 15:34:59 2011 +0000
@@ -50,7 +50,7 @@
 </div>
 
 
-<% principals = Principal.active.find(:all, :limit => 10, :order => 'type, login, lastname ASC') - @project.principals %>
+<% principals = Principal.active.find(:all, :limit => 100, :order => 'type, login, lastname ASC') - @project.principals %>
 
 <div class="splitcontentright">
 <% if roles.any? && principals.any? %>
@@ -66,9 +66,11 @@
                  :url => { :controller => 'members', :action => 'autocomplete_for_member', :id => @project },
                  :with => 'q')
                   %>
-		
+
 		<div id="principals">
-			<%= principals_check_box_tags 'member[user_ids][]', principals %>
+		<% if params[:q] && params[:q].length > 1 %>
+  		  <%= principals_check_box_tags 'member[user_ids][]', @principals %>
+		<% end %>
 		</div>
 		
     <p><%= l(:label_role_plural) %>:
--- a/app/views/projects/settings/_repository.rhtml	Mon Dec 20 22:15:06 2010 +0000
+++ b/app/views/projects/settings/_repository.rhtml	Wed Jan 19 15:34:59 2011 +0000
@@ -7,7 +7,7 @@
 
 <div class="box tabular">
 <% if !@repository || !@repository.url %>
-<ul><li>The repository for a project will normally be set up automatically within a few minutes of the project being created.</li></ul>
+<ul><li><%= l(:text_settings_repo_creation) %></li></ul>
 <% end %>
 <p><%= label_tag('repository_scm', l(:label_scm)) %><%= scm_select_tag(@repository) %></p>
 <%= repository_field_tags(f, @repository) if @repository %>
--- a/config/locales/en-GB.yml	Mon Dec 20 22:15:06 2010 +0000
+++ b/config/locales/en-GB.yml	Wed Jan 19 15:34:59 2011 +0000
@@ -237,6 +237,7 @@
   field_role: Role
   field_homepage: Homepage
   field_is_public: Public
+  field_is_private: Private
   field_parent: Subproject of
   field_is_in_roadmap: Issues displayed in roadmap
   field_login: Login
@@ -430,6 +431,7 @@
     other: "{{count}} projects"
   label_project_all: All Projects
   label_project_latest: Latest projects
+  label_managers: Managed by
   label_issue: Issue
   label_issue_new: New issue
   label_issue_plural: Issues
@@ -841,6 +843,7 @@
   text_tip_issue_end_day: task ending this day
   text_tip_issue_begin_end_day: task beginning and ending this day
   text_project_identifier_info: 'Only lower case letters (a-z), numbers and dashes are allowed.<br /> This will be used in all project-related URLs, and as the repository name. Once saved, the identifier <b>can not</b> be changed.'
+  text_project_homepage_info: 'Link to an external project page.'
   text_project_name_info: "This will be the name of your project throughout this site.<br /> You can change your project's name at any time, in the project's settings."
   text_project_visibility_info: "If your project is not public, it will only be visible to users that you have added as project members."
   text_user_ssamr_description_info: 'Please describe your current research or development interests, within the fields of audio and music.<br/>This information is publicly visible in your profile and you can edit it at any time.<br/>It may also be used to establish eligibility for your initial registration.'
@@ -885,6 +888,7 @@
   text_wiki_page_destroy_children: "Delete child pages and all their descendants"
   text_wiki_page_reassign_children: "Reassign child pages to this parent page"
   text_own_membership_delete_confirmation: "You are about to remove some or all of your permissions and may no longer be able to edit this project after that.\nAre you sure you want to continue?"
+  text_settings_repo_creation: The repository for a project should be set up automatically within a few minutes of the project being created.<br>You should not have to adjust any settings here; please check again in ten minutes.
   
   default_role_manager: Manager
   default_role_developer: Developer
@@ -943,7 +947,7 @@
   label_user_mail_option_only_my_events: Only for things I watch or I'm involved in
   label_user_mail_option_only_assigned: Only for things I am assigned to
   notice_not_authorized_archived_project: The project you're trying to access has been archived.
-  label_principal_search: "Search for user or group:"
+  label_principal_search: "Search by name:"
   label_user_search: "Search for user:"
   field_visible: Visible
   setting_emails_header: Emails header
--- a/config/locales/en.yml	Mon Dec 20 22:15:06 2010 +0000
+++ b/config/locales/en.yml	Wed Jan 19 15:34:59 2011 +0000
@@ -242,6 +242,7 @@
   field_role: Role
   field_homepage: Homepage
   field_is_public: Public
+  field_is_private: Private
   field_parent: Subproject of
   field_is_in_roadmap: Issues displayed in roadmap
   field_login: Login
@@ -444,6 +445,7 @@
     other: "{{count}} projects"
   label_project_all: All Projects
   label_project_latest: Latest projects
+  label_managers: Managed by
   label_issue: Issue
   label_issue_new: New issue
   label_issue_plural: Issues
@@ -793,7 +795,7 @@
   label_profile: Profile
   label_subtask_plural: Subtasks
   label_project_copy_notifications: Send email notifications during the project copy
-  label_principal_search: "Search for user or group:"
+  label_principal_search: "Search by name:"
   label_user_search: "Search for user:"
   
   button_login: Login
@@ -914,6 +916,7 @@
   text_own_membership_delete_confirmation: "You are about to remove some or all of your permissions and may no longer be able to edit this project after that.\nAre you sure you want to continue?"
   text_zoom_in: Zoom in
   text_zoom_out: Zoom out
+  text_settings_repo_creation: The repository for a project should be set up automatically within a few minutes of the project being created.<br>You should not have to adjust any settings here.<br>Please check again in ten minutes, and <a href="/projects/soundsoftware-site/wiki/Help">contact us</a> if there is any problem.
   
   default_role_manager: Manager
   default_role_developer: Developer
--- a/public/themes/soundsoftware/stylesheets/application.css	Mon Dec 20 22:15:06 2010 +0000
+++ b/public/themes/soundsoftware/stylesheets/application.css	Wed Jan 19 15:34:59 2011 +0000
@@ -67,6 +67,17 @@
 tr.entry { border-left: 1px solid #a9b680; border-right: 1px solid #a9b680; }
 tr.entry:last-child { border-bottom: 1px solid #a9b680; }
 
+table.projects th { text-align: left; }
+table.projects th.managers { color: #3e442c; }
+table.projects .hosted_here { font-weight: bold; }
+table.projects .child .name { font-weight: normal; }
+table.projects .child .description { font-size: 0.95em; }
+table.projects .child .firstcol { padding-left: 1em }
+table.projects .level2 .firstcol { padding-left: 2em; }
+table.projects .level3 .firstcol { padding-left: 3em; }
+
+ul.projects .public, ul.projects .private { padding-left: 0.5em; color: #3e442c; font-size: 0.95em }
+
 #top-menu { position: absolute; top: 0; z-index: 1; left: 0px; width: 100%; font-size: 90%; /* height: 2em; */ margin: 0; padding: 0; padding-top: 0.5em; background-color: #3e442c; }
 #top-menu ul { margin-left: 10px; }
 #top-menu a { font-weight: bold; }