From 3c44aac1f50cfee83e87baa21d758620749c01e0 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Lang Date: Fri, 7 Dec 2007 10:26:07 +0000 Subject: [PATCH] Added version details view accessible from the roadmap. git-svn-id: http://redmine.rubyforge.org/svn/trunk@955 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/versions_controller.rb | 10 ++++ app/helpers/versions_helper.rb | 28 +++++++++ app/models/issue_category.rb | 4 ++ app/models/tracker.rb | 4 ++ app/views/projects/roadmap.rhtml | 66 ++++++++------------- app/views/projects/settings/_versions.rhtml | 2 +- app/views/versions/_issue_counts.rhtml | 35 +++++++++++ app/views/versions/_overview.rhtml | 24 ++++++++ app/views/versions/show.rhtml | 14 +++++ lang/bg.yml | 1 + lang/cs.yml | 1 + lang/de.yml | 1 + lang/en.yml | 1 + lang/es.yml | 1 + lang/fr.yml | 1 + lang/he.yml | 1 + lang/it.yml | 1 + lang/ja.yml | 1 + lang/ko.yml | 1 + lang/nl.yml | 1 + lang/pl.yml | 1 + lang/pt-br.yml | 1 + lang/pt.yml | 1 + lang/ro.yml | 1 + lang/ru.yml | 1 + lang/sr.yml | 1 + lang/sv.yml | 1 + lang/zh.yml | 1 + lib/redmine.rb | 1 + public/stylesheets/application.css | 2 + test/functional/versions_controller_test.rb | 42 +++++++++++++ 31 files changed, 208 insertions(+), 43 deletions(-) create mode 100644 app/views/versions/_issue_counts.rhtml create mode 100644 app/views/versions/_overview.rhtml create mode 100644 app/views/versions/show.rhtml create mode 100644 test/functional/versions_controller_test.rb diff --git a/app/controllers/versions_controller.rb b/app/controllers/versions_controller.rb index 4e9016eb..1365c97e 100644 --- a/app/controllers/versions_controller.rb +++ b/app/controllers/versions_controller.rb @@ -21,6 +21,9 @@ class VersionsController < ApplicationController cache_sweeper :version_sweeper, :only => [ :edit, :destroy ] + def show + end + def edit if request.post? and @version.update_attributes(params[:version]) flash[:notice] = l(:notice_successful_update) @@ -49,6 +52,13 @@ class VersionsController < ApplicationController flash[:notice] = l(:notice_successful_delete) redirect_to :controller => 'projects', :action => 'list_files', :id => @project end + + def status_by + respond_to do |format| + format.html { render :action => 'show' } + format.js { render(:update) {|page| page.replace_html 'status_by', render_issue_status_by(@version, params[:status_by])} } + end + end private def find_project diff --git a/app/helpers/versions_helper.rb b/app/helpers/versions_helper.rb index 452f4d7f..0fcc6407 100644 --- a/app/helpers/versions_helper.rb +++ b/app/helpers/versions_helper.rb @@ -16,4 +16,32 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. module VersionsHelper + + STATUS_BY_CRITERIAS = %w(category tracker priority author assigned_to) + + def render_issue_status_by(version, criteria) + criteria ||= 'category' + raise 'Unknown criteria' unless STATUS_BY_CRITERIAS.include?(criteria) + + h = Hash.new {|k,v| k[v] = [0, 0]} + begin + # Total issue count + Issue.count(:group => criteria, + :conditions => ["#{Issue.table_name}.fixed_version_id = ?", version.id]).each {|c,s| h[c][0] = s} + # Open issues count + Issue.count(:group => criteria, + :include => :status, + :conditions => ["#{Issue.table_name}.fixed_version_id = ? AND #{IssueStatus.table_name}.is_closed = ?", version.id, false]).each {|c,s| h[c][1] = s} + rescue ActiveRecord::RecordNotFound + # When grouping by an association, Rails throws this exception if there's no result (bug) + end + counts = h.keys.compact.sort.collect {|k| {:group => k, :total => h[k][0], :open => h[k][1], :closed => (h[k][0] - h[k][1])}} + max = counts.collect {|c| c[:total]}.max + + render :partial => 'issue_counts', :locals => {:version => version, :criteria => criteria, :counts => counts, :max => max} + end + + def status_by_options_for_select(value) + options_for_select(STATUS_BY_CRITERIAS.collect {|criteria| [l("field_#{criteria}".to_sym), criteria]}, value) + end end diff --git a/app/models/issue_category.rb b/app/models/issue_category.rb index 9478504f..51baeb41 100644 --- a/app/models/issue_category.rb +++ b/app/models/issue_category.rb @@ -35,5 +35,9 @@ class IssueCategory < ActiveRecord::Base destroy_without_reassign end + def <=>(category) + name <=> category.name + end + def to_s; name end end diff --git a/app/models/tracker.rb b/app/models/tracker.rb index 6de2a098..8d864774 100644 --- a/app/models/tracker.rb +++ b/app/models/tracker.rb @@ -29,6 +29,10 @@ class Tracker < ActiveRecord::Base def to_s; name end + def <=>(tracker) + name <=> tracker.name + end + def self.all find(:all, :order => 'position') end diff --git a/app/views/projects/roadmap.rhtml b/app/views/projects/roadmap.rhtml index 7c3544a5..daf7639f 100644 --- a/app/views/projects/roadmap.rhtml +++ b/app/views/projects/roadmap.rhtml @@ -5,48 +5,28 @@ <% end %> <% @versions.each do |version| %> -

<%= version.name %>

- <% if version.completed? %> -

<%= format_date(version.effective_date) %>

- <% elsif version.overdue? %> -

<%= l(:label_roadmap_overdue, distance_of_time_in_words(Time.now, version.effective_date)) %> (<%= format_date(version.effective_date) %>)

- <% elsif version.effective_date %> -

<%=l(:label_roadmap_due_in)%> <%= distance_of_time_in_words Time.now, version.effective_date %> (<%= format_date(version.effective_date) %>)

- <% end %> -

<%=h version.description %>

- - <% if version.fixed_issues.count > 0 %> - <%= progress_bar([version.closed_pourcent, version.completed_pourcent], :width => '40em', :legend => ('%0.0f%' % version.completed_pourcent)) %> -

- <%= link_to(version.closed_issues_count, :controller => 'issues', :action => 'index', :project_id => @project, :status_id => 'c', :fixed_version_id => version, :set_filter => 1) %> - <%= lwr(:label_closed_issues, version.closed_issues_count) %> - (<%= '%0.0f' % (version.closed_issues_count.to_f / version.fixed_issues.count * 100) %>%) -   - <%= link_to(version.open_issues_count, :controller => 'issues', :action => 'index', :project_id => @project, :status_id => 'o', :fixed_version_id => version, :set_filter => 1) %> - <%= lwr(:label_open_issues, version.open_issues_count)%> - (<%= '%0.0f' % (version.open_issues_count.to_f / version.fixed_issues.count * 100) %>%) -

- <%= render(:partial => "wiki/content", :locals => {:content => version.wiki_page.content}) if version.wiki_page %> - <% issues = version.fixed_issues.find(:all, - :include => [:status, :tracker], - :conditions => ["tracker_id in (#{@selected_tracker_ids.join(',')})"], - :order => "#{Tracker.table_name}.position") unless @selected_tracker_ids.empty? - issues ||= [] - %> - - <% else %> -

<%= l(:label_roadmap_no_issues) %>

- <% end %> + <%= tag 'a', :name => version.name %> +

<%= link_to h(version.name), :controller => 'versions', :action => 'show', :id => version %>

+ <%= render :partial => 'versions/overview', :locals => {:version => version} %> + <%= render(:partial => "wiki/content", :locals => {:content => version.wiki_page.content}) if version.wiki_page %> + + <% issues = version.fixed_issues.find(:all, + :include => [:status, :tracker], + :conditions => ["tracker_id in (#{@selected_tracker_ids.join(',')})"], + :order => "#{Tracker.table_name}.position") unless @selected_tracker_ids.empty? + issues ||= [] + %> + <% end %> <% content_for :sidebar do %> @@ -66,3 +46,5 @@ <%= link_to version.name, :anchor => version.name %>
<% end %> <% end %> + +<% set_html_title l(:label_roadmap) %> diff --git a/app/views/projects/settings/_versions.rhtml b/app/views/projects/settings/_versions.rhtml index 63c408b0..7329c7f3 100644 --- a/app/views/projects/settings/_versions.rhtml +++ b/app/views/projects/settings/_versions.rhtml @@ -11,7 +11,7 @@ <% for version in @project.versions.sort %> - <%=h version.name %> + <%= link_to h(version.name), :controller => 'versions', :action => 'show', :id => version %> <%= format_date(version.effective_date) %> <%=h version.description %> <%= link_to(version.wiki_page_title, :controller => 'wiki', :page => Wiki.titleize(version.wiki_page_title)) unless version.wiki_page_title.blank? || @project.wiki.nil? %> diff --git a/app/views/versions/_issue_counts.rhtml b/app/views/versions/_issue_counts.rhtml new file mode 100644 index 00000000..4bab5c65 --- /dev/null +++ b/app/views/versions/_issue_counts.rhtml @@ -0,0 +1,35 @@ +
+
+ +<%= l(:label_issues_by, + select_tag('status_by', + status_by_options_for_select(criteria), + :id => 'status_by_select', + :onchange => remote_function(:url => { :action => :status_by, :id => version }, + :with => "Form.serialize('status_by_form')"))) %> + +<% if counts.empty? %> +

<%= l(:label_no_data) %>

+<% else %> + + <% counts.each do |count| %> + + + + + <% end %> +
+ <%= link_to count[:group], {:controller => 'issues', + :action => 'index', + :project_id => version.project, + :set_filter => 1, + :fixed_version_id => version, + "#{criteria}_id" => count[:group]} %> + + <%= progress_bar((count[:closed].to_f / count[:total])*100, + :legend => "#{count[:closed]}/#{count[:total]}", + :width => "#{(count[:total].to_f / max * 200).floor}px;") %> +
+<% end %> +
+
diff --git a/app/views/versions/_overview.rhtml b/app/views/versions/_overview.rhtml new file mode 100644 index 00000000..d3aa6b18 --- /dev/null +++ b/app/views/versions/_overview.rhtml @@ -0,0 +1,24 @@ +<% if version.completed? %> +

<%= format_date(version.effective_date) %>

+<% elsif version.overdue? %> +

<%= l(:label_roadmap_overdue, distance_of_time_in_words(Time.now, version.effective_date)) %> (<%= format_date(version.effective_date) %>)

+<% elsif version.effective_date %> +

<%=l(:label_roadmap_due_in)%> <%= distance_of_time_in_words Time.now, version.effective_date %> (<%= format_date(version.effective_date) %>)

+<% end %> + +

<%=h version.description %>

+ +<% if version.fixed_issues.count > 0 %> + <%= progress_bar([version.closed_pourcent, version.completed_pourcent], :width => '40em', :legend => ('%0.0f%' % version.completed_pourcent)) %> +

+ <%= link_to(version.closed_issues_count, :controller => 'issues', :action => 'index', :project_id => version.project, :status_id => 'c', :fixed_version_id => version, :set_filter => 1) %> + <%= lwr(:label_closed_issues, version.closed_issues_count) %> + (<%= '%0.0f' % (version.closed_issues_count.to_f / version.fixed_issues.count * 100) %>%) +   + <%= link_to(version.open_issues_count, :controller => 'issues', :action => 'index', :project_id => version.project, :status_id => 'o', :fixed_version_id => version, :set_filter => 1) %> + <%= lwr(:label_open_issues, version.open_issues_count)%> + (<%= '%0.0f' % (version.open_issues_count.to_f / version.fixed_issues.count * 100) %>%) +

+<% else %> +

<%= l(:label_roadmap_no_issues) %>

+<% end %> diff --git a/app/views/versions/show.rhtml b/app/views/versions/show.rhtml new file mode 100644 index 00000000..d8e2d243 --- /dev/null +++ b/app/views/versions/show.rhtml @@ -0,0 +1,14 @@ +
+<%= link_to_if_authorized l(:button_edit), {:controller => 'versions', :action => 'edit', :id => @version}, :class => 'icon icon-edit' %> +
+ +

<%= h(@version.name) %>

+ +
+<%= render_issue_status_by(@version, params[:status_by]) if @version.fixed_issues.count > 0 %> +
+ +<%= render :partial => 'versions/overview', :locals => {:version => @version} %> +<%= render(:partial => "wiki/content", :locals => {:content => @version.wiki_page.content}) if @version.wiki_page %> + +<% set_html_title h(@version.name) %> diff --git a/lang/bg.yml b/lang/bg.yml index 5f2dc6a8..59095268 100644 --- a/lang/bg.yml +++ b/lang/bg.yml @@ -548,3 +548,4 @@ field_time_zone: Time zone text_caracters_minimum: Must be at least %d characters long. setting_bcc_recipients: Blind carbon copy recipients (bcc) button_annotate: Annotate +label_issues_by: Issues by %s diff --git a/lang/cs.yml b/lang/cs.yml index 49697dda..816f9b92 100644 --- a/lang/cs.yml +++ b/lang/cs.yml @@ -548,3 +548,4 @@ field_time_zone: Time zone text_caracters_minimum: Must be at least %d characters long. setting_bcc_recipients: Blind carbon copy recipients (bcc) button_annotate: Annotate +label_issues_by: Issues by %s diff --git a/lang/de.yml b/lang/de.yml index 870f4501..046ed999 100644 --- a/lang/de.yml +++ b/lang/de.yml @@ -548,3 +548,4 @@ field_time_zone: Zeitzone text_caracters_minimum: Muss mindestens %d Zeichen lang sein. setting_bcc_recipients: Blind carbon copy recipients (bcc) button_annotate: Annotate +label_issues_by: Issues by %s diff --git a/lang/en.yml b/lang/en.yml index cdecb669..104e7fe6 100644 --- a/lang/en.yml +++ b/lang/en.yml @@ -210,6 +210,7 @@ label_issue: Issue label_issue_new: New issue label_issue_plural: Issues label_issue_view_all: View all issues +label_issues_by: Issues by %s label_document: Document label_document_new: New document label_document_plural: Documents diff --git a/lang/es.yml b/lang/es.yml index 5f25de7c..d806d066 100644 --- a/lang/es.yml +++ b/lang/es.yml @@ -551,3 +551,4 @@ notice_account_pending: "Su cuenta ha sido creada y está pendiende de la aproba setting_time_format: Formato de hora setting_bcc_recipients: Blind carbon copy recipients (bcc) button_annotate: Annotate +label_issues_by: Issues by %s diff --git a/lang/fr.yml b/lang/fr.yml index 63f367f0..36ccc463 100644 --- a/lang/fr.yml +++ b/lang/fr.yml @@ -210,6 +210,7 @@ label_issue: Demande label_issue_new: Nouvelle demande label_issue_plural: Demandes label_issue_view_all: Voir toutes les demandes +label_issues_by: Demandes par %s label_document: Document label_document_new: Nouveau document label_document_plural: Documents diff --git a/lang/he.yml b/lang/he.yml index 4ebbecf7..6be1a4c7 100644 --- a/lang/he.yml +++ b/lang/he.yml @@ -548,3 +548,4 @@ field_time_zone: Time zone text_caracters_minimum: Must be at least %d characters long. setting_bcc_recipients: Blind carbon copy recipients (bcc) button_annotate: Annotate +label_issues_by: Issues by %s diff --git a/lang/it.yml b/lang/it.yml index fa13513c..8a3e954f 100644 --- a/lang/it.yml +++ b/lang/it.yml @@ -548,3 +548,4 @@ field_time_zone: Time zone text_caracters_minimum: Must be at least %d characters long. setting_bcc_recipients: Blind carbon copy recipients (bcc) button_annotate: Annotate +label_issues_by: Issues by %s diff --git a/lang/ja.yml b/lang/ja.yml index d2f7d579..1ecfb1ae 100644 --- a/lang/ja.yml +++ b/lang/ja.yml @@ -549,3 +549,4 @@ field_time_zone: Time zone text_caracters_minimum: Must be at least %d characters long. setting_bcc_recipients: Blind carbon copy recipients (bcc) button_annotate: Annotate +label_issues_by: Issues by %s diff --git a/lang/ko.yml b/lang/ko.yml index dd50ce2b..ef081e62 100644 --- a/lang/ko.yml +++ b/lang/ko.yml @@ -548,3 +548,4 @@ field_time_zone: Time zone text_caracters_minimum: Must be at least %d characters long. setting_bcc_recipients: Blind carbon copy recipients (bcc) button_annotate: Annotate +label_issues_by: Issues by %s diff --git a/lang/nl.yml b/lang/nl.yml index 895feb5e..24a343eb 100644 --- a/lang/nl.yml +++ b/lang/nl.yml @@ -549,3 +549,4 @@ field_time_zone: Time zone text_caracters_minimum: Must be at least %d characters long. setting_bcc_recipients: Blind carbon copy recipients (bcc) button_annotate: Annotate +label_issues_by: Issues by %s diff --git a/lang/pl.yml b/lang/pl.yml index 381dc5e2..ff5f8d82 100644 --- a/lang/pl.yml +++ b/lang/pl.yml @@ -548,3 +548,4 @@ field_time_zone: Strefa czasowa text_caracters_minimum: Must be at least %d characters long. setting_bcc_recipients: Blind carbon copy recipients (bcc) button_annotate: Annotate +label_issues_by: Issues by %s diff --git a/lang/pt-br.yml b/lang/pt-br.yml index 4c4d862d..8c903edd 100644 --- a/lang/pt-br.yml +++ b/lang/pt-br.yml @@ -548,3 +548,4 @@ field_time_zone: Time zone text_caracters_minimum: Must be at least %d characters long. setting_bcc_recipients: Blind carbon copy recipients (bcc) button_annotate: Annotate +label_issues_by: Issues by %s diff --git a/lang/pt.yml b/lang/pt.yml index 1aabc8ac..10de07b5 100644 --- a/lang/pt.yml +++ b/lang/pt.yml @@ -548,3 +548,4 @@ field_time_zone: Time zone text_caracters_minimum: Must be at least %d characters long. setting_bcc_recipients: Blind carbon copy recipients (bcc) button_annotate: Annotate +label_issues_by: Issues by %s diff --git a/lang/ro.yml b/lang/ro.yml index 0c10c772..f7d3acd5 100644 --- a/lang/ro.yml +++ b/lang/ro.yml @@ -548,3 +548,4 @@ field_time_zone: Time zone text_caracters_minimum: Must be at least %d characters long. setting_bcc_recipients: Blind carbon copy recipients (bcc) button_annotate: Annotate +label_issues_by: Issues by %s diff --git a/lang/ru.yml b/lang/ru.yml index c6e27ca9..cad357c0 100644 --- a/lang/ru.yml +++ b/lang/ru.yml @@ -548,3 +548,4 @@ field_time_zone: Часовой пояс text_caracters_minimum: Must be at least %d characters long. setting_bcc_recipients: Blind carbon copy recipients (bcc) button_annotate: Annotate +label_issues_by: Issues by %s diff --git a/lang/sr.yml b/lang/sr.yml index 42aa3cc3..f9008d89 100644 --- a/lang/sr.yml +++ b/lang/sr.yml @@ -549,3 +549,4 @@ field_time_zone: Time zone text_caracters_minimum: Must be at least %d characters long. setting_bcc_recipients: Blind carbon copy recipients (bcc) button_annotate: Annotate +label_issues_by: Issues by %s diff --git a/lang/sv.yml b/lang/sv.yml index 7cbcc7a5..a4f55a17 100644 --- a/lang/sv.yml +++ b/lang/sv.yml @@ -549,3 +549,4 @@ field_time_zone: Time zone text_caracters_minimum: Must be at least %d characters long. setting_bcc_recipients: Blind carbon copy recipients (bcc) button_annotate: Annotate +label_issues_by: Issues by %s diff --git a/lang/zh.yml b/lang/zh.yml index ad18cecc..18fe3fda 100644 --- a/lang/zh.yml +++ b/lang/zh.yml @@ -551,3 +551,4 @@ field_time_zone: Time zone text_caracters_minimum: Must be at least %d characters long. setting_bcc_recipients: Blind carbon copy recipients (bcc) button_annotate: Annotate +label_issues_by: Issues by %s diff --git a/lib/redmine.rb b/lib/redmine.rb index 32239ce5..1f105343 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -27,6 +27,7 @@ Redmine::AccessControl.map do |map| # Issues map.permission :view_issues, {:projects => [:changelog, :roadmap], :issues => [:index, :changes, :show, :context_menu], + :versions => [:show, :status_by], :queries => :index, :reports => :issue_report}, :public => true map.permission :add_issues, {:projects => :add_issue} diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css index a10524db..f05fb9d9 100644 --- a/public/stylesheets/application.css +++ b/public/stylesheets/application.css @@ -267,6 +267,8 @@ table.progress td.open { background: #FFF none repeat scroll 0%; } p.pourcent {font-size: 80%;} p.progress-info {clear: left; font-style: italic; font-size: 80%;} +div#status_by { margin-left: 16px; margin-bottom: 16px; } + /***** Tabs *****/ #content .tabs{height: 2.6em;} #content .tabs ul{margin:0;} diff --git a/test/functional/versions_controller_test.rb b/test/functional/versions_controller_test.rb new file mode 100644 index 00000000..e8327938 --- /dev/null +++ b/test/functional/versions_controller_test.rb @@ -0,0 +1,42 @@ +# redMine - project management software +# Copyright (C) 2006-2007 Jean-Philippe Lang +# +# 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. + +require File.dirname(__FILE__) + '/../test_helper' +require 'versions_controller' + +# Re-raise errors caught by the controller. +class VersionsController; def rescue_action(e) raise e end; end + +class VersionsControllerTest < Test::Unit::TestCase + fixtures :projects, :versions, :users, :roles, :members, :enabled_modules + + def setup + @controller = VersionsController.new + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + User.current = nil + end + + def test_show + get :show, :id => 2 + assert_response :success + assert_template 'show' + assert_not_nil assigns(:version) + + assert_tag :tag => 'h2', :content => /1.0/ + end +end