Reuse ProjectQuery filters on the admin project list (#33422).

Patch by Takenori TAKAKI.


git-svn-id: https://svn.redmine.org/redmine/trunk@21519 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
Marius Balteanu
2022-04-01 15:08:52 +00:00
parent 44344cfe8a
commit 83ed32e8d7
11 changed files with 211 additions and 58 deletions

View File

@@ -26,19 +26,23 @@ class AdminController < ApplicationController
before_action :require_admin before_action :require_admin
helper :queries
include QueriesHelper
helper :projects_queries
helper :projects
def index def index
@no_configuration_data = Redmine::DefaultData::Loader::no_data? @no_configuration_data = Redmine::DefaultData::Loader::no_data?
end end
def projects def projects
@status = params[:status] || 1 retrieve_query(ProjectQuery, false, :defaults => @default_columns_names)
@query.admin_projects = 1
scope = @query.results_scope
scope = Project.status(@status).sorted @entry_count = scope.count
scope = scope.like(params[:name]) if params[:name].present? @entry_pages = Paginator.new @entry_count, per_page_option, params['page']
@projects = scope.limit(@entry_pages.per_page).offset(@entry_pages.offset).to_a
@project_count = scope.count
@project_pages = Paginator.new @project_count, per_page_option, params['page']
@projects = scope.limit(@project_pages.per_page).offset(@project_pages.offset).to_a
render :action => "projects", :layout => false if request.xhr? render :action => "projects", :layout => false if request.xhr?
end end

View File

@@ -52,6 +52,7 @@ class QueriesController < ApplicationController
@query.user = User.current @query.user = User.current
@query.project = @project @query.project = @project
@query.build_from_params(params) @query.build_from_params(params)
render :layout => 'admin' if params[:admin_projects]
end end
def create def create
@@ -62,13 +63,14 @@ class QueriesController < ApplicationController
if @query.save if @query.save
flash[:notice] = l(:notice_successful_create) flash[:notice] = l(:notice_successful_create)
redirect_to_items(:query_id => @query) redirect_to_items(:query_id => @query, :admin_projects => params[:admin_projects])
else else
render :action => 'new', :layout => !request.xhr? render :action => 'new', :layout => !request.xhr?
end end
end end
def edit def edit
render :layout => 'admin' if params[:admin_projects]
end end
def update def update
@@ -76,7 +78,7 @@ class QueriesController < ApplicationController
if @query.save if @query.save
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
redirect_to_items(:query_id => @query) redirect_to_items(:query_id => @query, :admin_projects => params[:admin_projects])
else else
render :action => 'edit' render :action => 'edit'
end end
@@ -110,10 +112,15 @@ class QueriesController < ApplicationController
@query ? @query.queried_class.to_s.underscore.pluralize.to_sym : nil @query ? @query.queried_class.to_s.underscore.pluralize.to_sym : nil
end end
def current_menu(project)
super if params[:admin_projects].nil?
end
private private
def find_query def find_query
@query = Query.find(params[:id]) @query = Query.find(params[:id])
@query.admin_projects = params[:admin_projects] if @query.is_a?(ProjectQuery)
@project = @query.project @project = @query.project
render_403 unless @query.editable_by?(User.current) render_403 unless @query.editable_by?(User.current)
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
@@ -163,8 +170,12 @@ class QueriesController < ApplicationController
end end
def redirect_to_project_query(options) def redirect_to_project_query(options)
if params[:admin_projects]
redirect_to admin_projects_path(options)
else
redirect_to projects_path(options) redirect_to projects_path(options)
end end
end
# Returns the Query subclass, IssueQuery by default # Returns the Query subclass, IssueQuery by default
# for compatibility with previous behaviour # for compatibility with previous behaviour

View File

@@ -462,6 +462,8 @@ module QueriesHelper
url_params = url_params =
if controller_name == 'issues' if controller_name == 'issues'
{:controller => 'issues', :action => 'index', :project_id => @project} {:controller => 'issues', :action => 'index', :project_id => @project}
elsif controller_name == 'admin' && action_name == 'projects'
{:admin_projects => '1'}
else else
{} {}
end end

View File

@@ -18,6 +18,8 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class ProjectQuery < Query class ProjectQuery < Query
attr_accessor :admin_projects
self.queried_class = Project self.queried_class = Project
self.view_permission = :search_project self.view_permission = :search_project
@@ -74,6 +76,12 @@ class ProjectQuery < Query
add_custom_fields_filters(project_custom_fields) add_custom_fields_filters(project_custom_fields)
end end
def build_from_params(params, defaults={})
query = super
query.admin_projects = params[:admin_projects]
query
end
def available_columns def available_columns
return @available_columns if @available_columns return @available_columns if @available_columns
@@ -84,8 +92,28 @@ class ProjectQuery < Query
end end
def available_display_types def available_display_types
if self.admin_projects
['list']
else
['board', 'list'] ['board', 'list']
end end
end
def display_type
if self.admin_projects
'list'
else
super
end
end
def project_statuses_values
values = super
if self.admin_projects
values << [l(:project_status_archived), Project::STATUS_ARCHIVED.to_s]
end
values
end
def default_columns_names def default_columns_names
@default_columns_names = Setting.project_list_defaults.symbolize_keys[:column_names].map(&:to_sym) @default_columns_names = Setting.project_list_defaults.symbolize_keys[:column_names].map(&:to_sym)
@@ -100,8 +128,12 @@ class ProjectQuery < Query
end end
def base_scope def base_scope
if self.admin_projects
Project.where(statement)
else
Project.visible.where(statement) Project.visible.where(statement)
end end
end
def results_scope(options={}) def results_scope(options={})
order_option = [group_by_sort_order, (options[:order] || sort_clause)].flatten.reject(&:blank?) order_option = [group_by_sort_order, (options[:order] || sort_clause)].flatten.reject(&:blank?)

View File

@@ -2,47 +2,21 @@
<%= link_to l(:label_project_new), new_project_path, :class => 'icon icon-add' %> <%= link_to l(:label_project_new), new_project_path, :class => 'icon icon-add' %>
</div> </div>
<%= title l(:label_project_plural) %> <h2><%= @query.new_record? ? l(:label_project_plural) : @query.name %></h2>
<%= form_tag({}, :method => :get) do %> <%= form_tag(admin_projects_path(@project, nil), :method => :get, :id => 'query_form') do %>
<fieldset><legend><%= l(:label_filter_plural) %></legend> <%= hidden_field_tag 'admin_projects', '1' %>
<label for='status'><%= l(:field_status) %>:</label> <%= render :partial => 'queries/query_form' %>
<%= select_tag 'status', project_status_options_for_select(@status), :class => "small", :onchange => "this.form.submit(); return false;" %>
<label for='name'><%= l(:label_project) %>:</label>
<%= text_field_tag 'name', params[:name], :size => 30 %>
<%= submit_tag l(:button_apply), :class => "small", :name => nil %>
<%= link_to l(:button_clear), admin_projects_path, :class => 'icon icon-reload' %>
</fieldset>
<% end %> <% end %>
&nbsp;
<% if @projects.any? %> <% if @query.valid? %>
<div class="autoscroll"> <%if @projects.any? %>
<table class="list"> <%= render :partial => 'projects/list', :locals => { :entries => @projects }%>
<thead><tr> <% else %>
<th><%=l(:label_project)%></th> <p class="nodata"><%= l(:label_no_data) %></p>
<th><%=l(:field_is_public)%></th> <% end %>
<th><%=l(:field_created_on)%></th>
<th></th>
</tr></thead>
<tbody>
<% project_tree(@projects, :init_level => true) do |project, level| %>
<tr class="<%= project.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>">
<td class="name"><span><%= link_to_project_settings(project, {}, :title => project.short_description) %></span></td>
<td><%= checked_image project.is_public? %></td>
<td><%= format_date(project.created_on) %></td>
<td class="buttons">
<%= link_to(l(:button_archive), archive_project_path(project, :status => params[:status]), :data => {:confirm => l(:text_are_you_sure)}, :method => :post, :class => 'icon icon-lock') unless project.archived? %>
<%= link_to(l(:button_unarchive), unarchive_project_path(project, :status => params[:status]), :method => :post, :class => 'icon icon-unlock') if project.archived? %>
<%= link_to(l(:button_copy), copy_project_path(project), :class => 'icon icon-copy') %>
<%= link_to(l(:button_delete), project_path(project), :method => :delete, :class => 'icon icon-del') %>
</td>
</tr>
<% end %> <% end %>
</tbody>
</table> <% content_for :sidebar do %>
</div> <%= render :partial => 'projects/sidebar' %>
<span class="pagination"><%= pagination_links_full @project_pages, @project_count %></span>
<% else %>
<p class="nodata"><%= l(:label_no_data) %></p>
<% end %> <% end %>

View File

@@ -6,6 +6,9 @@
<% @query.inline_columns.each do |column| %> <% @query.inline_columns.each do |column| %>
<%= column_header(@query, column) %> <%= column_header(@query, column) %>
<% end %> <% end %>
<% if controller_name == 'admin' && action_name == 'projects' %>
<th></th>
<% end %>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -23,12 +26,23 @@
<%= link_to_function("#{l(:button_collapse_all)}/#{l(:button_expand_all)}", <%= link_to_function("#{l(:button_collapse_all)}/#{l(:button_expand_all)}",
"toggleAllRowGroups(this)", :class => 'toggle-all') %> "toggleAllRowGroups(this)", :class => 'toggle-all') %>
</td> </td>
<% if controller_name == 'admin' && action_name == 'projects' %>
<td></td>
<% end %>
</tr> </tr>
<% end %> <% end %>
<tr id="project-<%= entry.id %>" class="<%= cycle('odd', 'even') %> <%= entry.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>"> <tr id="project-<%= entry.id %>" class="<%= cycle('odd', 'even') %> <%= entry.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>">
<% @query.inline_columns.each do |column| %> <% @query.inline_columns.each do |column| %>
<%= content_tag('td', column_content(column, entry), :class => column.css_classes) %> <%= content_tag('td', column_content(column, entry), :class => column.css_classes) %>
<% end %> <% end %>
<% if controller_name == 'admin' && action_name == 'projects' %>
<td class="buttons">
<%= link_to(l(:button_archive), archive_project_path(entry, :status => params[:status]), :data => {:confirm => l(:text_are_you_sure)}, :method => :post, :class => 'icon icon-lock') unless entry.archived? %>
<%= link_to(l(:button_unarchive), unarchive_project_path(entry, :status => params[:status]), :method => :post, :class => 'icon icon-unlock') if entry.archived? %>
<%= link_to(l(:button_copy), copy_project_path(entry), :class => 'icon icon-copy') %>
<%= link_to(l(:button_delete), project_path(entry), :method => :delete, :class => 'icon icon-del') %>
</td>
<% end %>
</tr> </tr>
<% end -%> <% end -%>
</tbody> </tbody>

View File

@@ -4,6 +4,7 @@
<div class="tabular"> <div class="tabular">
<%= hidden_field_tag 'gantt', '1' if params[:gantt] %> <%= hidden_field_tag 'gantt', '1' if params[:gantt] %>
<%= hidden_field_tag 'calendar', '1' if params[:calendar] %> <%= hidden_field_tag 'calendar', '1' if params[:calendar] %>
<%= hidden_field_tag 'admin_projects', '1' if params[:admin_projects] %>
<p><label for="query_name"><%=l(:field_name)%></label> <p><label for="query_name"><%=l(:field_name)%></label>
<%= text_field 'query', 'name', :size => 80 %></p> <%= text_field 'query', 'name', :size => 80 %></p>
@@ -34,6 +35,8 @@
<p><label for='display_type'><%= l(:label_display_type) %></label> <p><label for='display_type'><%= l(:label_display_type) %></label>
<%= available_display_types_tags(@query) %> <%= available_display_types_tags(@query) %>
</p> </p>
<% elsif @query.available_display_types.size == 1 %>
<%= hidden_field_tag 'query[display_type]', @query.available_display_types.first %>
<% end %> <% end %>
<p id ="default_columns"><label for="query_default_columns"><%=l(:label_default_columns)%></label> <p id ="default_columns"><label for="query_default_columns"><%=l(:label_default_columns)%></label>

View File

@@ -63,8 +63,9 @@
<% end %> <% end %>
<% else %> <% else %>
<% if @query.editable_by?(User.current) %> <% if @query.editable_by?(User.current) %>
<%= link_to l(:button_edit_object, object_name: l(:label_query).downcase), edit_query_path(@query), :class => 'icon icon-edit' %> <% redirect_params = (controller_name == 'admin' && action_name == 'projects') ? {:admin_projects => 1} : {} %>
<%= delete_link query_path(@query), {}, l(:button_delete_object, object_name: l(:label_query).downcase) %> <%= link_to l(:button_edit_object, object_name: l(:label_query).downcase), edit_query_path(@query, redirect_params), :class => 'icon icon-edit' %>
<%= delete_link query_path(@query, redirect_params), {}, l(:button_delete_object, object_name: l(:label_query).downcase) %>
<% end %> <% end %>
<% end %> <% end %>
</p> </p>

View File

@@ -38,29 +38,43 @@ class AdminControllerTest < Redmine::ControllerTest
assert_select 'div.nodata' assert_select 'div.nodata'
end end
def test_projects def test_projects_should_show_only_active_projects_by_default
p = Project.find(1)
p.update_column :status, 5
get :projects get :projects
assert_response :success assert_response :success
assert_select 'tr.project.closed', 0 assert_select 'tr.project.closed', 0
assert_select 'tr.project', 5
assert_select 'tr.project td.name', :text => 'OnlineStore'
assert_select 'tr.project td.name', :text => p.name, :count => 0
end end
def test_projects_with_status_filter def test_projects_with_status_filter
p = Project.find(1)
p.update_column :status, 5
get( get(
:projects, :projects,
:params => { :params => {
:status => 1 'set_filter' => '1',
'f' => ['status'],
'op' => {'status' => '='},
'v' => {'status' => ['5']}
} }
) )
assert_response :success assert_response :success
assert_select 'tr.project.closed', 0 assert_select 'tr.project', 1
assert_select 'tr.project td.name', :text => p.name
end end
def test_projects_with_name_filter def test_projects_with_name_filter
get( get(
:projects, :projects,
:params => { :params => {
:name => 'store', 'set_filter' => '1',
:status => '' 'f' => ['status', 'name'],
'op' => {'status' => '=', 'name' => '~'},
'v' => {'status' => ['1'], 'name' => ['store']}
} }
) )
assert_response :success assert_response :success

View File

@@ -585,6 +585,33 @@ class QueriesControllerTest < Redmine::ControllerTest
assert q.valid? assert q.valid?
end end
def test_create_admin_projects_query_should_redirect_to_admin_projects
@request.session[:user_id] = 1
q = new_record(ProjectQuery) do
post(
:create,
:params => {
:type => 'ProjectQuery',
:default_columns => '1',
:f => ["status"],
:op => {
"status" => "="
},
:v => {
"status" => ['1']
},
:query => {
"name" => "test_new_project_public_query", "visibility" => "2"
},
:admin_projects => 1
}
)
end
assert_redirected_to :controller => 'admin', :action => 'projects', :query_id => q.id, :admin_projects => 1
end
def test_edit_global_public_query def test_edit_global_public_query
@request.session[:user_id] = 1 @request.session[:user_id] = 1
get(:edit, :params => {:id => 4}) get(:edit, :params => {:id => 4})
@@ -690,6 +717,33 @@ class QueriesControllerTest < Redmine::ControllerTest
assert q.valid? assert q.valid?
end end
def test_update_admin_projects_query
q = ProjectQuery.create(:name => 'project_query')
@request.session[:user_id] = 1
put(
:update,
:params => {
:id => q.id,
:default_columns => '1',
:fields => ["status"],
:operators => {
"status" => "="
},
:values => {
"status" => ['1']
},
:query => {
"name" => "test_project_query_updated", "visibility" => "2"
},
:admin_projects => 1
}
)
assert_redirected_to :controller => 'admin', :action => 'projects', :query_id => q.id, :admin_projects => 1
assert Query.find_by_name('test_project_query_updated')
end
def test_update_with_failure def test_update_with_failure
@request.session[:user_id] = 1 @request.session[:user_id] = 1
put( put(

View File

@@ -62,6 +62,18 @@ class ProjectQueryTest < ActiveSupport::TestCase
assert_include :cf_3, query.available_columns.map(&:name) assert_include :cf_3, query.available_columns.map(&:name)
end end
def test_available_display_types_should_returns_bord_and_list
query = ProjectQuery.new
query.admin_projects = nil
assert_equal ['board', 'list'], query.available_display_types
end
def test_available_display_types_should_always_returns_list_when_admin_projects_is_set
query = ProjectQuery.new
query.admin_projects = 1
assert_equal ['list'], query.available_display_types
end
def test_display_type_default_should_equal_with_setting_project_list_display_type def test_display_type_default_should_equal_with_setting_project_list_display_type
ProjectQuery.new.available_display_types.each do |t| ProjectQuery.new.available_display_types.each do |t|
with_settings :project_list_display_type => t do with_settings :project_list_display_type => t do
@@ -104,4 +116,36 @@ class ProjectQueryTest < ActiveSupport::TestCase
assert_nil ProjectQuery.default assert_nil ProjectQuery.default
end end
def test_display_type_should_returns_list_when_admin_projects_is_set
q = ProjectQuery.new
q.admin_projects = 1
assert_equal 'list', q.display_type
end
def test_project_statuses_values_should_equal_ancestors_return
ancestor = Query.new
q = ProjectQuery.new
assert_equal ancestor.project_statuses_values, q.project_statuses_values
end
def test_project_statuses_values_should_includes_project_status_archeved_when_admin_projects_is_set
q = ProjectQuery.new
q.admin_projects = 1
assert_includes q.project_statuses_values, [l(:project_status_archived), Project::STATUS_ARCHIVED.to_s]
Query.new.project_statuses_values.each do |status|
assert_includes q.project_statuses_values, status
end
end
def test_base_scope_should_return_visible_projects
q = ProjectQuery.new
assert_equal Project.visible, q.base_scope
end
def test_base_scope_should_return_all_projects_when_admin_projects_is_set
q = ProjectQuery.new
q.admin_projects = 1
assert_equal Project.all, q.base_scope
end
end end