Feature #33422 » project-query-available-on-admin-project-list.patch
| app/controllers/admin_controller.rb | ||
|---|---|---|
| 26 | 26 | |
| 27 | 27 |
before_action :require_admin |
| 28 | 28 | |
| 29 |
helper :queries |
|
| 30 |
include QueriesHelper |
|
| 31 |
helper :projects_queries |
|
| 32 |
helper :projects |
|
| 33 | ||
| 29 | 34 |
def index |
| 30 | 35 |
@no_configuration_data = Redmine::DefaultData::Loader::no_data? |
| 31 | 36 |
end |
| 32 | 37 | |
| 33 | 38 |
def projects |
| 34 |
@status = params[:status] || 1 |
|
| 35 | ||
| 36 |
scope = Project.status(@status).sorted |
|
| 37 |
scope = scope.like(params[:name]) if params[:name].present? |
|
| 39 |
retrieve_query(ProjectQuery, false, :defaults => @default_columns_names) |
|
| 40 |
@query.admin_projects = 1 |
|
| 41 |
scope = @query.results_scope |
|
| 38 | 42 | |
| 39 |
@project_count = scope.count
|
|
| 40 |
@project_pages = Paginator.new @project_count, per_page_option, params['page']
|
|
| 41 |
@projects = scope.limit(@project_pages.per_page).offset(@project_pages.offset).to_a
|
|
| 43 |
@entry_count = scope.count
|
|
| 44 |
@entry_pages = Paginator.new @entry_count, per_page_option, params['page']
|
|
| 45 |
@projects = scope.limit(@entry_pages.per_page).offset(@entry_pages.offset).to_a
|
|
| 42 | 46 | |
| 43 | 47 |
render :action => "projects", :layout => false if request.xhr? |
| 44 | 48 |
end |
| app/controllers/queries_controller.rb | ||
|---|---|---|
| 52 | 52 |
@query.user = User.current |
| 53 | 53 |
@query.project = @project |
| 54 | 54 |
@query.build_from_params(params) |
| 55 |
render :layout => 'admin' if params[:admin_projects] |
|
| 55 | 56 |
end |
| 56 | 57 | |
| 57 | 58 |
def create |
| ... | ... | |
| 62 | 63 | |
| 63 | 64 |
if @query.save |
| 64 | 65 |
flash[:notice] = l(:notice_successful_create) |
| 65 |
redirect_to_items(:query_id => @query) |
|
| 66 |
redirect_to_items(:query_id => @query, :admin_projects => params[:admin_projects])
|
|
| 66 | 67 |
else |
| 67 | 68 |
render :action => 'new', :layout => !request.xhr? |
| 68 | 69 |
end |
| 69 | 70 |
end |
| 70 | 71 | |
| 71 | 72 |
def edit |
| 73 |
render :layout => 'admin' if params[:admin_projects] |
|
| 72 | 74 |
end |
| 73 | 75 | |
| 74 | 76 |
def update |
| ... | ... | |
| 76 | 78 | |
| 77 | 79 |
if @query.save |
| 78 | 80 |
flash[:notice] = l(:notice_successful_update) |
| 79 |
redirect_to_items(:query_id => @query) |
|
| 81 |
redirect_to_items(:query_id => @query, :admin_projects => params[:admin_projects])
|
|
| 80 | 82 |
else |
| 81 | 83 |
render :action => 'edit' |
| 82 | 84 |
end |
| ... | ... | |
| 110 | 112 |
@query ? @query.queried_class.to_s.underscore.pluralize.to_sym : nil |
| 111 | 113 |
end |
| 112 | 114 | |
| 115 |
def current_menu(project) |
|
| 116 |
super if params[:admin_projects].nil? |
|
| 117 |
end |
|
| 118 | ||
| 113 | 119 |
private |
| 114 | 120 | |
| 115 | 121 |
def find_query |
| 116 | 122 |
@query = Query.find(params[:id]) |
| 123 |
@query.admin_projects = params[:admin_projects] if @query.is_a?(ProjectQuery) |
|
| 117 | 124 |
@project = @query.project |
| 118 | 125 |
render_403 unless @query.editable_by?(User.current) |
| 119 | 126 |
rescue ActiveRecord::RecordNotFound |
| ... | ... | |
| 163 | 170 |
end |
| 164 | 171 | |
| 165 | 172 |
def redirect_to_project_query(options) |
| 166 |
redirect_to projects_path(options) |
|
| 173 |
if params[:admin_projects] |
|
| 174 |
redirect_to admin_projects_path(options) |
|
| 175 |
else |
|
| 176 |
redirect_to projects_path(options) |
|
| 177 |
end |
|
| 167 | 178 |
end |
| 168 | 179 | |
| 169 | 180 |
# Returns the Query subclass, IssueQuery by default |
| app/helpers/queries_helper.rb | ||
|---|---|---|
| 462 | 462 |
url_params = |
| 463 | 463 |
if controller_name == 'issues' |
| 464 | 464 |
{:controller => 'issues', :action => 'index', :project_id => @project}
|
| 465 |
elsif controller_name == 'admin' && action_name == 'projects' |
|
| 466 |
{:admin_projects => '1'}
|
|
| 465 | 467 |
else |
| 466 | 468 |
{}
|
| 467 | 469 |
end |
| app/models/project_query.rb | ||
|---|---|---|
| 18 | 18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 19 | 19 | |
| 20 | 20 |
class ProjectQuery < Query |
| 21 |
attr_accessor :admin_projects |
|
| 21 | 22 |
self.queried_class = Project |
| 22 | 23 |
self.view_permission = :search_project |
| 23 | 24 | |
| ... | ... | |
| 66 | 67 |
add_custom_fields_filters(project_custom_fields) |
| 67 | 68 |
end |
| 68 | 69 | |
| 70 |
def build_from_params(params, defaults={})
|
|
| 71 |
query = super |
|
| 72 |
query.admin_projects = params[:admin_projects] |
|
| 73 |
query |
|
| 74 |
end |
|
| 75 | ||
| 69 | 76 |
def available_columns |
| 70 | 77 |
return @available_columns if @available_columns |
| 71 | 78 | |
| ... | ... | |
| 76 | 83 |
end |
| 77 | 84 | |
| 78 | 85 |
def available_display_types |
| 79 |
['board', 'list'] |
|
| 86 |
unless self.admin_projects |
|
| 87 |
['board', 'list'] |
|
| 88 |
else |
|
| 89 |
['list'] |
|
| 90 |
end |
|
| 91 |
end |
|
| 92 | ||
| 93 |
def display_type |
|
| 94 |
unless self.admin_projects |
|
| 95 |
super |
|
| 96 |
else |
|
| 97 |
'list' |
|
| 98 |
end |
|
| 99 |
end |
|
| 100 | ||
| 101 |
def project_statuses_values |
|
| 102 |
values = super |
|
| 103 |
if self.admin_projects |
|
| 104 |
values.append([l(:project_status_archived), "#{Project::STATUS_ARCHIVED}"])
|
|
| 105 |
end |
|
| 106 |
values |
|
| 80 | 107 |
end |
| 81 | 108 | |
| 82 | 109 |
def default_columns_names |
| ... | ... | |
| 92 | 119 |
end |
| 93 | 120 | |
| 94 | 121 |
def base_scope |
| 95 |
Project.visible.where(statement) |
|
| 122 |
if self.admin_projects |
|
| 123 |
Project.where(statement) |
|
| 124 |
else |
|
| 125 |
Project.visible.where(statement) |
|
| 126 |
end |
|
| 96 | 127 |
end |
| 97 | 128 | |
| 98 | 129 |
def results_scope(options={})
|
| app/views/admin/projects.html.erb | ||
|---|---|---|
| 2 | 2 |
<%= link_to l(:label_project_new), new_project_path, :class => 'icon icon-add' %> |
| 3 | 3 |
</div> |
| 4 | 4 | |
| 5 |
<%= title l(:label_project_plural) %>
|
|
| 5 |
<h2><%= @query.new_record? ? l(:label_project_plural) : @query.name %></h2>
|
|
| 6 | 6 | |
| 7 |
<%= form_tag({}, :method => :get) do %>
|
|
| 8 |
<fieldset><legend><%= l(:label_filter_plural) %></legend> |
|
| 9 |
<label for='status'><%= l(:field_status) %>:</label> |
|
| 10 |
<%= select_tag 'status', project_status_options_for_select(@status), :class => "small", :onchange => "this.form.submit(); return false;" %> |
|
| 11 |
<label for='name'><%= l(:label_project) %>:</label> |
|
| 12 |
<%= text_field_tag 'name', params[:name], :size => 30 %> |
|
| 13 |
<%= submit_tag l(:button_apply), :class => "small", :name => nil %> |
|
| 14 |
<%= link_to l(:button_clear), admin_projects_path, :class => 'icon icon-reload' %> |
|
| 15 |
</fieldset> |
|
| 7 |
<%= form_tag(admin_projects_path(@project, nil), :method => :get, :id => 'query_form') do %> |
|
| 8 |
<%= hidden_field_tag 'admin_projects', '1' %> |
|
| 9 |
<%= render :partial => 'queries/query_form' %> |
|
| 16 | 10 |
<% end %> |
| 17 |
|
|
| 18 | 11 | |
| 19 |
<% if @projects.any? %> |
|
| 20 |
<div class="autoscroll"> |
|
| 21 |
<table class="list"> |
|
| 22 |
<thead><tr> |
|
| 23 |
<th><%=l(:label_project)%></th> |
|
| 24 |
<th><%=l(:field_is_public)%></th> |
|
| 25 |
<th><%=l(:field_created_on)%></th> |
|
| 26 |
<th></th> |
|
| 27 |
</tr></thead> |
|
| 28 |
<tbody> |
|
| 29 |
<% project_tree(@projects, :init_level => true) do |project, level| %> |
|
| 30 |
<tr class="<%= project.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>">
|
|
| 31 |
<td class="name"><span><%= link_to_project_settings(project, {}, :title => project.short_description) %></span></td>
|
|
| 32 |
<td><%= checked_image project.is_public? %></td> |
|
| 33 |
<td><%= format_date(project.created_on) %></td> |
|
| 34 |
<td class="buttons"> |
|
| 35 |
<%= 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? %>
|
|
| 36 |
<%= link_to(l(:button_unarchive), unarchive_project_path(project, :status => params[:status]), :method => :post, :class => 'icon icon-unlock') if project.archived? %> |
|
| 37 |
<%= link_to(l(:button_copy), copy_project_path(project), :class => 'icon icon-copy') %> |
|
| 38 |
<%= link_to(l(:button_delete), project_path(project), :method => :delete, :class => 'icon icon-del') %> |
|
| 39 |
</td> |
|
| 40 |
</tr> |
|
| 12 |
<% if @query.valid? %> |
|
| 13 |
<%if @projects.any? %> |
|
| 14 |
<%= render :partial => 'projects/list', :locals => { :entries => @projects }%>
|
|
| 15 |
<% else %> |
|
| 16 |
<p class="nodata"><%= l(:label_no_data) %></p> |
|
| 17 |
<% end %> |
|
| 41 | 18 |
<% end %> |
| 42 |
</tbody> |
|
| 43 |
</table> |
|
| 44 |
</div> |
|
| 45 |
<span class="pagination"><%= pagination_links_full @project_pages, @project_count %></span> |
|
| 46 |
<% else %> |
|
| 47 |
<p class="nodata"><%= l(:label_no_data) %></p> |
|
| 19 | ||
| 20 |
<% content_for :sidebar do %> |
|
| 21 |
<%= render :partial => 'projects/sidebar' %> |
|
| 48 | 22 |
<% end %> |
| app/views/projects/_list.html.erb | ||
|---|---|---|
| 6 | 6 |
<% @query.inline_columns.each do |column| %> |
| 7 | 7 |
<%= column_header(@query, column) %> |
| 8 | 8 |
<% end %> |
| 9 |
<% if controller_name == 'admin' && action_name == 'projects' %> |
|
| 10 |
<th></th> |
|
| 11 |
<% end %> |
|
| 9 | 12 |
</tr> |
| 10 | 13 |
</thead> |
| 11 | 14 |
<tbody> |
| ... | ... | |
| 23 | 26 |
<%= link_to_function("#{l(:button_collapse_all)}/#{l(:button_expand_all)}",
|
| 24 | 27 |
"toggleAllRowGroups(this)", :class => 'toggle-all') %> |
| 25 | 28 |
</td> |
| 29 |
<% if controller_name == 'admin' && action_name == 'projects' %> |
|
| 30 |
<td></td> |
|
| 31 |
<% end %> |
|
| 26 | 32 |
</tr> |
| 27 | 33 |
<% end %> |
| 28 | 34 |
<tr id="project-<%= entry.id %>" class="<%= cycle('odd', 'even') %> <%= entry.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>">
|
| 29 | 35 |
<% @query.inline_columns.each do |column| %> |
| 30 | 36 |
<%= content_tag('td', column_content(column, entry), :class => column.css_classes) %>
|
| 31 | 37 |
<% end %> |
| 38 |
<% if controller_name == 'admin' && action_name == 'projects' %> |
|
| 39 |
<td class="buttons"> |
|
| 40 |
<%= 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? %>
|
|
| 41 |
<%= link_to(l(:button_unarchive), unarchive_project_path(entry, :status => params[:status]), :method => :post, :class => 'icon icon-unlock') if entry.archived? %> |
|
| 42 |
<%= link_to(l(:button_copy), copy_project_path(entry), :class => 'icon icon-copy') %> |
|
| 43 |
<%= link_to(l(:button_delete), project_path(entry), :method => :delete, :class => 'icon icon-del') %> |
|
| 44 |
</td> |
|
| 45 |
<% end %> |
|
| 32 | 46 |
</tr> |
| 33 | 47 |
<% end -%> |
| 34 | 48 |
</tbody> |
| app/views/queries/_form.html.erb | ||
|---|---|---|
| 4 | 4 |
<div class="tabular"> |
| 5 | 5 |
<%= hidden_field_tag 'gantt', '1' if params[:gantt] %> |
| 6 | 6 |
<%= hidden_field_tag 'calendar', '1' if params[:calendar] %> |
| 7 |
<%= hidden_field_tag 'admin_projects', '1' if params[:admin_projects] %> |
|
| 7 | 8 | |
| 8 | 9 |
<p><label for="query_name"><%=l(:field_name)%></label> |
| 9 | 10 |
<%= text_field 'query', 'name', :size => 80 %></p> |
| ... | ... | |
| 34 | 35 |
<p><label for='display_type'><%= l(:label_display_type) %></label> |
| 35 | 36 |
<%= available_display_types_tags(@query) %> |
| 36 | 37 |
</p> |
| 38 |
<% elsif @query.available_display_types.size == 1 %> |
|
| 39 |
<%= hidden_field_tag 'query[display_type]', @query.available_display_types.first %> |
|
| 37 | 40 |
<% end %> |
| 38 | 41 | |
| 39 | 42 |
<p id ="default_columns"><label for="query_default_columns"><%=l(:label_default_columns)%></label> |
| app/views/queries/_query_form.html.erb | ||
|---|---|---|
| 63 | 63 |
<% end %> |
| 64 | 64 |
<% else %> |
| 65 | 65 |
<% if @query.editable_by?(User.current) %> |
| 66 |
<%= link_to l(:button_edit_object, object_name: l(:label_query).downcase), edit_query_path(@query), :class => 'icon icon-edit' %> |
|
| 67 |
<%= delete_link query_path(@query), {}, l(:button_delete_object, object_name: l(:label_query).downcase) %>
|
|
| 66 |
<% redirect_params = (controller_name == 'admin' && action_name == 'projects') ? {:admin_projects => 1} : {} %>
|
|
| 67 |
<%= link_to l(:button_edit_object, object_name: l(:label_query).downcase), edit_query_path(@query, redirect_params), :class => 'icon icon-edit' %> |
|
| 68 |
<%= delete_link query_path(@query, redirect_params), {}, l(:button_delete_object, object_name: l(:label_query).downcase) %>
|
|
| 68 | 69 |
<% end %> |
| 69 | 70 |
<% end %> |
| 70 | 71 |
</p> |
| test/functional/admin_controller_test.rb | ||
|---|---|---|
| 59 | 59 |
get( |
| 60 | 60 |
:projects, |
| 61 | 61 |
:params => {
|
| 62 |
:name => 'store', |
|
| 63 |
:status => '' |
|
| 62 |
'set_filte' => '1', |
|
| 63 |
'f' => ['status', 'name'], |
|
| 64 |
'op' => {'status' => '=', 'name' => '~'},
|
|
| 65 |
'v' => {'status' => ['1'], 'name' => ['store']}
|
|
| 64 | 66 |
} |
| 65 | 67 |
) |
| 66 | 68 |
assert_response :success |
| test/functional/queries_controller_test.rb | ||
|---|---|---|
| 585 | 585 |
assert q.valid? |
| 586 | 586 |
end |
| 587 | 587 | |
| 588 |
def test_create_admin_projects_query_should_redirect_to_admin_projects |
|
| 589 |
@request.session[:user_id] = 1 |
|
| 590 | ||
| 591 |
q = new_record(ProjectQuery) do |
|
| 592 |
post( |
|
| 593 |
:create, |
|
| 594 |
:params => {
|
|
| 595 |
:type => 'ProjectQuery', |
|
| 596 |
:default_columns => '1', |
|
| 597 |
:f => ["status"], |
|
| 598 |
:op => {
|
|
| 599 |
"status" => "=" |
|
| 600 |
}, |
|
| 601 |
:v => {
|
|
| 602 |
"status" => ['1'] |
|
| 603 |
}, |
|
| 604 |
:query => {
|
|
| 605 |
"name" => "test_new_project_public_query", "visibility" => "2" |
|
| 606 |
}, |
|
| 607 |
:admin_projects => 1 |
|
| 608 |
} |
|
| 609 |
) |
|
| 610 |
end |
|
| 611 | ||
| 612 |
assert_redirected_to :controller => 'admin', :action => 'projects', :query_id => q.id, :admin_projects => 1 |
|
| 613 |
end |
|
| 614 | ||
| 588 | 615 |
def test_edit_global_public_query |
| 589 | 616 |
@request.session[:user_id] = 1 |
| 590 | 617 |
get(:edit, :params => {:id => 4})
|
| ... | ... | |
| 690 | 717 |
assert q.valid? |
| 691 | 718 |
end |
| 692 | 719 | |
| 720 |
def test_update_admin_projects_query |
|
| 721 |
q = ProjectQuery.create(:name => 'project_query') |
|
| 722 |
@request.session[:user_id] = 1 |
|
| 723 | ||
| 724 |
put( |
|
| 725 |
:update, |
|
| 726 |
:params => {
|
|
| 727 |
:id => q.id, |
|
| 728 |
:default_columns => '1', |
|
| 729 |
:fields => ["status"], |
|
| 730 |
:operators => {
|
|
| 731 |
"status" => "=" |
|
| 732 |
}, |
|
| 733 |
:values => {
|
|
| 734 |
"status" => ['1'] |
|
| 735 |
}, |
|
| 736 |
:query => {
|
|
| 737 |
"name" => "test_project_query_updated", "visibility" => "2" |
|
| 738 |
}, |
|
| 739 |
:admin_projects => 1 |
|
| 740 |
} |
|
| 741 |
) |
|
| 742 | ||
| 743 |
assert_redirected_to :controller => 'admin', :action => 'projects', :query_id => q.id, :admin_projects => 1 |
|
| 744 |
assert Query.find_by_name('test_project_query_updated')
|
|
| 745 |
end |
|
| 746 | ||
| 693 | 747 |
def test_update_with_failure |
| 694 | 748 |
@request.session[:user_id] = 1 |
| 695 | 749 |
put( |
| test/unit/project_query_test.rb | ||
|---|---|---|
| 61 | 61 |
assert_include :cf_3, query.available_columns.map(&:name) |
| 62 | 62 |
end |
| 63 | 63 | |
| 64 |
def test_available_display_types_should_returns_bord_and_list |
|
| 65 |
query = ProjectQuery.new |
|
| 66 |
query.admin_projects = nil |
|
| 67 |
assert_equal ['board', 'list'], query.available_display_types |
|
| 68 |
end |
|
| 69 | ||
| 70 |
def test_available_display_types_should_always_returns_list_when_admin_projects_is_set |
|
| 71 |
query = ProjectQuery.new |
|
| 72 |
query.admin_projects = 1 |
|
| 73 |
assert_equal ['list'], query.available_display_types |
|
| 74 |
end |
|
| 75 | ||
| 64 | 76 |
def test_display_type_default_should_equal_with_setting_project_list_display_type |
| 65 | 77 |
ProjectQuery.new.available_display_types.each do |t| |
| 66 | 78 |
with_settings :project_list_display_type => t do |
| ... | ... | |
| 69 | 81 |
end |
| 70 | 82 |
end |
| 71 | 83 |
end |
| 84 | ||
| 85 |
def test_display_type_should_returns_list_when_admin_projects_is_set |
|
| 86 |
q = ProjectQuery.new |
|
| 87 |
q.admin_projects = 1 |
|
| 88 |
assert_equal 'list', q.display_type |
|
| 89 |
end |
|
| 90 | ||
| 91 |
def test_project_statuses_values_should_equal_ancestors_return |
|
| 92 |
ancestor = Query.new |
|
| 93 |
q = ProjectQuery.new |
|
| 94 |
assert_equal ancestor.project_statuses_values, q.project_statuses_values |
|
| 95 |
end |
|
| 96 | ||
| 97 |
def test_project_statuses_values_should_includes_project_status_archeved_when_admin_projects_is_set |
|
| 98 |
q = ProjectQuery.new |
|
| 99 |
q.admin_projects = 1 |
|
| 100 |
assert_includes q.project_statuses_values, [l(:project_status_archived), "#{Project::STATUS_ARCHIVED}"]
|
|
| 101 |
Query.new.project_statuses_values.each do |status| |
|
| 102 |
assert_includes q.project_statuses_values, status |
|
| 103 |
end |
|
| 104 |
end |
|
| 105 | ||
| 106 |
def test_base_scope_should_return_visible_projects |
|
| 107 |
q = ProjectQuery.new |
|
| 108 |
assert_equal Project.visible, q.base_scope |
|
| 109 |
end |
|
| 110 | ||
| 111 |
def test_base_scope_should_return_all_projects_when_admin_projects_is_set |
|
| 112 |
q = ProjectQuery.new |
|
| 113 |
q.admin_projects = 1 |
|
| 114 |
assert_equal Project.all, q.base_scope |
|
| 115 |
end |
|
| 72 | 116 |
end |