OSDN Git Service

Added a cross-project issue list. It displays the issues of all the projects visible...
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Fri, 31 Aug 2007 17:02:44 +0000 (17:02 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Fri, 31 Aug 2007 17:02:44 +0000 (17:02 +0000)
The users list available in the filters ('assigned to' / 'created by') is made of the members of all projects the current user belongs to.
For now, this view is only accessible from 'My page' ('issues assigned to me' or 'issues reported by me' blocks, to view the full lists)

On 'My page', assigned issue are now sorted by priority.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@684 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/controllers/issues_controller.rb
app/models/project.rb
app/models/query.rb
app/views/issues/index.rhtml [new file with mode: 0644]
app/views/my/blocks/_issuesassignedtome.rhtml
app/views/my/blocks/_issuesreportedbyme.rhtml
test/functional/my_controller_test.rb

index 56a9b11..fe1c788 100644 (file)
@@ -17,7 +17,7 @@
 
 class IssuesController < ApplicationController
   layout 'base', :except => :export_pdf
-  before_filter :find_project, :authorize
+  before_filter :find_project, :authorize, :except => :index
   
   cache_sweeper :issue_sweeper, :only => [ :edit, :change_status, :destroy ]
 
@@ -32,8 +32,27 @@ class IssuesController < ApplicationController
   helper :watchers
   include WatchersHelper
   helper :attachments
-  include AttachmentsHelper   
+  include AttachmentsHelper
+  helper :queries
+  helper :sort
+  include SortHelper
 
+  def index
+    sort_init "#{Issue.table_name}.id", "desc"
+    sort_update
+    retrieve_query
+    if @query.valid?
+      @issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement)             
+      @issue_pages = Paginator.new self, @issue_count, 25, params['page']                                                              
+      @issues = Issue.find :all, :order => sort_clause,
+                           :include => [ :assigned_to, :status, :tracker, :project, :priority ],
+                           :conditions => @query.statement,
+                           :limit  =>  @issue_pages.items_per_page,
+                           :offset =>  @issue_pages.current.offset                                             
+    end
+    render :layout => false if request.xhr?
+  end
+  
   def show
     @status_options = @issue.status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker) if logged_in_user
     @custom_values = @issue.custom_values.find(:all, :include => :custom_field)
@@ -149,5 +168,25 @@ private
     @html_title = "#{@project.name} - #{@issue.tracker.name} ##{@issue.id}"
   rescue ActiveRecord::RecordNotFound
     render_404
-  end  
+  end
+  
+  # Retrieve query from session or build a new query
+  def retrieve_query
+    if params[:set_filter] or !session[:query] or session[:query].project_id
+      # Give it a name, required to be valid
+      @query = Query.new(:name => "_", :executed_by => logged_in_user)
+      if params[:fields] and params[:fields].is_a? Array
+        params[:fields].each do |field|
+          @query.add_filter(field, params[:operators][field], params[:values][field])
+        end
+      else
+        @query.available_filters.keys.each do |field|
+          @query.add_short_filter(field, params[field]) if params[field]
+        end
+      end
+      session[:query] = @query
+    else
+      @query = session[:query]
+    end
+  end
 end
index 241c658..f994ed6 100644 (file)
@@ -77,11 +77,11 @@ class Project < ActiveRecord::Base
 
   def self.visible_by(user=nil)
     if user && user.admin?
-      return ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"]
+      return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"
     elsif user && user.memberships.any?
-      return ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND (#{Project.table_name}.is_public = ? or #{Project.table_name}.id IN (#{user.memberships.collect{|m| m.project_id}.join(',')}))", true]
+      return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND (#{Project.table_name}.is_public = #{connection.quoted_true} or #{Project.table_name}.id IN (#{user.memberships.collect{|m| m.project_id}.join(',')}))"
     else
-      return ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND #{Project.table_name}.is_public = ?", true]
+      return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND #{Project.table_name}.is_public = #{connection.quoted_true}"
     end
   end
   
index ff519d7..c700242 100644 (file)
@@ -91,14 +91,20 @@ class Query < ActiveRecord::Base
                            "updated_on" => { :type => :date_past, :order => 10 },
                            "start_date" => { :type => :date, :order => 11 },
                            "due_date" => { :type => :date, :order => 12 } }                          
-    unless project.nil?
-      # project specific filters
-      user_values = []
+    
+    user_values = []
+    if project
+      user_values += project.users.collect{|s| [s.name, s.id.to_s] }
+    elsif executed_by
       user_values << ["<< #{l(:label_me)} >>", "me"] if executed_by
-      user_values += @project.users.collect{|s| [s.name, s.id.to_s] }
-      
-      @available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => user_values }  
-      @available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values }  
+      # members of the user's projects
+      user_values += executed_by.projects.collect(&:users).flatten.uniq.sort.collect{|s| [s.name, s.id.to_s] }
+    end
+    @available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => user_values } unless user_values.empty?
+    @available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values } unless user_values.empty?
+  
+    if project
+      # project specific filters      
       @available_filters["category_id"] = { :type => :list_optional, :order => 6, :values => @project.issue_categories.collect{|s| [s.name, s.id.to_s] } }
       @available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => @project.versions.sort.collect{|s| [s.name, s.id.to_s] } }
       unless @project.active_children.empty?
@@ -166,7 +172,7 @@ class Query < ActiveRecord::Base
   def statement
     # project/subprojects clause
     clause = ''
-    if has_filter?("subproject_id")
+    if project && has_filter?("subproject_id")
       subproject_ids = []
       if operator_for("subproject_id") == "="
         subproject_ids = values_for("subproject_id").each(&:to_i)
@@ -174,8 +180,10 @@ class Query < ActiveRecord::Base
         subproject_ids = project.active_children.collect{|p| p.id}
       end
       clause << "#{Issue.table_name}.project_id IN (%d,%s)" % [project.id, subproject_ids.join(",")] if project
+    elsif project
+      clause << "#{Issue.table_name}.project_id=%d" % project.id
     else
-      clause << "#{Issue.table_name}.project_id=%d" % project.id if project
+      clause << Project.visible_by(executed_by)
     end
     
     # filters clauses
@@ -239,7 +247,8 @@ class Query < ActiveRecord::Base
       filters_clauses << sql
     end if filters and valid?
     
-    clause << (' AND ' + filters_clauses.join(' AND ')) unless filters_clauses.empty?
+    clause << ' AND ' unless clause.empty?
+    clause << filters_clauses.join(' AND ') unless filters_clauses.empty?
     clause
   end
 end
diff --git a/app/views/issues/index.rhtml b/app/views/issues/index.rhtml
new file mode 100644 (file)
index 0000000..0f63ba9
--- /dev/null
@@ -0,0 +1,55 @@
+<h2><%=l(:label_issue_plural)%></h2>
+    
+<% form_tag({}, :id => 'query_form') do %>
+<%= render :partial => 'queries/filters', :locals => {:query => @query} %>
+<% end %>
+<div class="contextual">
+<%= link_to_remote l(:button_apply), 
+                   { :url => { :set_filter => 1 },
+                     :update => "content",
+                     :with => "Form.serialize('query_form')"
+                   }, :class => 'icon icon-edit' %>
+                   
+<%= link_to_remote l(:button_clear),
+                   { :url => { :set_filter => 1 }, 
+                     :update => "content",
+                   }, :class => 'icon icon-reload'  %>
+</div>
+<br />&nbsp;
+
+<%= error_messages_for 'query' %>
+<% if @query.valid? %>
+<% if @issues.empty? %>
+<p><i><%= l(:label_no_data) %></i></p>
+<% else %>
+&nbsp;
+<table class="list">
+    <thead><tr>
+               <%= sort_header_tag("#{Issue.table_name}.id", :caption => '#') %>
+               <%= sort_header_tag("#{Project.table_name}.name", :caption => l(:field_project)) %>
+               <%= sort_header_tag("#{Issue.table_name}.tracker_id", :caption => l(:field_tracker)) %>
+               <%= sort_header_tag("#{IssueStatus.table_name}.name", :caption => l(:field_status)) %>
+               <%= sort_header_tag("#{Issue.table_name}.priority_id", :caption => l(:field_priority)) %>
+               <th><%=l(:field_subject)%></th>
+               <%= sort_header_tag("#{User.table_name}.lastname", :caption => l(:field_assigned_to)) %>
+               <%= sort_header_tag("#{Issue.table_name}.updated_on", :caption => l(:field_updated_on)) %>
+       </tr></thead>
+       <tbody>
+       <% for issue in @issues %>
+       <tr class="<%= cycle("odd", "even") %>">
+               <td align="center" valign="top"><%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %></td>
+               <td align="center" valign="top" nowrap><%=h issue.project.name %></td>
+               <td align="center" valign="top" nowrap><%= issue.tracker.name %></td>
+               <td  valign="top"nowrap><div class="square" style="background:#<%= issue.status.html_color %>;"></div> <%= issue.status.name %></td>
+               <td align="center" valign="top"><%= issue.priority.name %></td>
+               <td><%= link_to h(issue.subject), :controller => 'issues', :action => 'show', :id => issue %></td>
+               <td align="center" valign="top" nowrap><%= issue.assigned_to.name if issue.assigned_to %></td>
+               <td align="center" valign="top" nowrap><%= format_time(issue.updated_on) %></td>
+       </tr>
+       <% end %>
+       </tbody>
+</table>
+<p><%= pagination_links_full @issue_pages %>
+[ <%= @issue_pages.current.first_item %> - <%= @issue_pages.current.last_item %> / <%= @issue_count %> ]</p>
+<% end %>
+<% end %>
index 0d49279..9b97cb3 100644 (file)
@@ -3,8 +3,8 @@
                                 :conditions => ["assigned_to_id=? AND #{IssueStatus.table_name}.is_closed=? AND #{Project.table_name}.status=#{Project::STATUS_ACTIVE}", user.id, false],
                                 :limit => 10, 
                                 :include => [ :status, :project, :tracker ], 
-                                :order => "#{Issue.table_name}.updated_on DESC") %>
+                                :order => "#{Issue.table_name}.priority_id DESC, #{Issue.table_name}.updated_on DESC") %>
 <%= render :partial => 'issues/list_simple', :locals => { :issues => assigned_issues } %>
 <% if assigned_issues.length > 0 %>
-<p><%=lwr(:label_last_updates, assigned_issues.length)%></p>
+<p class="small"><%= link_to l(:label_issue_view_all), :controller => 'issues', :action => 'index', :set_filter => 1, :assigned_to_id => 'me' %></p>
 <% end %>
index 250e826..a077084 100644 (file)
@@ -6,5 +6,5 @@
                                 :order => "#{Issue.table_name}.updated_on DESC") %>
 <%= render :partial => 'issues/list_simple', :locals => { :issues => reported_issues } %>
 <% if reported_issues.length > 0 %>
-<p><%=lwr(:label_last_updates, reported_issues.length)%></p>
-<% end %>
\ No newline at end of file
+<p class="small"><%= link_to l(:label_issue_view_all), :controller => 'issues', :action => 'index', :set_filter => 1, :author_id => 'me' %></p>
+<% end %>
index a53991a..b2389c3 100644 (file)
@@ -22,6 +22,8 @@ require 'my_controller'
 class MyController; def rescue_action(e) raise e end; end
 
 class MyControllerTest < Test::Unit::TestCase
+  fixtures :users
+  
   def setup
     @controller = MyController.new
     @request    = ActionController::TestRequest.new
@@ -50,8 +52,7 @@ class MyControllerTest < Test::Unit::TestCase
 
   def test_update_account
     post :account, :user => {:firstname => "Joe", :login => "root", :admin => 1}
-    assert_response :success
-    assert_template 'account'
+    assert_redirected_to 'my/account'
     user = User.find(2)
     assert_equal user, assigns(:user)
     assert_equal "Joe", user.firstname