Merge pull request #3344 from oliverguenther/feature/projects_pagination_and_sort

[18800] Add pagination and column sorting to admin projects table
pull/3388/head
Oliver Günther 9 years ago
commit 5a79910b89
  1. 32
      app/assets/stylesheets/content/_information_section.sass
  2. 20
      app/assets/stylesheets/content/_tables.sass
  3. 1
      app/assets/stylesheets/default.css.sass
  4. 11
      app/controllers/admin_controller.rb
  5. 12
      app/helpers/application_helper.rb
  6. 38
      app/helpers/sort_helper.rb
  7. 71
      app/views/admin/projects.html.erb
  8. 3
      config/locales/en.yml

@ -0,0 +1,32 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
//++
$infosec-font-size-rewrite: .8rem
.information-section
font-size: $infosec-font-size-rewrite

@ -62,26 +62,26 @@ tr
&.project
td.name a
white-space: nowrap
&.idnt td.name span
&.idnt td.project--hierarchy span
background: image-url("bullet_arrow_right.png") no-repeat 0 50%
padding-left: 16px
&.idnt-1 td.name
&.idnt-1 td.project--hierarchy
padding-left: 0.5em
&.idnt-2 td.name
&.idnt-2 td.project--hierarchy
padding-left: 2em
&.idnt-3 td.name
&.idnt-3 td.project--hierarchy
padding-left: 3.5em
&.idnt-4 td.name
&.idnt-4 td.project--hierarchy
padding-left: 5em
&.idnt-5 td.name
&.idnt-5 td.project--hierarchy
padding-left: 6.5em
&.idnt-6 td.name
&.idnt-6 td.project--hierarchy
padding-left: 8em
&.idnt-7 td.name
&.idnt-7 td.project--hierarchy
padding-left: 9.5em
&.idnt-8 td.name
&.idnt-8 td.project--hierarchy
padding-left: 11em
&.idnt-9 td.name
&.idnt-9 td.project--hierarchy
padding-left: 12.5em
&.issue
white-space: nowrap

@ -68,6 +68,7 @@
@import content/attributes_key_value
@import content/attributes_group
@import content/attributes_table
@import content/information_section
@import content/work_package_details/activities_tab
@import content/work_package_details/attachments_tab

@ -33,6 +33,7 @@ class AdminController < ApplicationController
before_filter :require_admin
include SortHelper
include PaginationHelper
menu_item :projects, only: [:projects]
menu_item :plugins, only: [:plugins]
@ -43,6 +44,12 @@ class AdminController < ApplicationController
end
def projects
# We need to either clear the session sort
# or users can't access the default lft order with subprojects
# after once sorting the list
sort_clear
sort_init 'lft'
sort_update %w(lft name is_public created_on updated_on required_disk_space)
@status = params[:status] ? params[:status].to_i : 1
c = ARCondition.new(@status == 0 ? 'status <> 0' : ['status = ?', @status])
@ -52,8 +59,10 @@ class AdminController < ApplicationController
end
@projects = Project.with_required_storage
.order('lft')
.order(sort_clause)
.where(c.conditions)
.page(page_param)
.per_page(per_page_param)
render action: 'projects', layout: false if request.xhr?
end

@ -247,6 +247,18 @@ module ApplicationHelper
Project.project_tree(projects, &block)
end
# Returns a lft-sorted project hierarchy only when
# the sort helper has deemed a non-default sort option to be selected.
def project_tree_when_sorted(projects, &block)
if default_sort_order?
project_tree(projects, &block)
else
projects.each do |p|
yield p, 0
end
end
end
def project_nested_ul(projects, &_block)
s = ''
if projects.any?

@ -81,6 +81,8 @@
module SortHelper
class SortCriteria
attr_reader :criteria
def initialize
@criteria = []
end
@ -179,14 +181,16 @@ module SortHelper
# sort_init [['name', 'desc'], ['id', 'desc']]
#
def sort_init(*args)
case args.size
when 1
@sort_default = args.first.is_a?(Array) ? args.first : [[args.first]]
when 2
@sort_default = [[args.first, args.last]]
else
raise ArgumentError
end
criteria = case args.size
when 1
args.first.is_a?(Array) ? args.first : [[args.first]]
when 2
[[args.first, args.last]]
else
raise ArgumentError
end
@sort_default = SortCriteria.new
@sort_default.criteria = criteria
end
# Updates the sort state. Call this in the controller prior to calling
@ -197,7 +201,7 @@ module SortHelper
@sort_criteria = SortCriteria.new
@sort_criteria.available_criteria = criteria
@sort_criteria.from_param(params[:sort] || session[sort_name])
@sort_criteria.criteria = @sort_default if @sort_criteria.empty?
@sort_criteria.criteria = @sort_default.criteria if @sort_criteria.empty?
session[sort_name] = @sort_criteria.to_param
end
@ -214,6 +218,12 @@ module SortHelper
@sort_criteria.to_sql
end
# Determines whether the current selected sort criteria
# is identical to the default
def default_sort_order?
@sort_default.criteria == @sort_criteria.criteria
end
# Returns a link which sorts by the named column.
#
# - column is the name of an attribute in the sorted record collection.
@ -271,4 +281,14 @@ module SortHelper
content_tag('th', sort_link(column, caption, default_order, lang: lang), options)
end
# Returns a table header tag similar to +sort_header_tag+, but
# according to the LSG table component.
def sort_header_tag_with_lsg(column, options = {})
caption = options.delete(:caption) || column.to_s.humanize
default_order = options.delete(:default_order) || 'asc'
lang = options.delete(:lang) || nil
sort_link(column, caption, default_order, lang: lang)
end
end

@ -36,37 +36,12 @@ See doc/COPYRIGHT.rdoc for more details.
<% end %>
</li>
<% end %>
<div class="attributes-group">
<div class="attributes-group--header">
<div class="attributes-group--header-container">
<h3 class="attributes-group--header-text"><%= l(:label_information) %></h3>
</div>
</div>
<dl class="attributes-key-value">
<dt class="attributes-key-value--key"><%= l(:label_project_count) %></dt>
<dd class="attributes-key-value--value-container">
<div class="attributes-key-value--value -text">
<span title="<%= l(:label_project_count) %>">
<%= l(:label_x_projects, :count => Project.count) %>
</span>
</div>
</dd>
<dt class="attributes-key-value--key"><%= l(:label_required_disk_storage) %></dt>
<dd class="attributes-key-value--value-container">
<div class="attributes-key-value--value -text">
<span title="0">
<%= number_to_human_size(Project.total_projects_size, precision: 2) %>
</span>
</div>
</dd>
</dl>
</div>
<%= form_tag({}, :method => :get) do %>
<fieldset class="simple-filters--container">
<legend><%= l(:label_filter_plural) %></legend>
<ul class="simple-filters--filters">
<li class="simple-filters--filter">
<label for='status'><%= Project.human_attribute_name(:status) %> :</label>
<label for='status'><%= Project.human_attribute_name(:status) %>:</label>
<%= select_tag 'status', project_status_options_for_select(@status), :onchange => "this.form.submit(); return false;" %>
</li>
<li class="simple-filters--filter">
@ -80,15 +55,21 @@ See doc/COPYRIGHT.rdoc for more details.
</div>
</fieldset>
<% end %>
&nbsp;
<p class="information-section">
<i class="icon-info"></i>
<%= l(:label_projects_storage_information,
count: Project.count,
storage: number_to_human_size(Project.total_projects_size, precision: 2)) %>
</p>
<div class="generic-table--container">
<div class="generic-table--results-container">
<table role="grid" class="generic-table">
<table interactive-table role="grid" class="generic-table">
<colgroup>
<col highlight-col>
<col highlight-col>
<col highlight-col>
<col highlight-col>
<col highlight-col>
<col>
</colgroup>
<thead>
@ -96,26 +77,23 @@ See doc/COPYRIGHT.rdoc for more details.
<th>
<div class="generic-table--sort-header-outer">
<div class="generic-table--sort-header">
<span>
<%= Project.model_name.human %>
</span>
<%= sort_header_tag_with_lsg('name', caption: Project.model_name.human) %>
</div>
</div>
</th>
<th>
<th>
<div class="generic-table--sort-header-outer">
<div class="generic-table--sort-header">
<span>
<%= Project.human_attribute_name(:is_public) %>
</span>
<%= sort_header_tag_with_lsg('is_public',
caption: Project.human_attribute_name(:is_public)) %>
</div>
</div>
</th>
<th>
<div class="generic-table--sort-header-outer">
<div class="generic-table--sort-header">
<span>
<%= l(:label_required_disk_storage) %>
<%= sort_header_tag_with_lsg('required_disk_space',
caption: I18n.t(:label_required_disk_storage)) %>
</span>
</div>
</div>
@ -123,9 +101,16 @@ See doc/COPYRIGHT.rdoc for more details.
<th>
<div class="generic-table--sort-header-outer">
<div class="generic-table--sort-header">
<span>
<%= Project.human_attribute_name(:created_on) %>
</span>
<%= sort_header_tag_with_lsg('created_on',
caption: Project.human_attribute_name(:created_on)) %>
</div>
</div>
</th>
<th>
<div class="generic-table--sort-header-outer">
<div class="generic-table--sort-header">
<%= sort_header_tag_with_lsg('updated_on',
caption: I18n.t(:label_last_activity)) %>
</div>
</div>
</th>
@ -133,12 +118,13 @@ See doc/COPYRIGHT.rdoc for more details.
</tr>
</thead>
<tbody>
<% project_tree(@projects) do |project, level| %>
<% project_tree_when_sorted(@projects) do |project, level| %>
<tr class="<%= project.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>">
<td class="name"><span><%= link_to project, settings_project_path(project), :title => project.short_description %></span></td>
<td class="name project--hierarchy"><span><%= link_to project, settings_project_path(project), :title => project.short_description %></span></td>
<td><%= checked_image project.is_public? %></td>
<td><%= number_to_human_size(project.required_disk_space, precision: 2) if project.required_disk_space.to_i > 0 %></td>
<td><%= format_date(project.created_on) %></td>
<td><%= format_date(project.updated_on) %></td>
<td class="buttons">
<%= link_to(l(:button_archive),
archive_project_path(project, :status => params[:status]),
@ -161,5 +147,6 @@ See doc/COPYRIGHT.rdoc for more details.
</tbody>
</table>
<div class="generic-table--header-background"></div>
<%= pagination_links_full @projects %>
</div>
</div>

@ -814,6 +814,7 @@ en:
label_journal_diff: "Description Comparison"
label_jump_to_a_project: "Jump to a project..."
label_language_based: "Based on user's language"
label_last_activity: "Last activity"
label_last_changes: "last %{count} changes"
label_last_login: "Last login"
label_last_month: "last month"
@ -912,9 +913,11 @@ en:
label_project_copy_notifications: "Send email notifications during the project copy"
label_project_latest: "Latest projects"
label_project_default_type: "Allow empty type"
label_project_hierarchy: "Project hierarchy"
label_project_new: "New project"
label_project_plural: "Projects"
label_project_settings: "Project settings"
label_projects_storage_information: "%{count} projects using %{storage} disk storage"
label_project_view_all: "View all projects"
label_public_projects: "Public projects"
label_query_new: "New query"

Loading…
Cancel
Save