Commit 404bfce4 authored by Jean-Philippe Lang's avatar Jean-Philippe Lang

Added a cross-project issue list. It displays the issues of all the projects visible by the user.

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
parent 1187ad96
......@@ -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
......@@ -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
......
......@@ -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
<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 %>
......@@ -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 %>
......@@ -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 %>
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment