Project

General

Profile

Feature #33422 » 0001-ProjectQuery-filters-on-the-admin-project-list.patch

Jens Krämer, 2022-03-31 12:49

View differences:

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
22

  
21 23
  self.queried_class = Project
22 24
  self.view_permission = :search_project
23 25

  
......
74 76
    add_custom_fields_filters(project_custom_fields)
75 77
  end
76 78

  
79
  def build_from_params(params, defaults={})
80
    query = super
81
    query.admin_projects = params[:admin_projects]
82
    query
83
  end
84

  
77 85
  def available_columns
78 86
    return @available_columns if @available_columns
79 87

  
......
84 92
  end
85 93

  
86 94
  def available_display_types
87
    ['board', 'list']
95
    if self.admin_projects
96
      ['list']
97
    else
98
      ['board', 'list']
99
    end
100
  end
101

  
102
  def display_type
103
    if self.admin_projects
104
      'list'
105
    else
106
      super
107
    end
108
  end
109

  
110
  def project_statuses_values
111
    values = super
112
    if self.admin_projects
113
      values << [l(:project_status_archived), Project::STATUS_ARCHIVED.to_s]
114
    end
115
    values
88 116
  end
89 117

  
90 118
  def default_columns_names
......
100 128
  end
101 129

  
102 130
  def base_scope
103
    Project.visible.where(statement)
131
    if self.admin_projects
132
      Project.where(statement)
133
    else
134
      Project.visible.where(statement)
135
    end
104 136
  end
105 137

  
106 138
  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
&nbsp;
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
38 38
    assert_select 'div.nodata'
39 39
  end
40 40

  
41
  def test_projects
41
  def test_projects_should_show_only_active_projects_by_default
42
    p = Project.find(1)
43
    p.update_column :status, 5
44

  
42 45
    get :projects
43 46
    assert_response :success
44 47
    assert_select 'tr.project.closed', 0
48
    assert_select 'tr.project', 5
49
    assert_select 'tr.project td.name', :text => 'OnlineStore'
50
    assert_select 'tr.project td.name', :text => p.name, :count => 0
45 51
  end
46 52

  
47 53
  def test_projects_with_status_filter
54
    p = Project.find(1)
55
    p.update_column :status, 5
48 56
    get(
49 57
      :projects,
50 58
      :params => {
51
        :status => 1
59
        'set_filter' => '1',
60
        'f'  => ['status'],
61
        'op' => {'status' => '='},
62
        'v'  => {'status' => ['5']}
52 63
      }
53 64
    )
54 65
    assert_response :success
55
    assert_select 'tr.project.closed', 0
66
    assert_select 'tr.project', 1
67
    assert_select 'tr.project td.name', :text => p.name
56 68
  end
57 69

  
58 70
  def test_projects_with_name_filter
59 71
    get(
60 72
      :projects,
61 73
      :params => {
62
        :name => 'store',
63
        :status => ''
74
        'set_filter' => '1',
75
        'f'  => ['status', 'name'],
76
        'op' => {'status' => '=', 'name' => '~'},
77
        'v'  => {'status' => ['1'], 'name' => ['store']}
64 78
      }
65 79
    )
66 80
    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
62 62
    assert_include :cf_3, query.available_columns.map(&:name)
63 63
  end
64 64

  
65
  def test_available_display_types_should_returns_bord_and_list
66
    query = ProjectQuery.new
67
    query.admin_projects = nil
68
    assert_equal ['board', 'list'], query.available_display_types
69
  end
70

  
71
  def test_available_display_types_should_always_returns_list_when_admin_projects_is_set
72
    query = ProjectQuery.new
73
    query.admin_projects = 1
74
    assert_equal ['list'], query.available_display_types
75
  end
76

  
65 77
  def test_display_type_default_should_equal_with_setting_project_list_display_type
66 78
    ProjectQuery.new.available_display_types.each do |t|
67 79
      with_settings :project_list_display_type => t do
......
104 116

  
105 117
    assert_nil ProjectQuery.default
106 118
  end
119

  
120
  def test_display_type_should_returns_list_when_admin_projects_is_set
121
    q = ProjectQuery.new
122
    q.admin_projects = 1
123
    assert_equal 'list', q.display_type
124
  end
125

  
126
  def test_project_statuses_values_should_equal_ancestors_return
127
    ancestor = Query.new
128
    q = ProjectQuery.new
129
    assert_equal ancestor.project_statuses_values, q.project_statuses_values
130
  end
131

  
132
  def test_project_statuses_values_should_includes_project_status_archeved_when_admin_projects_is_set
133
    q = ProjectQuery.new
134
    q.admin_projects = 1
135
    assert_includes q.project_statuses_values, [l(:project_status_archived), Project::STATUS_ARCHIVED.to_s]
136
    Query.new.project_statuses_values.each do |status|
137
      assert_includes q.project_statuses_values, status
138
    end
139
  end
140

  
141
  def test_base_scope_should_return_visible_projects
142
    q = ProjectQuery.new
143
    assert_equal Project.visible, q.base_scope
144
  end
145

  
146
  def test_base_scope_should_return_all_projects_when_admin_projects_is_set
147
    q = ProjectQuery.new
148
    q.admin_projects = 1
149
    assert_equal Project.all, q.base_scope
150
  end
107 151
end
(5-5/6)