From 80256cf298e669e5b63b86594f6d9607e830cc80 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Tue, 24 Aug 2010 15:27:12 +0000 Subject: [PATCH 01/72] Refactor: extract #bulk_update method from IssuesController#bulk_edit. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4037 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/issues_controller.rb | 45 ++++++++++++----------- app/views/issues/bulk_edit.rhtml | 2 +- config/routes.rb | 1 + lib/redmine.rb | 2 +- test/functional/issues_controller_test.rb | 32 ++++++++-------- test/integration/routing_test.rb | 3 ++ 6 files changed, 46 insertions(+), 39 deletions(-) diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index dc1cef492..29b131360 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -20,7 +20,7 @@ class IssuesController < ApplicationController default_search_scope :issues before_filter :find_issue, :only => [:show, :edit, :update] - before_filter :find_issues, :only => [:bulk_edit, :move, :perform_move, :destroy] + before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :move, :perform_move, :destroy] before_filter :find_project, :only => [:new, :create] before_filter :authorize, :except => [:index] before_filter :find_optional_project, :only => [:index] @@ -54,6 +54,7 @@ class IssuesController < ApplicationController :render => { :nothing => true, :status => :method_not_allowed } verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed } + verify :method => :post, :only => :bulk_update, :render => {:nothing => true, :status => :method_not_allowed } verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed } def index @@ -191,29 +192,31 @@ class IssuesController < ApplicationController # Bulk edit a set of issues def bulk_edit @issues.sort! - if request.post? - attributes = (params[:issue] || {}).reject {|k,v| v.blank?} - attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'} - attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values] - - unsaved_issue_ids = [] - @issues.each do |issue| - issue.reload - journal = issue.init_journal(User.current, params[:notes]) - issue.safe_attributes = attributes - call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue }) - unless issue.save - # Keep unsaved issue ids to display them in flash error - unsaved_issue_ids << issue.id - end - end - set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids) - redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project}) - return - end @available_statuses = Workflow.available_statuses(@project) @custom_fields = @project.all_issue_custom_fields end + + def bulk_update + @issues.sort! + + attributes = (params[:issue] || {}).reject {|k,v| v.blank?} + attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'} + attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values] + + unsaved_issue_ids = [] + @issues.each do |issue| + issue.reload + journal = issue.init_journal(User.current, params[:notes]) + issue.safe_attributes = attributes + call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue }) + unless issue.save + # Keep unsaved issue ids to display them in flash error + unsaved_issue_ids << issue.id + end + end + set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids) + redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project}) + end def destroy @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f diff --git a/app/views/issues/bulk_edit.rhtml b/app/views/issues/bulk_edit.rhtml index b01128840..5fdfd58a6 100644 --- a/app/views/issues/bulk_edit.rhtml +++ b/app/views/issues/bulk_edit.rhtml @@ -2,7 +2,7 @@ -<% form_tag() do %> +<% form_tag(:action => 'bulk_update') do %> <%= @issues.collect {|i| hidden_field_tag('ids[]', i.id)}.join %>
diff --git a/config/routes.rb b/config/routes.rb index b480a4900..4e1f9d7d5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -132,6 +132,7 @@ ActionController::Routing::Routes.draw do |map| issues_actions.connect 'issues/:id/quoted', :controller => 'journals', :action => 'new', :id => /\d+/ issues_actions.connect 'issues/:id/:action', :action => /edit|destroy/, :id => /\d+/ issues_actions.connect 'issues.:format', :action => 'create', :format => /xml/ + issues_actions.connect 'issues/bulk_edit', :action => 'bulk_update' end issues_routes.with_options :conditions => {:method => :put} do |issues_actions| issues_actions.connect 'issues/:id/edit', :action => 'update', :id => /\d+/ diff --git a/lib/redmine.rb b/lib/redmine.rb index b0a5a9f09..e1f1e2e16 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -66,7 +66,7 @@ Redmine::AccessControl.map do |map| :queries => :index, :reports => [:issue_report, :issue_report_details]} map.permission :add_issues, {:issues => [:new, :create, :update_form]} - map.permission :edit_issues, {:issues => [:edit, :update, :bulk_edit, :update_form], :journals => [:new]} + map.permission :edit_issues, {:issues => [:edit, :update, :bulk_edit, :bulk_update, :update_form], :journals => [:new]} map.permission :manage_issue_relations, {:issue_relations => [:new, :destroy]} map.permission :manage_subtasks, {} map.permission :add_issue_notes, {:issues => [:edit, :update], :journals => [:new]} diff --git a/test/functional/issues_controller_test.rb b/test/functional/issues_controller_test.rb index 7c00db708..7d69db8ea 100644 --- a/test/functional/issues_controller_test.rb +++ b/test/functional/issues_controller_test.rb @@ -910,10 +910,10 @@ class IssuesControllerTest < ActionController::TestCase assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'} end - def test_bulk_edit + def test_bulk_update @request.session[:user_id] = 2 # update issues priority - post :bulk_edit, :ids => [1, 2], :notes => 'Bulk editing', + post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing', :issue => {:priority_id => 7, :assigned_to_id => '', :custom_field_values => {'2' => ''}} @@ -929,10 +929,10 @@ class IssuesControllerTest < ActionController::TestCase assert_equal 1, journal.details.size end - def test_bullk_edit_should_send_a_notification + def test_bullk_update_should_send_a_notification @request.session[:user_id] = 2 ActionMailer::Base.deliveries.clear - post(:bulk_edit, + post(:bulk_update, { :ids => [1, 2], :notes => 'Bulk editing', @@ -947,10 +947,10 @@ class IssuesControllerTest < ActionController::TestCase assert_equal 2, ActionMailer::Base.deliveries.size end - def test_bulk_edit_status + def test_bulk_update_status @request.session[:user_id] = 2 # update issues priority - post :bulk_edit, :ids => [1, 2], :notes => 'Bulk editing status', + post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status', :issue => {:priority_id => '', :assigned_to_id => '', :status_id => '5'} @@ -960,10 +960,10 @@ class IssuesControllerTest < ActionController::TestCase assert issue.closed? end - def test_bulk_edit_custom_field + def test_bulk_update_custom_field @request.session[:user_id] = 2 # update issues priority - post :bulk_edit, :ids => [1, 2], :notes => 'Bulk editing custom field', + post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field', :issue => {:priority_id => '', :assigned_to_id => '', :custom_field_values => {'2' => '777'}} @@ -978,20 +978,20 @@ class IssuesControllerTest < ActionController::TestCase assert_equal '777', journal.details.first.value end - def test_bulk_unassign + def test_bulk_update_unassign assert_not_nil Issue.find(2).assigned_to @request.session[:user_id] = 2 # unassign issues - post :bulk_edit, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'} + post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'} assert_response 302 # check that the issues were updated assert_nil Issue.find(2).assigned_to end - def test_post_bulk_edit_should_allow_fixed_version_to_be_set_to_a_subproject + def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject @request.session[:user_id] = 2 - post :bulk_edit, :ids => [1,2], :issue => {:fixed_version_id => 4} + post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4} assert_response :redirect issues = Issue.find([1,2]) @@ -1001,17 +1001,17 @@ class IssuesControllerTest < ActionController::TestCase end end - def test_post_bulk_edit_should_redirect_back_using_the_back_url_parameter + def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter @request.session[:user_id] = 2 - post :bulk_edit, :ids => [1,2], :back_url => '/issues' + post :bulk_update, :ids => [1,2], :back_url => '/issues' assert_response :redirect assert_redirected_to '/issues' end - def test_post_bulk_edit_should_not_redirect_back_using_the_back_url_parameter_off_the_host + def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host @request.session[:user_id] = 2 - post :bulk_edit, :ids => [1,2], :back_url => 'http://google.com' + post :bulk_update, :ids => [1,2], :back_url => 'http://google.com' assert_response :redirect assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier diff --git a/test/integration/routing_test.rb b/test/integration/routing_test.rb index b05a1285d..119b506ae 100644 --- a/test/integration/routing_test.rb +++ b/test/integration/routing_test.rb @@ -108,6 +108,9 @@ class RoutingTest < ActionController::IntegrationTest should_route :post, "/issues/context_menu", :controller => 'context_menus', :action => 'issues' should_route :get, "/issues/changes", :controller => 'journals', :action => 'index' + + should_route :get, "/issues/bulk_edit", :controller => 'issues', :action => 'bulk_edit' + should_route :post, "/issues/bulk_edit", :controller => 'issues', :action => 'bulk_update' end context "issue categories" do From 2e2241cf8455db4ececccd3dc647ead4bb75cb24 Mon Sep 17 00:00:00 2001 From: Azamat Hackimov Date: Tue, 24 Aug 2010 15:32:11 +0000 Subject: [PATCH 02/72] Translation updates * de (#6198) * ja (#6189) * zh-TW (#6197) git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4038 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- config/locales/de.yml | 18 ++++++++++-------- config/locales/ja.yml | 6 +++--- config/locales/zh-TW.yml | 11 +++++------ 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/config/locales/de.yml b/config/locales/de.yml index e4d1de93d..78cc99d4a 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -68,10 +68,11 @@ de: other: "fast {{count}} Jahren" number: + # Default format for numbers format: - precision: 2 separator: ',' delimiter: '.' + precision: 2 currency: format: unit: '€' @@ -214,6 +215,7 @@ de: mail_body_wiki_content_added: "Die Wiki-Seite '{{page}}' wurde von {{author}} hinzugefügt." mail_subject_wiki_content_updated: "Wiki-Seite '{{page}}' erfolgreich aktualisiert" mail_body_wiki_content_updated: "Die Wiki-Seite '{{page}}' wurde von {{author}} aktualisiert." + gui_validation_error: 1 Fehler gui_validation_error_plural: "{{count}} Fehler" @@ -252,7 +254,7 @@ de: field_priority: Priorität field_fixed_version: Zielversion field_user: Benutzer - field_principal: Principal + field_principal: Auftraggeber field_role: Rolle field_homepage: Projekt-Homepage field_is_public: Öffentlich @@ -297,6 +299,7 @@ de: field_redirect_existing_links: Existierende Links umleiten field_estimated_hours: Geschätzter Aufwand field_column_names: Spalten + field_time_entries: Logzeit field_time_zone: Zeitzone field_searchable: Durchsuchbar field_default_value: Standardwert @@ -428,6 +431,8 @@ de: project_module_wiki: Wiki project_module_repository: Projektarchiv project_module_boards: Foren + project_module_calendar: Kalender + project_module_gantt: Gantt label_user: Benutzer label_user_plural: Benutzer @@ -485,7 +490,7 @@ de: label_my_page: Meine Seite label_my_account: Mein Konto label_my_projects: Meine Projekte - label_my_page_block: My page block + label_my_page_block: Bereich "Meine Seite" label_administration: Administration label_login: Anmelden label_logout: Abmelden @@ -499,7 +504,7 @@ de: label_user_activity: "Aktivität von {{value}}" label_new: Neu label_logged_as: Angemeldet als - label_environment: Environment + label_environment: Umgebung label_authentication: Authentifizierung label_auth_source: Authentifizierungs-Modus label_auth_source_new: Neuer Authentifizierungs-Modus @@ -831,7 +836,7 @@ de: button_quote: Zitieren button_duplicate: Duplizieren button_show: Anzeigen - + status_active: aktiv status_registered: angemeldet status_locked: gesperrt @@ -927,6 +932,3 @@ de: enumeration_activities: Aktivitäten (Zeiterfassung) enumeration_system_activity: System-Aktivität - field_time_entries: Log time - project_module_gantt: Gantt - project_module_calendar: Calendar diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 75d29df28..50ccc8729 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -305,6 +305,7 @@ ja: field_redirect_existing_links: 既存のリンクをリダイレクトする field_estimated_hours: 予定工数 field_column_names: 項目 + field_time_entries: 時間を記録 field_time_zone: タイムゾーン field_searchable: 検索条件に設定可能とする field_default_value: デフォルト値 @@ -436,6 +437,8 @@ ja: project_module_wiki: Wiki project_module_repository: リポジトリ project_module_boards: フォーラム + project_module_gantt: ガントチャート + project_module_calendar: カレンダー label_user: ユーザ label_user_plural: ユーザ @@ -934,6 +937,3 @@ ja: enumeration_doc_categories: 文書カテゴリ enumeration_activities: 作業分類 (時間トラッキング) enumeration_system_activity: システム作業分類 - field_time_entries: Log time - project_module_gantt: Gantt - project_module_calendar: Calendar diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 7d40c2ece..9a6d83b38 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -1,4 +1,4 @@ -# Chinese (Taiwan) translations for Ruby on Rails +# Chinese (Taiwan) translations for Ruby on Rails # by tsechingho (http://github.com/tsechingho) # See http://github.com/svenfuchs/rails-i18n/ for details. @@ -497,6 +497,8 @@ project_module_wiki: Wiki project_module_repository: 版本控管 project_module_boards: 討論區 + project_module_calendar: 日曆 + project_module_gantt: 甘特圖 label_user: 用戶 label_user_plural: 用戶清單 @@ -514,7 +516,7 @@ label_issue: 項目 label_issue_new: 建立新項目 label_issue_plural: 項目清單 - label_issue_view_all: 檢視全部的項目 + label_issue_view_all: 檢視所有項目 label_issues_by: "項目按 {{value}} 分組顯示" label_issue_added: 項目已新增 label_issue_updated: 項目已更新 @@ -564,7 +566,7 @@ label_last_login: 最近一次連線 label_registered_on: 註冊於 label_activity: 活動 - label_overall_activity: 檢視整體活動 + label_overall_activity: 整體活動 label_user_activity: "{{value}} 的活動" label_new: 建立新的... label_logged_as: 目前登入 @@ -995,6 +997,3 @@ enumeration_doc_categories: 文件分類 enumeration_activities: 活動 (時間追蹤) enumeration_system_activity: 系統活動 - - project_module_gantt: Gantt - project_module_calendar: Calendar From bc951a9d4f849099f907c5a01b066b75f9d7bf4c Mon Sep 17 00:00:00 2001 From: Azamat Hackimov Date: Tue, 24 Aug 2010 16:15:14 +0000 Subject: [PATCH 03/72] remove BOM from zh-TW git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4039 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- config/locales/zh-TW.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 9a6d83b38..0216525b0 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -1,4 +1,4 @@ -# Chinese (Taiwan) translations for Ruby on Rails +# Chinese (Taiwan) translations for Ruby on Rails # by tsechingho (http://github.com/tsechingho) # See http://github.com/svenfuchs/rails-i18n/ for details. From daa4272c0e7f657a0070424224cabccbbeca6197 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Wed, 25 Aug 2010 14:59:16 +0000 Subject: [PATCH 04/72] Refactor: extract method in bulk_update. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4040 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/issues_controller.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index 29b131360..f10ba03bf 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -198,11 +198,8 @@ class IssuesController < ApplicationController def bulk_update @issues.sort! + attributes = parse_params_for_bulk_issue_attributes(params) - attributes = (params[:issue] || {}).reject {|k,v| v.blank?} - attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'} - attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values] - unsaved_issue_ids = [] @issues.each do |issue| issue.reload @@ -318,4 +315,11 @@ private return false end end + + def parse_params_for_bulk_issue_attributes(params) + attributes = (params[:issue] || {}).reject {|k,v| v.blank?} + attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'} + attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values] + attributes + end end From 56e984b5e740398d0659432ecdee770de1943e84 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Thu, 26 Aug 2010 16:36:59 +0000 Subject: [PATCH 05/72] Refactor: convert many of the custom Issue routes to REST resources. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4041 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/calendars_controller.rb | 5 +- app/controllers/gantts_controller.rb | 4 ++ app/views/calendars/show.html.erb | 3 +- app/views/gantts/show.html.erb | 3 +- config/routes.rb | 66 +++++++------------ lib/redmine.rb | 4 +- .../context_menus_controller_test.rb | 2 +- test/integration/issues_test.rb | 2 +- test/integration/routing_test.rb | 8 +-- 9 files changed, 44 insertions(+), 53 deletions(-) diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb index 1115691a1..febacd075 100644 --- a/app/controllers/calendars_controller.rb +++ b/app/controllers/calendars_controller.rb @@ -32,8 +32,11 @@ class CalendarsController < ApplicationController @calendar.events = events end - render :layout => false if request.xhr? + render :action => 'show', :layout => false if request.xhr? end + def update + show + end end diff --git a/app/controllers/gantts_controller.rb b/app/controllers/gantts_controller.rb index cdfef5238..6a6071e86 100644 --- a/app/controllers/gantts_controller.rb +++ b/app/controllers/gantts_controller.rb @@ -43,4 +43,8 @@ class GanttsController < ApplicationController end end + def update + show + end + end diff --git a/app/views/calendars/show.html.erb b/app/views/calendars/show.html.erb index 4541cc0c8..afbceb9cd 100644 --- a/app/views/calendars/show.html.erb +++ b/app/views/calendars/show.html.erb @@ -1,6 +1,7 @@

<%= l(:label_calendar) %>

-<% form_tag({}, :id => 'query_form') do %> +<% form_tag(calendar_path, :method => :put, :id => 'query_form') do %> + <%= hidden_field_tag('project_id', @project.to_param) if @project%>
<%= l(:label_filter_plural) %>
diff --git a/app/views/gantts/show.html.erb b/app/views/gantts/show.html.erb index 653a41d41..5d4ef0dbf 100644 --- a/app/views/gantts/show.html.erb +++ b/app/views/gantts/show.html.erb @@ -1,6 +1,7 @@

<%= l(:label_gantt) %>

-<% form_tag(params.merge(:month => nil, :year => nil, :months => nil), :id => 'query_form') do %> +<% form_tag(gantt_path(:month => params[:month], :year => params[:year], :months => params[:months]), :method => :put, :id => 'query_form') do %> + <%= hidden_field_tag('project_id', @project.to_param) if @project%>
<%= l(:label_filter_plural) %>
diff --git a/config/routes.rb b/config/routes.rb index 4e1f9d7d5..ea5112216 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -104,58 +104,39 @@ ActionController::Routing::Routes.draw do |map| end map.resources :issue_moves, :only => [:new, :create], :path_prefix => '/issues', :as => 'move' + + # Misc issue routes. TODO: move into resources map.auto_complete_issues '/issues/auto_complete', :controller => 'auto_completes', :action => 'issues' - # TODO: would look nicer as /issues/:id/preview - map.preview_issue '/issues/preview/:id', :controller => 'previews', :action => 'issue' + map.preview_issue '/issues/preview/:id', :controller => 'previews', :action => 'issue' # TODO: would look nicer as /issues/:id/preview map.issues_context_menu '/issues/context_menu', :controller => 'context_menus', :action => 'issues' map.issue_changes '/issues/changes', :controller => 'journals', :action => 'index' - - map.with_options :controller => 'issues' do |issues_routes| - issues_routes.with_options :conditions => {:method => :get} do |issues_views| - issues_views.connect 'issues', :action => 'index' - issues_views.connect 'issues.:format', :action => 'index' - issues_views.connect 'projects/:project_id/issues', :action => 'index' - issues_views.connect 'projects/:project_id/issues.:format', :action => 'index' - issues_views.connect 'projects/:project_id/issues/new', :action => 'new' - issues_views.connect 'projects/:project_id/issues/gantt', :controller => 'gantts', :action => 'show' - issues_views.connect 'projects/:project_id/issues/calendar', :controller => 'calendars', :action => 'show' - issues_views.connect 'projects/:project_id/issues/:copy_from/copy', :action => 'new' - issues_views.connect 'issues/:id', :action => 'show', :id => /\d+/ - issues_views.connect 'issues/:id.:format', :action => 'show', :id => /\d+/ - issues_views.connect 'issues/:id/edit', :action => 'edit', :id => /\d+/ - end - issues_routes.with_options :conditions => {:method => :post} do |issues_actions| - issues_actions.connect 'issues', :action => 'index' - issues_actions.connect 'projects/:project_id/issues', :action => 'create' - issues_actions.connect 'projects/:project_id/issues/gantt', :controller => 'gantts', :action => 'show' - issues_actions.connect 'projects/:project_id/issues/calendar', :controller => 'calendars', :action => 'show' - issues_actions.connect 'issues/:id/quoted', :controller => 'journals', :action => 'new', :id => /\d+/ - issues_actions.connect 'issues/:id/:action', :action => /edit|destroy/, :id => /\d+/ - issues_actions.connect 'issues.:format', :action => 'create', :format => /xml/ - issues_actions.connect 'issues/bulk_edit', :action => 'bulk_update' - end - issues_routes.with_options :conditions => {:method => :put} do |issues_actions| - issues_actions.connect 'issues/:id/edit', :action => 'update', :id => /\d+/ - issues_actions.connect 'issues/:id.:format', :action => 'update', :id => /\d+/, :format => /xml/ - end - issues_routes.with_options :conditions => {:method => :delete} do |issues_actions| - issues_actions.connect 'issues/:id.:format', :action => 'destroy', :id => /\d+/, :format => /xml/ - end - issues_routes.connect 'issues/gantt', :controller => 'gantts', :action => 'show' - issues_routes.connect 'issues/calendar', :controller => 'calendars', :action => 'show' - issues_routes.connect 'issues/:action' + map.bulk_edit_issue 'issues/bulk_edit', :controller => 'issues', :action => 'bulk_edit', :conditions => { :method => :get } + map.bulk_update_issue 'issues/bulk_edit', :controller => 'issues', :action => 'bulk_update', :conditions => { :method => :post } + map.quoted_issue '/issues/:id/quoted', :controller => 'journals', :action => 'new', :id => /\d+/, :conditions => { :method => :post } + map.connect '/issues/:id/destroy', :controller => 'issues', :action => 'destroy', :conditions => { :method => :post } # legacy + + map.resource :gantt, :path_prefix => '/issues', :controller => 'gantts', :only => [:show, :update] + map.resource :gantt, :path_prefix => '/projects/:project_id/issues', :controller => 'gantts', :only => [:show, :update] + map.resource :calendar, :path_prefix => '/issues', :controller => 'calendars', :only => [:show, :update] + map.resource :calendar, :path_prefix => '/projects/:project_id/issues', :controller => 'calendars', :only => [:show, :update] + + map.with_options :controller => 'reports', :conditions => {:method => :get} do |reports| + reports.connect 'projects/:id/issues/report', :action => 'issue_report' + reports.connect 'projects/:id/issues/report/:detail', :action => 'issue_report_details' end + # Following two routes conflict with the resources because #index allows POST + map.connect '/issues', :controller => 'issues', :action => 'index', :conditions => { :method => :post } + map.connect '/issues/create', :controller => 'issues', :action => 'index', :conditions => { :method => :post } + + map.resources :issues, :member => { :edit => :post }, :collection => {} + map.resources :issues, :path_prefix => '/projects/:project_id', :collection => { :create => :post } + map.with_options :controller => 'issue_relations', :conditions => {:method => :post} do |relations| relations.connect 'issues/:issue_id/relations/:id', :action => 'new' relations.connect 'issues/:issue_id/relations/:id/destroy', :action => 'destroy' end - map.with_options :controller => 'reports', :conditions => {:method => :get} do |reports| - reports.connect 'projects/:id/issues/report', :action => 'issue_report' - reports.connect 'projects/:id/issues/report/:detail', :action => 'issue_report_details' - end - map.with_options :controller => 'news' do |news_routes| news_routes.with_options :conditions => {:method => :get} do |news_views| news_views.connect 'news', :action => 'index' @@ -203,6 +184,7 @@ ActionController::Routing::Routes.draw do |map| project_views.connect 'projects/:id/files', :action => 'list_files' project_views.connect 'projects/:id/files/new', :action => 'add_file' project_views.connect 'projects/:id/settings/:tab', :action => 'settings' + project_views.connect 'projects/:project_id/issues/:copy_from/copy', :controller => 'issues', :action => 'new' end projects.with_options :action => 'activity', :conditions => {:method => :get} do |activity| diff --git a/lib/redmine.rb b/lib/redmine.rb index e1f1e2e16..69b7b7a94 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -137,11 +137,11 @@ Redmine::AccessControl.map do |map| end map.project_module :calendar do |map| - map.permission :view_calendar, :calendars => :show + map.permission :view_calendar, :calendars => [:show, :update] end map.project_module :gantt do |map| - map.permission :view_gantt, :gantts => :show + map.permission :view_gantt, :gantts => [:show, :update] end end diff --git a/test/functional/context_menus_controller_test.rb b/test/functional/context_menus_controller_test.rb index eee9dc608..764350879 100644 --- a/test/functional/context_menus_controller_test.rb +++ b/test/functional/context_menus_controller_test.rb @@ -12,7 +12,7 @@ class ContextMenusControllerTest < ActionController::TestCase :attributes => { :href => '/issues/1/edit', :class => 'icon-edit' } assert_tag :tag => 'a', :content => 'Closed', - :attributes => { :href => '/issues/1/edit?issue%5Bstatus_id%5D=5', + :attributes => { :href => '/issues/1?issue%5Bstatus_id%5D=5', :class => '' } assert_tag :tag => 'a', :content => 'Immediate', :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&issue%5Bpriority_id%5D=8', diff --git a/test/integration/issues_test.rb b/test/integration/issues_test.rb index 0c1a36d34..c8a151cfd 100644 --- a/test/integration/issues_test.rb +++ b/test/integration/issues_test.rb @@ -69,7 +69,7 @@ class IssuesTest < ActionController::IntegrationTest log_user('jsmith', 'jsmith') set_tmp_attachments_directory - put 'issues/1/edit', + put 'issues/1', :notes => 'Some notes', :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'This is an attachment'}} assert_redirected_to "issues/1" diff --git a/test/integration/routing_test.rb b/test/integration/routing_test.rb index 119b506ae..9b5f39399 100644 --- a/test/integration/routing_test.rb +++ b/test/integration/routing_test.rb @@ -91,14 +91,14 @@ class RoutingTest < ActionController::IntegrationTest should_route :post, "/issues/1/quoted", :controller => 'journals', :action => 'new', :id => '1' should_route :get, "/issues/calendar", :controller => 'calendars', :action => 'show' - should_route :post, "/issues/calendar", :controller => 'calendars', :action => 'show' + should_route :put, "/issues/calendar", :controller => 'calendars', :action => 'update' should_route :get, "/projects/project-name/issues/calendar", :controller => 'calendars', :action => 'show', :project_id => 'project-name' - should_route :post, "/projects/project-name/issues/calendar", :controller => 'calendars', :action => 'show', :project_id => 'project-name' + should_route :put, "/projects/project-name/issues/calendar", :controller => 'calendars', :action => 'update', :project_id => 'project-name' should_route :get, "/issues/gantt", :controller => 'gantts', :action => 'show' - should_route :post, "/issues/gantt", :controller => 'gantts', :action => 'show' + should_route :put, "/issues/gantt", :controller => 'gantts', :action => 'update' should_route :get, "/projects/project-name/issues/gantt", :controller => 'gantts', :action => 'show', :project_id => 'project-name' - should_route :post, "/projects/project-name/issues/gantt", :controller => 'gantts', :action => 'show', :project_id => 'project-name' + should_route :put, "/projects/project-name/issues/gantt", :controller => 'gantts', :action => 'update', :project_id => 'project-name' should_route :get, "/issues/auto_complete", :controller => 'auto_completes', :action => 'issues' From 3eea03d70efd4e894a5020e8bae62a6af6e59803 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Thu, 26 Aug 2010 16:37:05 +0000 Subject: [PATCH 06/72] Refactor: extract link to previous and next month into helpers. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4042 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/helpers/calendars_helper.rb | 14 ++++++++++++++ app/views/calendars/show.html.erb | 9 +-------- 2 files changed, 15 insertions(+), 8 deletions(-) create mode 100644 app/helpers/calendars_helper.rb diff --git a/app/helpers/calendars_helper.rb b/app/helpers/calendars_helper.rb new file mode 100644 index 000000000..d3544f267 --- /dev/null +++ b/app/helpers/calendars_helper.rb @@ -0,0 +1,14 @@ +module CalendarsHelper + def link_to_previous_month(year, month) + link_to_remote ('« ' + (month==1 ? "#{month_name(12)} #{year-1}" : "#{month_name(month-1)}")), + {:update => "content", :url => { :year => (month==1 ? year-1 : year), :month =>(month==1 ? 12 : month-1) }}, + {:href => url_for(:action => 'show', :year => (month==1 ? year-1 : year), :month =>(month==1 ? 12 : month-1))} + end + + def link_to_next_month(year, month) + link_to_remote ((month==12 ? "#{month_name(1)} #{year+1}" : "#{month_name(month+1)}") + ' »'), + {:update => "content", :url => { :year => (month==12 ? year+1 : year), :month =>(month==12 ? 1 : month+1) }}, + {:href => url_for(:action => 'show', :year => (month==12 ? year+1 : year), :month =>(month==12 ? 1 : month+1))} + + end +end diff --git a/app/views/calendars/show.html.erb b/app/views/calendars/show.html.erb index afbceb9cd..09bf7ddf2 100644 --- a/app/views/calendars/show.html.erb +++ b/app/views/calendars/show.html.erb @@ -10,14 +10,7 @@

-<%= link_to_remote ('« ' + (@month==1 ? "#{month_name(12)} #{@year-1}" : "#{month_name(@month-1)}")), - {:update => "content", :url => { :year => (@month==1 ? @year-1 : @year), :month =>(@month==1 ? 12 : @month-1) }}, - {:href => url_for(:action => 'show', :year => (@month==1 ? @year-1 : @year), :month =>(@month==1 ? 12 : @month-1))} - %> | -<%= link_to_remote ((@month==12 ? "#{month_name(1)} #{@year+1}" : "#{month_name(@month+1)}") + ' »'), - {:update => "content", :url => { :year => (@month==12 ? @year+1 : @year), :month =>(@month==12 ? 1 : @month+1) }}, - {:href => url_for(:action => 'show', :year => (@month==12 ? @year+1 : @year), :month =>(@month==12 ? 1 : @month+1))} - %> + <%= link_to_previous_month(@year, @month) %> | <%= link_to_next_month(@year, @month) %>

From 91380eeaab3028aac9a33559fe3932a86b066b6e Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Thu, 26 Aug 2010 16:37:11 +0000 Subject: [PATCH 07/72] Refactor: extract ternary operators to temps. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4043 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/helpers/calendars_helper.rb | 36 +++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/app/helpers/calendars_helper.rb b/app/helpers/calendars_helper.rb index d3544f267..52cc42512 100644 --- a/app/helpers/calendars_helper.rb +++ b/app/helpers/calendars_helper.rb @@ -1,14 +1,38 @@ module CalendarsHelper def link_to_previous_month(year, month) - link_to_remote ('« ' + (month==1 ? "#{month_name(12)} #{year-1}" : "#{month_name(month-1)}")), - {:update => "content", :url => { :year => (month==1 ? year-1 : year), :month =>(month==1 ? 12 : month-1) }}, - {:href => url_for(:action => 'show', :year => (month==1 ? year-1 : year), :month =>(month==1 ? 12 : month-1))} + target_year, target_month = if month == 1 + [year - 1, 12] + else + [year, month - 1] + end + + name = if target_month == 12 + "#{month_name(target_month)} #{target_year}" + else + "#{month_name(target_month)}" + end + + link_to_remote ('« ' + name), + {:update => "content", :url => { :year => target_year, :month => target_month }}, + {:href => url_for(:action => 'show', :year => target_year, :month => target_month)} end def link_to_next_month(year, month) - link_to_remote ((month==12 ? "#{month_name(1)} #{year+1}" : "#{month_name(month+1)}") + ' »'), - {:update => "content", :url => { :year => (month==12 ? year+1 : year), :month =>(month==12 ? 1 : month+1) }}, - {:href => url_for(:action => 'show', :year => (month==12 ? year+1 : year), :month =>(month==12 ? 1 : month+1))} + target_year, target_month = if month == 12 + [year + 1, 1] + else + [year, month + 1] + end + + name = if target_month == 1 + "#{month_name(target_month)} #{target_year}" + else + "#{month_name(target_month)}" + end + + link_to_remote (name + ' »'), + {:update => "content", :url => { :year => target_year, :month => target_month }}, + {:href => url_for(:action => 'show', :year => target_year, :month =>target_month)} end end From ab1e74d16cb21ee1f7410c94538c2b6e2bdd3b5b Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Thu, 26 Aug 2010 16:37:16 +0000 Subject: [PATCH 08/72] Refactor: extract target link generation. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4044 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/helpers/calendars_helper.rb | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/helpers/calendars_helper.rb b/app/helpers/calendars_helper.rb index 52cc42512..2bc028d65 100644 --- a/app/helpers/calendars_helper.rb +++ b/app/helpers/calendars_helper.rb @@ -12,9 +12,11 @@ module CalendarsHelper "#{month_name(target_month)}" end - link_to_remote ('« ' + name), - {:update => "content", :url => { :year => target_year, :month => target_month }}, - {:href => url_for(:action => 'show', :year => target_year, :month => target_month)} + link_target = calendar_path(:year => target_year, :month => target_month) + + link_to_remote(('« ' + name), + {:update => "content", :url => link_target, :method => :put}, + {:href => link_target}) end def link_to_next_month(year, month) @@ -30,9 +32,11 @@ module CalendarsHelper "#{month_name(target_month)}" end - link_to_remote (name + ' »'), - {:update => "content", :url => { :year => target_year, :month => target_month }}, - {:href => url_for(:action => 'show', :year => target_year, :month =>target_month)} + link_target = calendar_path(:year => target_year, :month => target_month) + + link_to_remote((name + ' »'), + {:update => "content", :url => link_target, :method => :put}, + {:href => link_target}) end end From 30821586cb31dd2af9b634477c026a817e294986 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Thu, 26 Aug 2010 16:37:21 +0000 Subject: [PATCH 09/72] Add the project_id to the Calendar link to month. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4045 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/helpers/calendars_helper.rb | 12 +++++++----- app/views/calendars/show.html.erb | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/helpers/calendars_helper.rb b/app/helpers/calendars_helper.rb index 2bc028d65..88327f51c 100644 --- a/app/helpers/calendars_helper.rb +++ b/app/helpers/calendars_helper.rb @@ -1,5 +1,5 @@ module CalendarsHelper - def link_to_previous_month(year, month) + def link_to_previous_month(year, month, options={}) target_year, target_month = if month == 1 [year - 1, 12] else @@ -11,15 +11,16 @@ module CalendarsHelper else "#{month_name(target_month)}" end - - link_target = calendar_path(:year => target_year, :month => target_month) + + project_id = options[:project].present? ? options[:project].to_param : nil + link_target = calendar_path(:year => target_year, :month => target_month, :project_id => project_id) link_to_remote(('« ' + name), {:update => "content", :url => link_target, :method => :put}, {:href => link_target}) end - def link_to_next_month(year, month) + def link_to_next_month(year, month, options={}) target_year, target_month = if month == 12 [year + 1, 1] else @@ -32,7 +33,8 @@ module CalendarsHelper "#{month_name(target_month)}" end - link_target = calendar_path(:year => target_year, :month => target_month) + project_id = options[:project].present? ? options[:project].to_param : nil + link_target = calendar_path(:year => target_year, :month => target_month, :project_id => project_id) link_to_remote((name + ' »'), {:update => "content", :url => link_target, :method => :put}, diff --git a/app/views/calendars/show.html.erb b/app/views/calendars/show.html.erb index 09bf7ddf2..530416661 100644 --- a/app/views/calendars/show.html.erb +++ b/app/views/calendars/show.html.erb @@ -10,7 +10,7 @@

- <%= link_to_previous_month(@year, @month) %> | <%= link_to_next_month(@year, @month) %> + <%= link_to_previous_month(@year, @month, :project => @project) %> | <%= link_to_next_month(@year, @month, :project => @project) %>

From 5b08b2f33d4072e2a41545637ef40365c3ef7ea4 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Thu, 26 Aug 2010 16:37:26 +0000 Subject: [PATCH 10/72] Refactor: extract common code to link_to_month. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4046 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/helpers/calendars_helper.rb | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/app/helpers/calendars_helper.rb b/app/helpers/calendars_helper.rb index 88327f51c..08e665dcd 100644 --- a/app/helpers/calendars_helper.rb +++ b/app/helpers/calendars_helper.rb @@ -12,12 +12,7 @@ module CalendarsHelper "#{month_name(target_month)}" end - project_id = options[:project].present? ? options[:project].to_param : nil - link_target = calendar_path(:year => target_year, :month => target_month, :project_id => project_id) - - link_to_remote(('« ' + name), - {:update => "content", :url => link_target, :method => :put}, - {:href => link_target}) + link_to_month(('« ' + name), target_year, target_month, options) end def link_to_next_month(year, month, options={}) @@ -33,12 +28,18 @@ module CalendarsHelper "#{month_name(target_month)}" end - project_id = options[:project].present? ? options[:project].to_param : nil - link_target = calendar_path(:year => target_year, :month => target_month, :project_id => project_id) + link_to_month((name + ' »'), target_year, target_month, options) + end - link_to_remote((name + ' »'), + def link_to_month(link_name, year, month, options={}) + project_id = options[:project].present? ? options[:project].to_param : nil + + link_target = calendar_path(:year => year, :month => month, :project_id => project_id) + + link_to_remote(link_name, {:update => "content", :url => link_target, :method => :put}, {:href => link_target}) end + end From b925325ddbd5fb594f20221dd724f7822ed4c3d3 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Fri, 27 Aug 2010 14:05:54 +0000 Subject: [PATCH 11/72] Refactor: extract ProjectsController#activity to a new Activities controller. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4047 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/activities_controller.rb | 59 +++++++++++++ app/controllers/projects_controller.rb | 50 +---------- .../index.html.erb} | 0 app/views/boards/index.rhtml | 4 +- app/views/projects/index.rhtml | 2 +- app/views/projects/show.rhtml | 2 +- app/views/users/show.rhtml | 6 +- app/views/welcome/index.rhtml | 2 +- app/views/wiki/special_date_index.rhtml | 4 +- config/routes.rb | 2 +- lib/redmine.rb | 4 +- test/functional/activities_controller_test.rb | 87 +++++++++++++++++++ test/functional/projects_controller_test.rb | 81 ----------------- test/integration/routing_test.rb | 8 +- 14 files changed, 166 insertions(+), 145 deletions(-) create mode 100644 app/controllers/activities_controller.rb rename app/views/{projects/activity.rhtml => activities/index.html.erb} (100%) create mode 100644 test/functional/activities_controller_test.rb diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb new file mode 100644 index 000000000..ae6a67369 --- /dev/null +++ b/app/controllers/activities_controller.rb @@ -0,0 +1,59 @@ +class ActivitiesController < ApplicationController + menu_item :activity + before_filter :find_optional_project + accept_key_auth :index + + def index + @days = Setting.activity_days_default.to_i + + if params[:from] + begin; @date_to = params[:from].to_date + 1; rescue; end + end + + @date_to ||= Date.today + 1 + @date_from = @date_to - @days + @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1') + @author = (params[:user_id].blank? ? nil : User.active.find(params[:user_id])) + + @activity = Redmine::Activity::Fetcher.new(User.current, :project => @project, + :with_subprojects => @with_subprojects, + :author => @author) + @activity.scope_select {|t| !params["show_#{t}"].nil?} + @activity.scope = (@author.nil? ? :default : :all) if @activity.scope.empty? + + events = @activity.events(@date_from, @date_to) + + if events.empty? || stale?(:etag => [events.first, User.current]) + respond_to do |format| + format.html { + @events_by_day = events.group_by(&:event_date) + render :layout => false if request.xhr? + } + format.atom { + title = l(:label_activity) + if @author + title = @author.name + elsif @activity.scope.size == 1 + title = l("label_#{@activity.scope.first.singularize}_plural") + end + render_feed(events, :title => "#{@project || Setting.app_title}: #{title}") + } + end + end + + rescue ActiveRecord::RecordNotFound + render_404 + end + + private + + # TODO: refactor, duplicated in projects_controller + def find_optional_project + return true unless params[:id] + @project = Project.find(params[:id]) + authorize + rescue ActiveRecord::RecordNotFound + render_404 + end + +end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 44071d214..6bb766386 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -17,17 +17,15 @@ class ProjectsController < ApplicationController menu_item :overview - menu_item :activity, :only => :activity menu_item :roadmap, :only => :roadmap menu_item :files, :only => [:list_files, :add_file] menu_item :settings, :only => :settings - before_filter :find_project, :except => [ :index, :list, :add, :copy, :activity ] - before_filter :find_optional_project, :only => :activity - before_filter :authorize, :except => [ :index, :list, :add, :copy, :archive, :unarchive, :destroy, :activity ] + before_filter :find_project, :except => [ :index, :list, :add, :copy ] + before_filter :authorize, :except => [ :index, :list, :add, :copy, :archive, :unarchive, :destroy] before_filter :authorize_global, :only => :add before_filter :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ] - accept_key_auth :activity, :index + accept_key_auth :index after_filter :only => [:add, :edit, :archive, :unarchive, :destroy] do |controller| if controller.request.post? @@ -313,48 +311,6 @@ class ProjectsController < ApplicationController @versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?} end - def activity - @days = Setting.activity_days_default.to_i - - if params[:from] - begin; @date_to = params[:from].to_date + 1; rescue; end - end - - @date_to ||= Date.today + 1 - @date_from = @date_to - @days - @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1') - @author = (params[:user_id].blank? ? nil : User.active.find(params[:user_id])) - - @activity = Redmine::Activity::Fetcher.new(User.current, :project => @project, - :with_subprojects => @with_subprojects, - :author => @author) - @activity.scope_select {|t| !params["show_#{t}"].nil?} - @activity.scope = (@author.nil? ? :default : :all) if @activity.scope.empty? - - events = @activity.events(@date_from, @date_to) - - if events.empty? || stale?(:etag => [events.first, User.current]) - respond_to do |format| - format.html { - @events_by_day = events.group_by(&:event_date) - render :layout => false if request.xhr? - } - format.atom { - title = l(:label_activity) - if @author - title = @author.name - elsif @activity.scope.size == 1 - title = l("label_#{@activity.scope.first.singularize}_plural") - end - render_feed(events, :title => "#{@project || Setting.app_title}: #{title}") - } - end - end - - rescue ActiveRecord::RecordNotFound - render_404 - end - private def find_optional_project return true unless params[:id] diff --git a/app/views/projects/activity.rhtml b/app/views/activities/index.html.erb similarity index 100% rename from app/views/projects/activity.rhtml rename to app/views/activities/index.html.erb diff --git a/app/views/boards/index.rhtml b/app/views/boards/index.rhtml index 7cc6a0e2f..6310f942e 100644 --- a/app/views/boards/index.rhtml +++ b/app/views/boards/index.rhtml @@ -30,11 +30,11 @@ <% other_formats_links do |f| %> - <%= f.link_to 'Atom', :url => {:controller => 'projects', :action => 'activity', :id => @project, :show_messages => 1, :key => User.current.rss_key} %> + <%= f.link_to 'Atom', :url => {:controller => 'activities', :action => 'index', :id => @project, :show_messages => 1, :key => User.current.rss_key} %> <% end %> <% content_for :header_tags do %> - <%= auto_discovery_link_tag(:atom, {:controller => 'projects', :action => 'activity', :id => @project, :format => 'atom', :show_messages => 1, :key => User.current.rss_key}) %> + <%= auto_discovery_link_tag(:atom, {:controller => 'activities', :action => 'index', :id => @project, :format => 'atom', :show_messages => 1, :key => User.current.rss_key}) %> <% end %> <% html_title l(:label_board_plural) %> diff --git a/app/views/projects/index.rhtml b/app/views/projects/index.rhtml index b4952e905..a2ba1c389 100644 --- a/app/views/projects/index.rhtml +++ b/app/views/projects/index.rhtml @@ -6,7 +6,7 @@ <%= link_to(l(:label_project_new), {:controller => 'projects', :action => 'add'}, :class => 'icon icon-add') + ' |' if User.current.allowed_to?(:add_project, nil, :global => true) %> <%= link_to(l(:label_issue_view_all), { :controller => 'issues' }) + ' |' if User.current.allowed_to?(:view_issues, nil, :global => true) %> <%= link_to(l(:label_overall_spent_time), { :controller => 'time_entries' }) + ' |' if User.current.allowed_to?(:view_time_entries, nil, :global => true) %> - <%= link_to l(:label_overall_activity), { :controller => 'projects', :action => 'activity' }%> + <%= link_to l(:label_overall_activity), { :controller => 'activities', :action => 'index' }%>

<%=l(:label_project_plural)%>

diff --git a/app/views/projects/show.rhtml b/app/views/projects/show.rhtml index 62ea27848..0ad9a11da 100644 --- a/app/views/projects/show.rhtml +++ b/app/views/projects/show.rhtml @@ -74,7 +74,7 @@ <% end %> <% content_for :header_tags do %> -<%= auto_discovery_link_tag(:atom, {:action => 'activity', :id => @project, :format => 'atom', :key => User.current.rss_key}) %> +<%= auto_discovery_link_tag(:atom, {:controller => 'activities', :action => 'index', :id => @project, :format => 'atom', :key => User.current.rss_key}) %> <% end %> <% html_title(l(:label_overview)) -%> diff --git a/app/views/users/show.rhtml b/app/views/users/show.rhtml index df5aec825..a2f90226c 100644 --- a/app/views/users/show.rhtml +++ b/app/views/users/show.rhtml @@ -35,7 +35,7 @@
<% unless @events_by_day.empty? %> -

<%= link_to l(:label_activity), :controller => 'projects', :action => 'activity', :id => nil, :user_id => @user, :from => @events_by_day.keys.first %>

+

<%= link_to l(:label_activity), :controller => 'activities', :action => 'index', :id => nil, :user_id => @user, :from => @events_by_day.keys.first %>

<%=l(:label_reported_issues)%>: <%= Issue.count(:conditions => ["author_id=?", @user.id]) %> @@ -57,11 +57,11 @@

<% other_formats_links do |f| %> - <%= f.link_to 'Atom', :url => {:controller => 'projects', :action => 'activity', :id => nil, :user_id => @user, :key => User.current.rss_key} %> + <%= f.link_to 'Atom', :url => {:controller => 'activities', :action => 'index', :id => nil, :user_id => @user, :key => User.current.rss_key} %> <% end %> <% content_for :header_tags do %> - <%= auto_discovery_link_tag(:atom, :controller => 'projects', :action => 'activity', :user_id => @user, :format => :atom, :key => User.current.rss_key) %> + <%= auto_discovery_link_tag(:atom, :controller => 'activities', :action => 'index', :user_id => @user, :format => :atom, :key => User.current.rss_key) %> <% end %> <% end %> <%= call_hook :view_account_right_bottom, :user => @user %> diff --git a/app/views/welcome/index.rhtml b/app/views/welcome/index.rhtml index 6ac09c153..982d6da52 100644 --- a/app/views/welcome/index.rhtml +++ b/app/views/welcome/index.rhtml @@ -34,6 +34,6 @@ <% content_for :header_tags do %> <%= auto_discovery_link_tag(:atom, {:controller => 'news', :action => 'index', :key => User.current.rss_key, :format => 'atom'}, :title => "#{Setting.app_title}: #{l(:label_news_latest)}") %> -<%= auto_discovery_link_tag(:atom, {:controller => 'projects', :action => 'activity', :key => User.current.rss_key, :format => 'atom'}, +<%= auto_discovery_link_tag(:atom, {:controller => 'activities', :action => 'index', :key => User.current.rss_key, :format => 'atom'}, :title => "#{Setting.app_title}: #{l(:label_activity)}") %> <% end %> diff --git a/app/views/wiki/special_date_index.rhtml b/app/views/wiki/special_date_index.rhtml index 228737a5e..b34fb8464 100644 --- a/app/views/wiki/special_date_index.rhtml +++ b/app/views/wiki/special_date_index.rhtml @@ -23,11 +23,11 @@ <% unless @pages.empty? %> <% other_formats_links do |f| %> - <%= f.link_to 'Atom', :url => {:controller => 'projects', :action => 'activity', :id => @project, :show_wiki_edits => 1, :key => User.current.rss_key} %> + <%= f.link_to 'Atom', :url => {:controller => 'activities', :action => 'index', :id => @project, :show_wiki_edits => 1, :key => User.current.rss_key} %> <%= f.link_to('HTML', :url => {:action => 'special', :page => 'export'}) if User.current.allowed_to?(:export_wiki_pages, @project) %> <% end %> <% end %> <% content_for :header_tags do %> -<%= auto_discovery_link_tag(:atom, :controller => 'projects', :action => 'activity', :id => @project, :show_wiki_edits => 1, :format => 'atom', :key => User.current.rss_key) %> +<%= auto_discovery_link_tag(:atom, :controller => 'activities', :action => 'index', :id => @project, :show_wiki_edits => 1, :format => 'atom', :key => User.current.rss_key) %> <% end %> diff --git a/config/routes.rb b/config/routes.rb index ea5112216..1465e1ffe 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -187,7 +187,7 @@ ActionController::Routing::Routes.draw do |map| project_views.connect 'projects/:project_id/issues/:copy_from/copy', :controller => 'issues', :action => 'new' end - projects.with_options :action => 'activity', :conditions => {:method => :get} do |activity| + projects.with_options :controller => 'activities', :action => 'index', :conditions => {:method => :get} do |activity| activity.connect 'projects/:id/activity' activity.connect 'projects/:id/activity.:format' activity.connect 'activity', :id => nil diff --git a/lib/redmine.rb b/lib/redmine.rb index 69b7b7a94..7040a1f99 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -44,7 +44,7 @@ end # Permissions Redmine::AccessControl.map do |map| - map.permission :view_project, {:projects => [:show, :activity]}, :public => true + map.permission :view_project, {:projects => [:show], :activities => [:index]}, :public => true map.permission :search_project, {:search => :index}, :public => true map.permission :add_project, {:projects => :add}, :require => :loggedin map.permission :edit_project, {:projects => [:settings, :edit]}, :require => :member @@ -185,7 +185,7 @@ end Redmine::MenuManager.map :project_menu do |menu| menu.push :overview, { :controller => 'projects', :action => 'show' } - menu.push :activity, { :controller => 'projects', :action => 'activity' } + menu.push :activity, { :controller => 'activities', :action => 'index' } menu.push :roadmap, { :controller => 'projects', :action => 'roadmap' }, :if => Proc.new { |p| p.shared_versions.any? } menu.push :issues, { :controller => 'issues', :action => 'index' }, :param => :project_id, :caption => :label_issue_plural diff --git a/test/functional/activities_controller_test.rb b/test/functional/activities_controller_test.rb new file mode 100644 index 000000000..ba9c33985 --- /dev/null +++ b/test/functional/activities_controller_test.rb @@ -0,0 +1,87 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class ActivitiesControllerTest < ActionController::TestCase + fixtures :all + + def test_project_index + get :index, :id => 1, :with_subprojects => 0 + assert_response :success + assert_template 'index' + assert_not_nil assigns(:events_by_day) + + assert_tag :tag => "h3", + :content => /#{2.days.ago.to_date.day}/, + :sibling => { :tag => "dl", + :child => { :tag => "dt", + :attributes => { :class => /issue-edit/ }, + :child => { :tag => "a", + :content => /(#{IssueStatus.find(2).name})/, + } + } + } + end + + def test_previous_project_index + get :index, :id => 1, :from => 3.days.ago.to_date + assert_response :success + assert_template 'index' + assert_not_nil assigns(:events_by_day) + + assert_tag :tag => "h3", + :content => /#{3.day.ago.to_date.day}/, + :sibling => { :tag => "dl", + :child => { :tag => "dt", + :attributes => { :class => /issue/ }, + :child => { :tag => "a", + :content => /#{Issue.find(1).subject}/, + } + } + } + end + + def test_global_index + get :index + assert_response :success + assert_template 'index' + assert_not_nil assigns(:events_by_day) + + assert_tag :tag => "h3", + :content => /#{5.day.ago.to_date.day}/, + :sibling => { :tag => "dl", + :child => { :tag => "dt", + :attributes => { :class => /issue/ }, + :child => { :tag => "a", + :content => /#{Issue.find(5).subject}/, + } + } + } + end + + def test_user_index + get :index, :user_id => 2 + assert_response :success + assert_template 'index' + assert_not_nil assigns(:events_by_day) + + assert_tag :tag => "h3", + :content => /#{3.day.ago.to_date.day}/, + :sibling => { :tag => "dl", + :child => { :tag => "dt", + :attributes => { :class => /issue/ }, + :child => { :tag => "a", + :content => /#{Issue.find(1).subject}/, + } + } + } + end + + def test_index_atom_feed + get :index, :format => 'atom' + assert_response :success + assert_template 'common/feed.atom.rxml' + assert_tag :tag => 'entry', :child => { + :tag => 'link', + :attributes => {:href => 'http://test.host/issues/11'}} + end + +end diff --git a/test/functional/projects_controller_test.rb b/test/functional/projects_controller_test.rb index 988496cec..f9c8ce022 100644 --- a/test/functional/projects_controller_test.rb +++ b/test/functional/projects_controller_test.rb @@ -400,87 +400,6 @@ class ProjectsControllerTest < ActionController::TestCase assert assigns(:versions).include?(Version.find(4)), "Shared version not found" assert assigns(:versions).include?(@subproject_version), "Subproject version not found" end - def test_project_activity - get :activity, :id => 1, :with_subprojects => 0 - assert_response :success - assert_template 'activity' - assert_not_nil assigns(:events_by_day) - - assert_tag :tag => "h3", - :content => /#{2.days.ago.to_date.day}/, - :sibling => { :tag => "dl", - :child => { :tag => "dt", - :attributes => { :class => /issue-edit/ }, - :child => { :tag => "a", - :content => /(#{IssueStatus.find(2).name})/, - } - } - } - end - - def test_previous_project_activity - get :activity, :id => 1, :from => 3.days.ago.to_date - assert_response :success - assert_template 'activity' - assert_not_nil assigns(:events_by_day) - - assert_tag :tag => "h3", - :content => /#{3.day.ago.to_date.day}/, - :sibling => { :tag => "dl", - :child => { :tag => "dt", - :attributes => { :class => /issue/ }, - :child => { :tag => "a", - :content => /#{Issue.find(1).subject}/, - } - } - } - end - - def test_global_activity - get :activity - assert_response :success - assert_template 'activity' - assert_not_nil assigns(:events_by_day) - - assert_tag :tag => "h3", - :content => /#{5.day.ago.to_date.day}/, - :sibling => { :tag => "dl", - :child => { :tag => "dt", - :attributes => { :class => /issue/ }, - :child => { :tag => "a", - :content => /#{Issue.find(5).subject}/, - } - } - } - end - - def test_user_activity - get :activity, :user_id => 2 - assert_response :success - assert_template 'activity' - assert_not_nil assigns(:events_by_day) - - assert_tag :tag => "h3", - :content => /#{3.day.ago.to_date.day}/, - :sibling => { :tag => "dl", - :child => { :tag => "dt", - :attributes => { :class => /issue/ }, - :child => { :tag => "a", - :content => /#{Issue.find(1).subject}/, - } - } - } - end - - def test_activity_atom_feed - get :activity, :format => 'atom' - assert_response :success - assert_template 'common/feed.atom.rxml' - assert_tag :tag => 'entry', :child => { - :tag => 'link', - :attributes => {:href => 'http://test.host/issues/11'}} - end - def test_archive @request.session[:user_id] = 1 # admin post :archive, :id => 1 diff --git a/test/integration/routing_test.rb b/test/integration/routing_test.rb index 9b5f39399..62fdaf47a 100644 --- a/test/integration/routing_test.rb +++ b/test/integration/routing_test.rb @@ -19,8 +19,8 @@ require "#{File.dirname(__FILE__)}/../test_helper" class RoutingTest < ActionController::IntegrationTest context "activities" do - should_route :get, "/activity", :controller => 'projects', :action => 'activity', :id => nil - should_route :get, "/activity.atom", :controller => 'projects', :action => 'activity', :id => nil, :format => 'atom' + should_route :get, "/activity", :controller => 'activities', :action => 'index', :id => nil + should_route :get, "/activity.atom", :controller => 'activities', :action => 'index', :id => nil, :format => 'atom' end context "attachments" do @@ -175,8 +175,8 @@ class RoutingTest < ActionController::IntegrationTest should_route :get, "/projects/33/files", :controller => 'projects', :action => 'list_files', :id => '33' should_route :get, "/projects/33/files/new", :controller => 'projects', :action => 'add_file', :id => '33' should_route :get, "/projects/33/roadmap", :controller => 'projects', :action => 'roadmap', :id => '33' - should_route :get, "/projects/33/activity", :controller => 'projects', :action => 'activity', :id => '33' - should_route :get, "/projects/33/activity.atom", :controller => 'projects', :action => 'activity', :id => '33', :format => 'atom' + should_route :get, "/projects/33/activity", :controller => 'activities', :action => 'index', :id => '33' + should_route :get, "/projects/33/activity.atom", :controller => 'activities', :action => 'index', :id => '33', :format => 'atom' should_route :post, "/projects/new", :controller => 'projects', :action => 'add' should_route :post, "/projects.xml", :controller => 'projects', :action => 'add', :format => 'xml' From f92dcdf50a4ca854aabbf2b85e67dfcd71800ed4 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Fri, 27 Aug 2010 20:08:59 +0000 Subject: [PATCH 12/72] Allow setting an issue's notes via params[:issue][:notes]. (XML API) git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4048 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/issues_controller.rb | 2 +- test/integration/issues_api_test.rb | 20 +++++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index f10ba03bf..0364e307c 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -270,7 +270,7 @@ private @edit_allowed = User.current.allowed_to?(:edit_issues, @project) @time_entry = TimeEntry.new - @notes = params[:notes] + @notes = params[:notes] || (params[:issue].present? ? params[:issue][:notes] : nil) @issue.init_journal(User.current, @notes) # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue] diff --git a/test/integration/issues_api_test.rb b/test/integration/issues_api_test.rb index a1e2cb703..e26cf643b 100644 --- a/test/integration/issues_api_test.rb +++ b/test/integration/issues_api_test.rb @@ -198,7 +198,7 @@ class IssuesApiTest < ActionController::IntegrationTest setup do @issue_count = Issue.count @journal_count = Journal.count - @attributes = {:subject => 'API update'} + @attributes = {:subject => 'API update', :notes => 'A new note'} put '/issues/1.xml', {:issue => @attributes}, :authorization => credentials('jsmith') end @@ -214,10 +214,15 @@ class IssuesApiTest < ActionController::IntegrationTest assert_equal Journal.count, @journal_count + 1 end + should "add the note to the journal" do + journal = Journal.last + assert_equal "A new note", journal.notes + end + should "update the issue" do issue = Issue.find(1) @attributes.each do |attribute, value| - assert_equal value, issue.send(attribute) + assert_equal value, issue.send(attribute) unless attribute == :notes end end @@ -252,7 +257,7 @@ class IssuesApiTest < ActionController::IntegrationTest setup do @issue_count = Issue.count @journal_count = Journal.count - @attributes = {:subject => 'API update'} + @attributes = {:subject => 'API update', :notes => 'A new note'} put '/issues/1.json', {:issue => @attributes}, :authorization => credentials('jsmith') end @@ -268,13 +273,18 @@ class IssuesApiTest < ActionController::IntegrationTest assert_equal Journal.count, @journal_count + 1 end + should "add the note to the journal" do + journal = Journal.last + assert_equal "A new note", journal.notes + end + should "update the issue" do issue = Issue.find(1) @attributes.each do |attribute, value| - assert_equal value, issue.send(attribute) + assert_equal value, issue.send(attribute) unless attribute == :notes end end - + end context "PUT /issues/1.json with failed update" do From 1ebb0d9caec7cdc8d07c53d4d2fff416e41408aa Mon Sep 17 00:00:00 2001 From: Azamat Hackimov Date: Sun, 29 Aug 2010 12:54:04 +0000 Subject: [PATCH 13/72] Translation updates * mk (#6208) * sv (#6210) git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4049 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- config/locales/mk.yml | 915 ++++++++++++++++++ config/locales/sv.yml | 8 +- .../javascripts/calendar/lang/calendar-mk.js | 37 +- .../jstoolbar/lang/jstoolbar-mk.js | 23 +- 4 files changed, 950 insertions(+), 33 deletions(-) create mode 100644 config/locales/mk.yml diff --git a/config/locales/mk.yml b/config/locales/mk.yml new file mode 100644 index 000000000..c680f900c --- /dev/null +++ b/config/locales/mk.yml @@ -0,0 +1,915 @@ +mk: + # Text direction: Left-to-Right (ltr) or Right-to-Left (rtl) + direction: ltr + date: + formats: + # Use the strftime parameters for formats. + # When no format has been given, it uses default. + # You can provide other formats here if you like! + default: "%d/%m/%Y" + short: "%d %b" + long: "%d %B, %Y" + + day_names: [недела, понеделник, вторник, среда, четврток, петок, сабота] + abbr_day_names: [нед, пон, вто, сре, чет, пет, саб] + + # Don't forget the nil at the beginning; there's no such thing as a 0th month + month_names: [~, јануари, февруари, март, април, мај, јуни, јули, август, септември, октомври, ноември, декември] + abbr_month_names: [~, јан, фев, мар, апр, мај, јун, јул, авг, сеп, окт, ное, дек] + # Used in date_select and datime_select. + order: [ :day, :month, :year ] + + time: + formats: + default: "%d/%m/%Y %H:%M" + time: "%H:%M" + short: "%d %b %H:%M" + long: "%d %B, %Y %H:%M" + am: "предпладне" + pm: "попладне" + + datetime: + distance_in_words: + half_a_minute: "пола минута" + less_than_x_seconds: + one: "помалку од 1 секунда" + other: "помалку од {{count}} секунди" + x_seconds: + one: "1 секунда" + other: "{{count}} секунди" + less_than_x_minutes: + one: "помалку од 1 минута" + other: "помалку од {{count}} минути" + x_minutes: + one: "1 минута" + other: "{{count}} минути" + about_x_hours: + one: "околу 1 час" + other: "околу {{count}} часа" + x_days: + one: "1 ден" + other: "{{count}} дена" + about_x_months: + one: "околу 1 месец" + other: "околу {{count}} месеци" + x_months: + one: "1 месец" + other: "{{count}} месеци" + about_x_years: + one: "околу 1 година" + other: "околу {{count}} години" + over_x_years: + one: "преку 1 година" + other: "преку {{count}} години" + almost_x_years: + one: "скоро 1 година" + other: "скоро {{count}} години" + + number: + # Default format for numbers + format: + separator: "." + delimiter: "" + precision: 3 + human: + format: + delimiter: "" + precision: 1 + storage_units: + format: "%n %u" + units: + byte: + one: "Byte" + other: "Bytes" + kb: "KB" + mb: "MB" + gb: "GB" + tb: "TB" + + +# Used in array.to_sentence. + support: + array: + sentence_connector: "и" + skip_last_comma: false + + activerecord: + errors: + messages: + inclusion: "не е вклучено во листата" + exclusion: "е резервирано" + invalid: "е невалидно" + confirmation: "не се совпаѓа со потврдата" + accepted: "мора да е прифатено" + empty: "неможе да е празно" + blank: "неможе да е празно" + too_long: "е предолго (макс. {{count}} знаци)" + too_short: "е прекратко (мин. {{count}} знаци)" + wrong_length: "е погрешна должина (треба да е {{count}} знаци)" + taken: "е веќе зафатено" + not_a_number: "не е број" + not_a_date: "не е валидна дата" + greater_than: "мора да е поголемо од {{count}}" + greater_than_or_equal_to: "мора да е поголемо или еднакво на {{count}}" + equal_to: "мора да е еднакво на {{count}}" + less_than: "мора да е помало од {{count}}" + less_than_or_equal_to: "мора да е помало или еднакво на {{count}}" + odd: "мора да е непарно" + even: "мора да е парно" + greater_than_start_date: "мора да е поголема од почетната дата" + not_same_project: "не припаѓа на истиот проект" + circular_dependency: "Оваа врска ќе креира кружна зависност" + cant_link_an_issue_with_a_descendant: "Задача неможе да се поврзе со една од нејзините подзадачи" + + actionview_instancetag_blank_option: Изберете + + general_text_No: 'Не' + general_text_Yes: 'Да' + general_text_no: 'не' + general_text_yes: 'да' + general_lang_name: 'Macedonian (Македонски)' + general_csv_separator: ',' + general_csv_decimal_separator: '.' + general_csv_encoding: UTF-8 + general_pdf_encoding: UTF-8 + general_first_day_of_week: '1' + + notice_account_updated: Профилот е успешно ажуриран. + notice_account_invalid_creditentials: Неточен корисник или лозинка + notice_account_password_updated: Лозинката е успешно ажурирана. + notice_account_wrong_password: Погрешна лозинка + notice_account_register_done: Профилот е успешно креиран. За активација, клкнете на врската што ви е пратена по е-пошта. + notice_account_unknown_email: Непознат корисник. + notice_can_t_change_password: This account uses an external authentication source. Impossible to change the password. + notice_account_lost_email_sent: An email with instructions to choose a new password has been sent to you. + notice_account_activated: Your account has been activated. You can now log in. + notice_successful_create: Успешно креирање. + notice_successful_update: Успешно ажурирање. + notice_successful_delete: Успешно бришење. + notice_successful_connection: Успешна конекција. + notice_file_not_found: The page you were trying to access doesn't exist or has been removed. + notice_locking_conflict: Data has been updated by another user. + notice_not_authorized: You are not authorized to access this page. + notice_email_sent: "Е-порака е пратена на {{value}}" + notice_email_error: "Се случи грешка при праќање на е-пораката ({{value}})" + notice_feeds_access_key_reseted: Вашиот RSS клуч за пристап е reset. + notice_api_access_key_reseted: Вашиот API клуч за пристап е reset. + notice_failed_to_save_issues: "Failed to save {{count}} issue(s) on {{total}} selected: {{ids}}." + notice_failed_to_save_members: "Failed to save member(s): {{errors}}." + notice_no_issue_selected: "No issue is selected! Please, check the issues you want to edit." + notice_account_pending: "Your account was created and is now pending administrator approval." + notice_default_data_loaded: Default configuration successfully loaded. + notice_unable_delete_version: Unable to delete version. + notice_unable_delete_time_entry: Unable to delete time log entry. + notice_issue_done_ratios_updated: Issue done ratios updated. + + error_can_t_load_default_data: "Default configuration could not be loaded: {{value}}" + error_scm_not_found: "The entry or revision was not found in the repository." + error_scm_command_failed: "An error occurred when trying to access the repository: {{value}}" + error_scm_annotate: "The entry does not exist or can not be annotated." + error_issue_not_found_in_project: 'The issue was not found or does not belong to this project' + error_no_tracker_in_project: 'No tracker is associated to this project. Please check the Project settings.' + error_no_default_issue_status: 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").' + error_can_not_delete_custom_field: Unable to delete custom field + error_can_not_delete_tracker: "This tracker contains issues and can't be deleted." + error_can_not_remove_role: "This role is in use and can not be deleted." + error_can_not_reopen_issue_on_closed_version: 'An issue assigned to a closed version can not be reopened' + error_can_not_archive_project: This project can not be archived + error_issue_done_ratios_not_updated: "Issue done ratios not updated." + error_workflow_copy_source: 'Please select a source tracker or role' + error_workflow_copy_target: 'Please select target tracker(s) and role(s)' + error_unable_delete_issue_status: 'Unable to delete issue status' + error_unable_to_connect: "Unable to connect ({{value}})" + warning_attachments_not_saved: "{{count}} file(s) could not be saved." + + mail_subject_lost_password: "Вашата {{value}} лозинка" + mail_body_lost_password: 'To change your password, click on the following link:' + mail_subject_register: "Your {{value}} account activation" + mail_body_register: 'To activate your account, click on the following link:' + mail_body_account_information_external: "You can use your {{value}} account to log in." + mail_body_account_information: Your account information + mail_subject_account_activation_request: "{{value}} account activation request" + mail_body_account_activation_request: "Нов корисник ({{value}}) е регистриран. The account is pending your approval:" + mail_subject_reminder: "{{count}} issue(s) due in the next {{days}} days" + mail_body_reminder: "{{count}} issue(s) that are assigned to you are due in the next {{days}} days:" + mail_subject_wiki_content_added: "'{{page}}' wiki page has been added" + mail_body_wiki_content_added: "The '{{page}}' wiki page has been added by {{author}}." + mail_subject_wiki_content_updated: "'{{page}}' wiki page has been updated" + mail_body_wiki_content_updated: "The '{{page}}' wiki page has been updated by {{author}}." + + gui_validation_error: 1 грешка + gui_validation_error_plural: "{{count}} грешки" + + field_name: Име + field_description: Опис + field_summary: Краток опис + field_is_required: Задолжително + field_firstname: Име + field_lastname: Презиме + field_mail: Е-пошта + field_filename: Датотека + field_filesize: Големина + field_downloads: Превземања + field_author: Автор + field_created_on: Креиран + field_updated_on: Ажурирано + field_field_format: Формат + field_is_for_all: За сите проекти + field_possible_values: Можни вредности + field_regexp: Regular expression + field_min_length: Минимална должина + field_max_length: Максимална должина + field_value: Вредност + field_category: Категорија + field_title: Наслов + field_project: Проект + field_issue: Задача + field_status: Статус + field_notes: Белешки + field_is_closed: Задачата е затворена + field_is_default: Default value + field_tracker: Tracker + field_subject: Наслов + field_due_date: Краен рок + field_assigned_to: Доделена на + field_priority: Приоритет + field_fixed_version: Target version + field_user: Корисник + field_principal: Principal + field_role: Улога + field_homepage: Веб страна + field_is_public: Јавен + field_parent: Подпроект на + field_is_in_roadmap: Issues displayed in roadmap + field_login: Корисник + field_mail_notification: Известувања по e-пошта + field_admin: Администратор + field_last_login_on: Последна најава + field_language: Јазик + field_effective_date: Дата + field_password: Лозинка + field_new_password: Нова лозинка + field_password_confirmation: Потврда + field_version: Верзија + field_type: Тип + field_host: Хост + field_port: Порт + field_account: Account + field_base_dn: Base DN + field_attr_login: Login attribute + field_attr_firstname: Firstname attribute + field_attr_lastname: Lastname attribute + field_attr_mail: Email attribute + field_onthefly: Моментално (On-the-fly) креирање на корисници + field_start_date: Почеток + field_done_ratio: % Завршено + field_auth_source: Режим на автентикација + field_hide_mail: Криј ја мојата адреса на е-пошта + field_comments: Коментар + field_url: URL + field_start_page: Почетна страна + field_subproject: Подпроект + field_hours: Часови + field_activity: Активност + field_spent_on: Дата + field_identifier: Идентификатор + field_is_filter: Користи како филтер + field_issue_to: Поврзана задача + field_delay: Доцнење + field_assignable: На оваа улога може да се доделуваат задачи + field_redirect_existing_links: Пренасочи ги постоечките врски + field_estimated_hours: Проценето време + field_column_names: Колони + field_time_entries: Бележи време + field_time_zone: Временска зона + field_searchable: Може да се пребарува + field_default_value: Default value + field_comments_sorting: Прикажувај коментари + field_parent_title: Parent page + field_editable: Може да се уредува + field_watcher: Watcher + field_identity_url: OpenID URL + field_content: Содржина + field_group_by: Групирај ги резултатите според + field_sharing: Споделување + field_parent_issue: Parent task + + setting_app_title: Наслов на апликацијата + setting_app_subtitle: Поднаслов на апликацијата + setting_welcome_text: Текст за добредојде + setting_default_language: Default јазик + setting_login_required: Задолжителна автентикација + setting_self_registration: Само-регистрација + setting_attachment_max_size: Макс. големина на прилог + setting_issues_export_limit: Issues export limit + setting_mail_from: Emission email address + setting_bcc_recipients: Blind carbon copy recipients (bcc) + setting_plain_text_mail: Текстуални е-пораки (без HTML) + setting_host_name: Име на хост и патека + setting_text_formatting: Форматирање на текст + setting_wiki_compression: Компресија на историјата на вики + setting_feeds_limit: Feed content limit + setting_default_projects_public: Новите проекти се иницијално јавни + setting_autofetch_changesets: Autofetch commits + setting_sys_api_enabled: Enable WS for repository management + setting_commit_ref_keywords: Referencing keywords + setting_commit_fix_keywords: Fixing keywords + setting_autologin: Автоматска најава + setting_date_format: Формат на дата + setting_time_format: Формат на време + setting_cross_project_issue_relations: Дозволи релации на задачи меѓу проекти + setting_issue_list_default_columns: Default columns displayed on the issue list + setting_repositories_encodings: Repositories encodings + setting_commit_logs_encoding: Commit messages encoding + setting_emails_footer: Emails footer + setting_protocol: Протокол + setting_per_page_options: Objects per page options + setting_user_format: Приказ на корисниците + setting_activity_days_default: Денови прикажана во активноста на проектот + setting_display_subprojects_issues: Прикажи ги задачите на подпроектите во главните проекти + setting_enabled_scm: Овозможи SCM + setting_mail_handler_body_delimiters: "Truncate emails after one of these lines" + setting_mail_handler_api_enabled: Enable WS for incoming emails + setting_mail_handler_api_key: API клуч + setting_sequential_project_identifiers: Генерирај последователни идентификатори на проекти + setting_gravatar_enabled: Користи Gravatar кориснички икони + setting_gravatar_default: Default Gravatar image + setting_diff_max_lines_displayed: Max number of diff lines displayed + setting_file_max_size_displayed: Max size of text files displayed inline + setting_repository_log_display_limit: Maximum number of revisions displayed on file log + setting_openid: Дозволи OpenID најава и регистрација + setting_password_min_length: Мин. должина на лозинка + setting_new_project_user_role_id: Улога доделена на неадминистраторски корисник кој креира проект + setting_default_projects_modules: Default enabled modules for new projects + setting_issue_done_ratio: Calculate the issue done ratio with + setting_issue_done_ratio_issue_field: Use the issue field + setting_issue_done_ratio_issue_status: Use the issue status + setting_start_of_week: Start calendars on + setting_rest_api_enabled: Enable REST web service + setting_cache_formatted_text: Cache formatted text + + permission_add_project: Креирај проекти + permission_add_subprojects: Креирај подпроекти + permission_edit_project: Уреди проект + permission_select_project_modules: Изберете модули за проект + permission_manage_members: Manage members + permission_manage_project_activities: Manage project activities + permission_manage_versions: Manage versions + permission_manage_categories: Manage issue categories + permission_view_issues: Прегледај задачи + permission_add_issues: Додавај задачи + permission_edit_issues: Уредувај задачи + permission_manage_issue_relations: Manage issue relations + permission_add_issue_notes: Додавај белешки + permission_edit_issue_notes: Уредувај белешки + permission_edit_own_issue_notes: Уредувај сопствени белешки + permission_move_issues: Преместувај задачи + permission_delete_issues: Бриши задачи + permission_manage_public_queries: Manage public queries + permission_save_queries: Save queries + permission_view_gantt: View gantt chart + permission_view_calendar: View calendar + permission_view_issue_watchers: View watchers list + permission_add_issue_watchers: Add watchers + permission_delete_issue_watchers: Delete watchers + permission_log_time: Бележи потрошено време + permission_view_time_entries: Прегледај потрошено време + permission_edit_time_entries: Уредувај белешки за потрошено време + permission_edit_own_time_entries: Уредувај сопствени белешки за потрошено време + permission_manage_news: Manage news + permission_comment_news: Коментирај на вести + permission_manage_documents: Manage documents + permission_view_documents: Прегледувај документи + permission_manage_files: Manage files + permission_view_files: Прегледувај датотеки + permission_manage_wiki: Manage wiki + permission_rename_wiki_pages: Преименувај вики страници + permission_delete_wiki_pages: Бриши вики страници + permission_view_wiki_pages: Прегледувај вики + permission_view_wiki_edits: Прегледувај вики историја + permission_edit_wiki_pages: Уредувај вики страници + permission_delete_wiki_pages_attachments: Бриши прилози + permission_protect_wiki_pages: Заштитувај вики страници + permission_manage_repository: Manage repository + permission_browse_repository: Browse repository + permission_view_changesets: View changesets + permission_commit_access: Commit access + permission_manage_boards: Manage boards + permission_view_messages: View messages + permission_add_messages: Post messages + permission_edit_messages: Уредувај пораки + permission_edit_own_messages: Уредувај сопствени пораки + permission_delete_messages: Бриши пораки + permission_delete_own_messages: Бриши сопствени пораки + permission_export_wiki_pages: Export wiki pages + permission_manage_subtasks: Manage subtasks + + project_module_issue_tracking: Следење на задачи + project_module_time_tracking: Следење на време + project_module_news: Вести + project_module_documents: Документи + project_module_files: Датотеки + project_module_wiki: Вики + project_module_repository: Repository + project_module_boards: Форуми + project_module_calendar: Календар + project_module_gantt: Gantt + + label_user: Корисник + label_user_plural: Корисници + label_user_new: Нов корисник + label_user_anonymous: Анонимен + label_project: Проект + label_project_new: Нов проект + label_project_plural: Проекти + label_x_projects: + zero: нема проекти + one: 1 проект + other: "{{count}} проекти" + label_project_all: Сите проекти + label_project_latest: Последните проекти + label_issue: Задача + label_issue_new: Нова задача + label_issue_plural: Задачи + label_issue_view_all: Прегледај ги сите задачи + label_issues_by: "Задачи по {{value}}" + label_issue_added: Задачата е додадена + label_issue_updated: Задачата е ажурирана + label_document: Документ + label_document_new: Нов документ + label_document_plural: Документи + label_document_added: Документот е додаден + label_role: Улога + label_role_plural: Улоги + label_role_new: Нова улога + label_role_and_permissions: Улоги и овластувања + label_member: Член + label_member_new: Нов член + label_member_plural: Членови + label_tracker: Tracker + label_tracker_plural: Trackers + label_tracker_new: New tracker + label_workflow: Workflow + label_issue_status: Статус на задача + label_issue_status_plural: Статуси на задачи + label_issue_status_new: Нов статус + label_issue_category: Категорија на задача + label_issue_category_plural: Категории на задачи + label_issue_category_new: Нова категорија + label_custom_field: Прилагодено поле + label_custom_field_plural: Прилагодени полиња + label_custom_field_new: Ново прилагодено поле + label_enumerations: Enumerations + label_enumeration_new: Нова вредност + label_information: Информација + label_information_plural: Информации + label_please_login: Најави се + label_register: Регистрирај се + label_login_with_open_id_option: или најави се со OpenID + label_password_lost: Изгубена лозинка + label_home: Почетна + label_my_page: Мојата страна + label_my_account: Мојот профил + label_my_projects: Мои проекти + label_my_page_block: Блок елемент + label_administration: Администрација + label_login: Најави се + label_logout: Одјави се + label_help: Помош + label_reported_issues: Пријавени задачи + label_assigned_to_me_issues: Задачи доделени на мене + label_last_login: Последна најава + label_registered_on: Регистриран на + label_activity: Активност + label_overall_activity: Севкупна активност + label_user_activity: "Активност на {{value}}" + label_new: Нова + label_logged_as: Најавени сте како + label_environment: Опкружување + label_authentication: Автентикација + label_auth_source: Режим на автентикација + label_auth_source_new: Нов режим на автентикација + label_auth_source_plural: Режими на автентикација + label_subproject_plural: Подпроекти + label_subproject_new: Нов подпроект + label_and_its_subprojects: "{{value}} и неговите подпроекти" + label_min_max_length: Мин. - Макс. должина + label_list: Листа + label_date: Дата + label_integer: Integer + label_float: Float + label_boolean: Boolean + label_string: Текст + label_text: Долг текст + label_attribute: Атрибут + label_attribute_plural: Атрибути + label_download: "{{count}} превземање" + label_download_plural: "{{count}} превземања" + label_no_data: Нема податоци за прикажување + label_change_status: Промени статус + label_history: Историја + label_attachment: Датотека + label_attachment_new: Нова датотека + label_attachment_delete: Избриши датотека + label_attachment_plural: Датотеки + label_file_added: Датотеката е додадена + label_report: Извештај + label_report_plural: Извештаи + label_news: Новост + label_news_new: Додади новост + label_news_plural: Новости + label_news_latest: Последни новости + label_news_view_all: Прегледај ги сите новости + label_news_added: Новостта е додадена + label_settings: Settings + label_overview: Преглед + label_version: Верзија + label_version_new: Нова верзија + label_version_plural: Верзии + label_close_versions: Затвори ги завршените врзии + label_confirmation: Потврда + label_export_to: 'Достапно и во:' + label_read: Прочитај... + label_public_projects: Јавни проекти + label_open_issues: отворена + label_open_issues_plural: отворени + label_closed_issues: затворена + label_closed_issues_plural: затворени + label_x_open_issues_abbr_on_total: + zero: 0 отворени / {{total}} + one: 1 отворена / {{total}} + other: "{{count}} отворени / {{total}}" + label_x_open_issues_abbr: + zero: 0 отворени + one: 1 отворена + other: "{{count}} отворени" + label_x_closed_issues_abbr: + zero: 0 затворени + one: 1 затворена + other: "{{count}} затворени" + label_total: Вкупно + label_permissions: Овластувања + label_current_status: Моментален статус + label_new_statuses_allowed: Дозволени нови статуси + label_all: сите + label_none: ниеден + label_nobody: никој + label_next: Следно + label_previous: Претходно + label_used_by: Користено од + label_details: Детали + label_add_note: Додади белешка + label_per_page: По страна + label_calendar: Календар + label_months_from: месеци од + label_gantt: Gantt + label_internal: Internal + label_last_changes: "последни {{count}} промени" + label_change_view_all: Прегледај ги сите промени + label_personalize_page: Прилагоди ја странава + label_comment: Коментар + label_comment_plural: Коментари + label_x_comments: + zero: нема коментари + one: 1 коментар + other: "{{count}} коментари" + label_comment_add: Додади коментар + label_comment_added: Коментарот е додаден + label_comment_delete: Избриши коментари + label_query: Custom query + label_query_plural: Custom queries + label_query_new: New query + label_filter_add: Додади филтер + label_filter_plural: Филтри + label_equals: е + label_not_equals: не е + label_in_less_than: за помалку од + label_in_more_than: за повеќе од + label_greater_or_equal: '>=' + label_less_or_equal: '<=' + label_in: во + label_today: денес + label_all_time: цело време + label_yesterday: вчера + label_this_week: оваа недела + label_last_week: минатата недела + label_last_n_days: "последните {{count}} дена" + label_this_month: овој месец + label_last_month: минатиот месец + label_this_year: оваа година + label_date_range: Date range + label_less_than_ago: пред помалку од денови + label_more_than_ago: пред повеќе од денови + label_ago: пред денови + label_contains: содржи + label_not_contains: не содржи + label_day_plural: денови + label_repository: Складиште + label_repository_plural: Складишта + label_browse: Прелистувај + label_modification: "{{count}} промени" + label_modification_plural: "{{count}} промени" + label_branch: Гранка + label_tag: Tag + label_revision: Ревизија + label_revision_plural: Ревизии + label_revision_id: "Ревизија {{value}}" + label_associated_revisions: Associated revisions + label_added: added + label_modified: modified + label_copied: copied + label_renamed: renamed + label_deleted: deleted + label_latest_revision: Последна ревизија + label_latest_revision_plural: Последни ревизии + label_view_revisions: Прегледај ги ревизиите + label_view_all_revisions: Прегледај ги сите ревизии + label_max_size: Макс. големина + label_sort_highest: Премести најгоре + label_sort_higher: Премести нагоре + label_sort_lower: Премести надоле + label_sort_lowest: Премести најдоле + label_roadmap: Roadmap + label_roadmap_due_in: "Due in {{value}}" + label_roadmap_overdue: "Касни {{value}}" + label_roadmap_no_issues: Нема задачи за оваа верзија + label_search: Барај + label_result_plural: Резултати + label_all_words: Сите зборови + label_wiki: Вики + label_wiki_edit: Вики уредување + label_wiki_edit_plural: Вики уредувања + label_wiki_page: Вики страница + label_wiki_page_plural: Вики страници + label_index_by_title: Индекс по наслов + label_index_by_date: Индекс по дата + label_current_version: Current version + label_preview: Preview + label_feed_plural: Feeds + label_changes_details: Детали за сите промени + label_issue_tracking: Следење на задачи + label_spent_time: Потрошено време + label_overall_spent_time: Вкупно потрошено време + label_f_hour: "{{value}} час" + label_f_hour_plural: "{{value}} часа" + label_time_tracking: Следење на време + label_change_plural: Промени + label_statistics: Статистики + label_commits_per_month: Commits per month + label_commits_per_author: Commits per author + label_view_diff: View differences + label_diff_inline: inline + label_diff_side_by_side: side by side + label_options: Опции + label_copy_workflow_from: Copy workflow from + label_permissions_report: Permissions report + label_watched_issues: Watched issues + label_related_issues: Поврзани задачи + label_applied_status: Applied status + label_loading: Loading... + label_relation_new: Нова релација + label_relation_delete: Избриши релација + label_relates_to: related to + label_duplicates: дупликати + label_duplicated_by: duplicated by + label_blocks: blocks + label_blocked_by: блокирано од + label_precedes: претходи + label_follows: следи + label_end_to_start: крај до почеток + label_end_to_end: крај до крај + label_start_to_start: почеток до почеток + label_start_to_end: почеток до крај + label_stay_logged_in: Останете најавени + label_disabled: disabled + label_show_completed_versions: Show completed versions + label_me: јас + label_board: Форум + label_board_new: Нов форум + label_board_plural: Форуми + label_board_locked: Заклучен + label_board_sticky: Sticky + label_topic_plural: Теми + label_message_plural: Пораки + label_message_last: Последна порака + label_message_new: Нова порака + label_message_posted: Поракате е додадена + label_reply_plural: Одговори + label_send_information: Испрати ги информациите за профилот на корисникот + label_year: Година + label_month: Месец + label_week: Недела + label_date_from: Од + label_date_to: До + label_language_based: Според јазикот на корисникот + label_sort_by: "Подреди според {{value}}" + label_send_test_email: Испрати тест е-порака + label_feeds_access_key: RSS клуч за пристап + label_missing_feeds_access_key: Недостика RSS клуч за пристап + label_feeds_access_key_created_on: "RSS клучот за пристап креиран пред {{value}}" + label_module_plural: Модули + label_added_time_by: "Додадено од {{author}} пред {{age}}" + label_updated_time_by: "Ажурирано од {{author}} пред {{age}}" + label_updated_time: "Ажурирано пред {{value}}" + label_jump_to_a_project: Префрли се на проект... + label_file_plural: Датотеки + label_changeset_plural: Changesets + label_default_columns: Основни колони + label_no_change_option: (Без промена) + label_bulk_edit_selected_issues: Групно уредување на задачи + label_theme: Тема + label_default: Default + label_search_titles_only: Пребарувај само наслови + label_user_mail_option_all: "За било кој настан во сите мои проекти" + label_user_mail_option_selected: "За било кој настан само во избраните проекти..." + label_user_mail_option_none: "Само за работите кои ги следам или од кои сум дел" + label_user_mail_no_self_notified: "Не ме известувај за промените што јас ги правам" + label_registration_activation_by_email: активација на профил преку е-пошта + label_registration_manual_activation: мануелна активација на профил + label_registration_automatic_activation: автоматска активација на профил + label_display_per_page: "По страна: {{value}}" + label_age: Age + label_change_properties: Change properties + label_general: Општо + label_more: Повеќе + label_scm: SCM + label_plugins: Додатоци + label_ldap_authentication: LDAP автентикација + label_downloads_abbr: Превземања + label_optional_description: Опис (незадолжително) + label_add_another_file: Додади уште една датотека + label_preferences: Preferences + label_chronological_order: Во хронолошки ред + label_reverse_chronological_order: In reverse chronological order + label_planning: Планирање + label_incoming_emails: Дојдовни е-пораки + label_generate_key: Генерирај клуч + label_issue_watchers: Watchers + label_example: Пример + label_display: Прикажи + label_sort: Подреди + label_ascending: Растечки + label_descending: Опаѓачки + label_date_from_to: Од {{start}} до {{end}} + label_wiki_content_added: Вики страница додадена + label_wiki_content_updated: Вики страница ажурирана + label_group: Група + label_group_plural: Групи + label_group_new: Нова група + label_time_entry_plural: Потрошено време + label_version_sharing_none: Не споделено + label_version_sharing_descendants: Со сите подпроекти + label_version_sharing_hierarchy: Со хиерархијата на проектот + label_version_sharing_tree: Со дрвото на проектот + label_version_sharing_system: Со сите проекти + label_update_issue_done_ratios: Update issue done ratios + label_copy_source: Извор + label_copy_target: Дестинација + label_copy_same_as_target: Исто како дестинацијата + label_display_used_statuses_only: Only display statuses that are used by this tracker + label_api_access_key: API клуч за пристап + label_missing_api_access_key: Недостига API клуч за пристап + label_api_access_key_created_on: "API клучот за пристап е креиран пред {{value}}" + label_profile: Профил + label_subtask_plural: Подзадачи + label_project_copy_notifications: Праќај известувања по е-пошта при копирање на проект + + button_login: Најави се + button_submit: Испрати + button_save: Зачувај + button_check_all: Штиклирај ги сите + button_uncheck_all: Одштиклирај ги сите + button_delete: Избриши + button_create: Креирај + button_create_and_continue: Креирај и продолжи + button_test: Тест + button_edit: Уреди + button_add: Додади + button_change: Промени + button_apply: Примени + button_clear: Избриши + button_lock: Заклучи + button_unlock: Отклучи + button_download: Превземи + button_list: List + button_view: Прегледај + button_move: Премести + button_move_and_follow: Премести и следи + button_back: Back + button_cancel: Откажи + button_activate: Активирај + button_sort: Подреди + button_log_time: Бележи време + button_rollback: Rollback to this version + button_watch: Следи + button_unwatch: Не следи + button_reply: Одговори + button_archive: Архивирај + button_unarchive: Одархивирај + button_reset: Reset + button_rename: Преименувај + button_change_password: Промени лозинка + button_copy: Копирај + button_copy_and_follow: Копирај и следи + button_annotate: Annotate + button_update: Ажурирај + button_configure: Конфигурирај + button_quote: Цитирај + button_duplicate: Копирај + button_show: Show + + status_active: активни + status_registered: регистрирани + status_locked: заклучени + + version_status_open: отворени + version_status_locked: заклучени + version_status_closed: затворени + + field_active: Active + + text_select_mail_notifications: Изберете за кои настани да се праќаат известувања по е-пошта да се праќаат. + text_regexp_info: eg. ^[A-Z0-9]+$ + text_min_max_length_info: 0 значи без ограничување + text_project_destroy_confirmation: Дали сте сигурни дека сакате да го избришете проектот и сите поврзани податоци? + text_subprojects_destroy_warning: "Неговите подпроекти: {{value}} исто така ќе бидат избришани." + text_workflow_edit: Select a role and a tracker to edit the workflow + text_are_you_sure: Дали сте сигурни? + text_journal_changed: "{{label}} променето од {{old}} во {{new}}" + text_journal_set_to: "{{label}} set to {{value}}" + text_journal_deleted: "{{label}} избришан ({{old}})" + text_journal_added: "{{label}} {{value}} додаден" + text_tip_task_begin_day: задачи што почнуваат овој ден + text_tip_task_end_day: задачи што завршуваат овој ден + text_tip_task_begin_end_day: задачи што почнуваат и завршуваат овој ден + text_project_identifier_info: 'Само мали букви (a-z), бројки и dashes се дозволени
По зачувувањето, идентификаторот неможе да се смени.' + text_caracters_maximum: "{{count}} знаци максимум." + text_caracters_minimum: "Мора да е најмалку {{count}} знаци долго." + text_length_between: "Должина помеѓу {{min}} и {{max}} знаци." + text_tracker_no_workflow: No workflow defined for this tracker + text_unallowed_characters: Недозволени знаци + text_comma_separated: Дозволени се повеќе вредности (разделени со запирка). + text_line_separated: Дозволени се повеќе вредности (една линија за секоја вредност). + text_issues_ref_in_commit_messages: Referencing and fixing issues in commit messages + text_issue_added: "Задачата {{id}} е пријавена од {{author}}." + text_issue_updated: "Зачата {{id}} е ажурирана од {{author}}." + text_wiki_destroy_confirmation: Дали сте сигурни дека сакате да го избришете ова вики и целата негова содржина? + text_issue_category_destroy_question: "Некои задачи ({{count}}) се доделени на оваа категорија. Што сакате да правите?" + text_issue_category_destroy_assignments: Remove category assignments + text_issue_category_reassign_to: Додели ги задачите на оваа категорија + text_user_mail_option: "For unselected projects, you will only receive notifications about things you watch or you're involved in (eg. issues you're the author or assignee)." + text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." + text_load_default_configuration: Load the default configuration + text_status_changed_by_changeset: "Applied in changeset {{value}}." + text_issues_destroy_confirmation: 'Дали сте сигурни дека сакате да ги избришете избраните задачи?' + text_select_project_modules: 'Изберете модули за овој проект:' + text_default_administrator_account_changed: Default administrator account changed + text_file_repository_writable: Во папката за прилози може да се запишува + text_plugin_assets_writable: Во папката за додатоци може да се запишува + text_rmagick_available: RMagick available (незадолжително) + text_destroy_time_entries_question: "{{hours}} hours were reported on the issues you are about to delete. What do you want to do ?" + text_destroy_time_entries: Delete reported hours + text_assign_time_entries_to_project: Додели ги пријавените часови на проектот + text_reassign_time_entries: 'Reassign reported hours to this issue:' + text_user_wrote: "{{value}} напиша:" + text_enumeration_destroy_question: "{{count}} objects are assigned to this value." + text_enumeration_category_reassign_to: 'Reassign them to this value:' + text_email_delivery_not_configured: "Доставата по е-пошта не е конфигурирана, и известувањата се оневозможени.\nКонфигурирајте го Вашиот SMTP сервер во config/email.yml и рестартирајте ја апликацијата." + text_repository_usernames_mapping: "Select or update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped." + text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.' + text_custom_field_possible_values_info: 'One line for each value' + text_wiki_page_destroy_question: "This page has {{descendants}} child page(s) and descendant(s). What do you want to do?" + text_wiki_page_nullify_children: "Keep child pages as root pages" + text_wiki_page_destroy_children: "Delete child pages and all their descendants" + text_wiki_page_reassign_children: "Reassign child pages to this parent page" + text_own_membership_delete_confirmation: "You are about to remove some or all of your permissions and may no longer be able to edit this project after that.\nAre you sure you want to continue?" + text_zoom_in: Zoom in + text_zoom_out: Zoom out + + default_role_manager: Менаџер + default_role_developer: Developer + default_role_reporter: Reporter + default_tracker_bug: Грешка + default_tracker_feature: Функционалност + default_tracker_support: Поддршка + default_issue_status_new: Нова + default_issue_status_in_progress: Во прогрес + default_issue_status_resolved: Разрешена + default_issue_status_feedback: Feedback + default_issue_status_closed: Затворена + default_issue_status_rejected: Одбиена + default_doc_category_user: Корисничка документација + default_doc_category_tech: Техничка документација + default_priority_low: Низок + default_priority_normal: Нормален + default_priority_high: Висок + default_priority_urgent: Итно + default_priority_immediate: Веднаш + default_activity_design: Дизајн + default_activity_development: Развој + + enumeration_issue_priorities: Приоритети на задача + enumeration_doc_categories: Категории на документ + enumeration_activities: Активности (следење на време) + enumeration_system_activity: Системска активност + diff --git a/config/locales/sv.yml b/config/locales/sv.yml index d6a9454fb..e02ee7867 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -325,6 +325,7 @@ sv: field_redirect_existing_links: Omdirigera existerande länkar field_estimated_hours: Estimerad tid field_column_names: Kolumner + field_time_entries: Spenderad tid field_time_zone: Tidszon field_searchable: Sökbar field_default_value: Standardvärde @@ -456,6 +457,8 @@ sv: project_module_wiki: Wiki project_module_repository: Versionsarkiv project_module_boards: Forum + project_module_calendar: Kalender + project_module_gantt: Gantt label_user: Användare label_user_plural: Användare @@ -857,7 +860,7 @@ sv: button_update: Uppdatera button_configure: Konfigurera button_quote: Citera - button_duplicate: Duplisera + button_duplicate: Duplicera button_show: Visa status_active: aktiv @@ -954,6 +957,3 @@ sv: enumeration_doc_categories: Dokumentkategorier enumeration_activities: Aktiviteter (tidsuppföljning) enumeration_system_activity: Systemaktivitet - field_time_entries: Log time - project_module_gantt: Gantt - project_module_calendar: Calendar diff --git a/public/javascripts/calendar/lang/calendar-mk.js b/public/javascripts/calendar/lang/calendar-mk.js index 34fcaaee7..863e3bf2b 100644 --- a/public/javascripts/calendar/lang/calendar-mk.js +++ b/public/javascripts/calendar/lang/calendar-mk.js @@ -1,7 +1,7 @@ // ** I18N // Calendar МК language -// Author: Илин Татабитовски, +// Author: Ilin Tatabitovski, // Encoding: UTF-8 // Distributed under the same terms as the calendar itself. @@ -84,26 +84,26 @@ Calendar._TT["INFO"] = "За календарот"; Calendar._TT["ABOUT"] = "DHTML Date/Time Selector\n" + "(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) -"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + -"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"За последна верзија посети: http://www.dynarch.com/projects/calendar/\n" + +"Дистрибуирано под GNU LGPL. Види http://gnu.org/licenses/lgpl.html за детали." + "\n\n" + -"Date selection:\n" + -"- Use the \xab, \xbb buttons to select year\n" + -"- Use the " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " buttons to select month\n" + -"- Hold mouse button on any of the above buttons for faster selection."; +"Бирање на дата:\n" + +"- Користи ги \xab, \xbb копчињата за да избереш година\n" + +"- Користи ги " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " копчињата за да избере месеци\n" + +"- Држи го притиснато копчето на глувчето на било кое копче за побрзо бирање."; Calendar._TT["ABOUT_TIME"] = "\n\n" + -"Time selection:\n" + -"- Click on any of the time parts to increase it\n" + -"- or Shift-click to decrease it\n" + -"- or click and drag for faster selection."; +"Бирање на време:\n" + +"- Клик на временските делови за да го зголемиш\n" + +"- или Shift-клик да го намалиш\n" + +"- или клик и влечи за побрзо бирање."; -Calendar._TT["PREV_YEAR"] = "Претходна година (hold for menu)"; -Calendar._TT["PREV_MONTH"] = "Претходен месец (hold for menu)"; +Calendar._TT["PREV_YEAR"] = "Претходна година (држи за мени)"; +Calendar._TT["PREV_MONTH"] = "Претходен месец (држи за мени)"; Calendar._TT["GO_TODAY"] = "Go Today"; -Calendar._TT["NEXT_MONTH"] = "Следен месец (hold for menu)"; -Calendar._TT["NEXT_YEAR"] = "Следна година (hold for menu)"; -Calendar._TT["SEL_DATE"] = "Изберете дата"; -Calendar._TT["DRAG_TO_MOVE"] = "Drag to move"; +Calendar._TT["NEXT_MONTH"] = "Следен месец (држи за мени)"; +Calendar._TT["NEXT_YEAR"] = "Следна година (држи за мени)"; +Calendar._TT["SEL_DATE"] = "Избери дата"; +Calendar._TT["DRAG_TO_MOVE"] = "Влечи да поместиш"; Calendar._TT["PART_TODAY"] = " (денес)"; // the following is to inform that "%s" is to be the first day of week @@ -117,7 +117,7 @@ Calendar._TT["WEEKEND"] = "0,6"; Calendar._TT["CLOSE"] = "Затвори"; Calendar._TT["TODAY"] = "Денес"; -Calendar._TT["TIME_PART"] = "(Shift-)Click or drag to change value"; +Calendar._TT["TIME_PART"] = "(Shift-)Клик или влечи за да промениш вредност"; // date formats Calendar._TT["DEF_DATE_FORMAT"] = "%d-%m-%Y"; @@ -125,3 +125,4 @@ Calendar._TT["TT_DATE_FORMAT"] = "%a, %e %b"; Calendar._TT["WK"] = "нед"; Calendar._TT["TIME"] = "Време:"; + diff --git a/public/javascripts/jstoolbar/lang/jstoolbar-mk.js b/public/javascripts/jstoolbar/lang/jstoolbar-mk.js index 3af63a1cc..30c68ec6c 100644 --- a/public/javascripts/jstoolbar/lang/jstoolbar-mk.js +++ b/public/javascripts/jstoolbar/lang/jstoolbar-mk.js @@ -1,16 +1,17 @@ jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Strong'; -jsToolBar.strings['Italic'] = 'Italic'; -jsToolBar.strings['Underline'] = 'Underline'; -jsToolBar.strings['Deleted'] = 'Deleted'; -jsToolBar.strings['Code'] = 'Inline Code'; -jsToolBar.strings['Heading 1'] = 'Heading 1'; -jsToolBar.strings['Heading 2'] = 'Heading 2'; -jsToolBar.strings['Heading 3'] = 'Heading 3'; -jsToolBar.strings['Unordered list'] = 'Unordered list'; +jsToolBar.strings['Strong'] = 'Задебелен'; +jsToolBar.strings['Italic'] = 'Закосен'; +jsToolBar.strings['Underline'] = 'Подвлечен'; +jsToolBar.strings['Deleted'] = 'Прецртан'; +jsToolBar.strings['Code'] = 'Код'; +jsToolBar.strings['Heading 1'] = 'Заглавје 1'; +jsToolBar.strings['Heading 2'] = 'Заглавје 2'; +jsToolBar.strings['Heading 3'] = 'Заглавје 3'; +jsToolBar.strings['Unordered list'] = 'Неподредена листа'; jsToolBar.strings['Ordered list'] = 'Подредена листа'; jsToolBar.strings['Quote'] = 'Цитат'; jsToolBar.strings['Unquote'] = 'Отстрани цитат'; -jsToolBar.strings['Preformatted text'] = 'Preformatted text'; -jsToolBar.strings['Wiki link'] = 'Линк до вики страна'; +jsToolBar.strings['Preformatted text'] = 'Форматиран текст'; +jsToolBar.strings['Wiki link'] = 'Врска до вики страна'; jsToolBar.strings['Image'] = 'Слика'; + From a188abbe2813372d426afd2ab05841f0503f00c1 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Mon, 30 Aug 2010 15:30:28 +0000 Subject: [PATCH 14/72] Refactor: move method, ProjectsController#roadmap to VersionsController#index. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4050 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/projects_controller.rb | 32 --------------- app/controllers/versions_controller.rb | 39 +++++++++++++++++-- .../roadmap.rhtml => versions/index.html.erb} | 0 config/routes.rb | 3 +- lib/redmine.rb | 7 ++-- test/functional/projects_controller_test.rb | 32 --------------- test/functional/versions_controller_test.rb | 33 ++++++++++++++++ test/integration/routing_test.rb | 2 +- 8 files changed, 75 insertions(+), 73 deletions(-) rename app/views/{projects/roadmap.rhtml => versions/index.html.erb} (100%) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 6bb766386..845ab3c15 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -287,30 +287,6 @@ class ProjectsController < ApplicationController render :layout => !request.xhr? end - def roadmap - @trackers = @project.trackers.find(:all, :order => 'position') - retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?}) - @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1') - project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id] - - @versions = @project.shared_versions || [] - @versions += @project.rolled_up_versions.visible if @with_subprojects - @versions = @versions.uniq.sort - @versions.reject! {|version| version.closed? || version.completed? } unless params[:completed] - - @issues_by_version = {} - unless @selected_tracker_ids.empty? - @versions.each do |version| - issues = version.fixed_issues.visible.find(:all, - :include => [:project, :status, :tracker, :priority], - :conditions => {:tracker_id => @selected_tracker_ids, :project_id => project_ids}, - :order => "#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id") - @issues_by_version[version] = issues - end - end - @versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?} - end - private def find_optional_project return true unless params[:id] @@ -320,14 +296,6 @@ private render_404 end - def retrieve_selected_tracker_ids(selectable_trackers, default_trackers=nil) - if ids = params[:tracker_ids] - @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s } - else - @selected_tracker_ids = (default_trackers || selectable_trackers).collect {|t| t.id.to_s } - end - end - # Validates parent_id param according to user's permissions # TODO: move it to Project model in a validation that depends on User.current def validate_parent_id diff --git a/app/controllers/versions_controller.rb b/app/controllers/versions_controller.rb index 46b4778d4..dd01da95b 100644 --- a/app/controllers/versions_controller.rb +++ b/app/controllers/versions_controller.rb @@ -18,13 +18,37 @@ class VersionsController < ApplicationController menu_item :roadmap model_object Version - before_filter :find_model_object, :except => [:new, :close_completed] - before_filter :find_project_from_association, :except => [:new, :close_completed] - before_filter :find_project, :only => [:new, :close_completed] + before_filter :find_model_object, :except => [:index, :new, :close_completed] + before_filter :find_project_from_association, :except => [:index, :new, :close_completed] + before_filter :find_project, :only => [:index, :new, :close_completed] before_filter :authorize helper :custom_fields helper :projects + + def index + @trackers = @project.trackers.find(:all, :order => 'position') + retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?}) + @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1') + project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id] + + @versions = @project.shared_versions || [] + @versions += @project.rolled_up_versions.visible if @with_subprojects + @versions = @versions.uniq.sort + @versions.reject! {|version| version.closed? || version.completed? } unless params[:completed] + + @issues_by_version = {} + unless @selected_tracker_ids.empty? + @versions.each do |version| + issues = version.fixed_issues.visible.find(:all, + :include => [:project, :status, :tracker, :priority], + :conditions => {:tracker_id => @selected_tracker_ids, :project_id => project_ids}, + :order => "#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id") + @issues_by_version[version] = issues + end + end + @versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?} + end def show @issues = @version.fixed_issues.visible.find(:all, @@ -105,4 +129,13 @@ private rescue ActiveRecord::RecordNotFound render_404 end + + def retrieve_selected_tracker_ids(selectable_trackers, default_trackers=nil) + if ids = params[:tracker_ids] + @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s } + else + @selected_tracker_ids = (default_trackers || selectable_trackers).collect {|t| t.id.to_s } + end + end + end diff --git a/app/views/projects/roadmap.rhtml b/app/views/versions/index.html.erb similarity index 100% rename from app/views/projects/roadmap.rhtml rename to app/views/versions/index.html.erb diff --git a/config/routes.rb b/config/routes.rb index 1465e1ffe..1414e1000 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -180,7 +180,7 @@ ActionController::Routing::Routes.draw do |map| project_views.connect 'projects/new', :action => 'add' project_views.connect 'projects/:id', :action => 'show' project_views.connect 'projects/:id.:format', :action => 'show' - project_views.connect 'projects/:id/:action', :action => /roadmap|destroy|settings/ + project_views.connect 'projects/:id/:action', :action => /destroy|settings/ project_views.connect 'projects/:id/files', :action => 'list_files' project_views.connect 'projects/:id/files/new', :action => 'add_file' project_views.connect 'projects/:id/settings/:tab', :action => 'settings' @@ -215,6 +215,7 @@ ActionController::Routing::Routes.draw do |map| map.with_options :controller => 'versions' do |versions| versions.connect 'projects/:project_id/versions/new', :action => 'new' + versions.connect 'projects/:project_id/roadmap', :action => 'index' versions.with_options :conditions => {:method => :post} do |version_actions| version_actions.connect 'projects/:project_id/versions/close_completed', :action => 'close_completed' end diff --git a/lib/redmine.rb b/lib/redmine.rb index 7040a1f99..ecfc46252 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -57,11 +57,10 @@ Redmine::AccessControl.map do |map| # Issue categories map.permission :manage_categories, {:projects => :settings, :issue_categories => [:new, :edit, :destroy]}, :require => :member # Issues - map.permission :view_issues, {:projects => :roadmap, - :issues => [:index, :show], + map.permission :view_issues, {:issues => [:index, :show], :auto_complete => [:issues], :context_menus => [:issues], - :versions => [:show, :status_by], + :versions => [:index, :show, :status_by], :journals => :index, :queries => :index, :reports => [:issue_report, :issue_report_details]} @@ -186,7 +185,7 @@ end Redmine::MenuManager.map :project_menu do |menu| menu.push :overview, { :controller => 'projects', :action => 'show' } menu.push :activity, { :controller => 'activities', :action => 'index' } - menu.push :roadmap, { :controller => 'projects', :action => 'roadmap' }, + menu.push :roadmap, { :controller => 'versions', :action => 'index' }, :param => :project_id, :if => Proc.new { |p| p.shared_versions.any? } menu.push :issues, { :controller => 'issues', :action => 'index' }, :param => :project_id, :caption => :label_issue_plural menu.push :new_issue, { :controller => 'issues', :action => 'new' }, :param => :project_id, :caption => :label_issue_new, diff --git a/test/functional/projects_controller_test.rb b/test/functional/projects_controller_test.rb index f9c8ce022..36d60c859 100644 --- a/test/functional/projects_controller_test.rb +++ b/test/functional/projects_controller_test.rb @@ -368,38 +368,6 @@ class ProjectsControllerTest < ActionController::TestCase :attributes => { :href => '/attachments/download/9/version_file.zip' } end - def test_roadmap - get :roadmap, :id => 1 - assert_response :success - assert_template 'roadmap' - assert_not_nil assigns(:versions) - # Version with no date set appears - assert assigns(:versions).include?(Version.find(3)) - # Completed version doesn't appear - assert !assigns(:versions).include?(Version.find(1)) - end - - def test_roadmap_with_completed_versions - get :roadmap, :id => 1, :completed => 1 - assert_response :success - assert_template 'roadmap' - assert_not_nil assigns(:versions) - # Version with no date set appears - assert assigns(:versions).include?(Version.find(3)) - # Completed version appears - assert assigns(:versions).include?(Version.find(1)) - end - - def test_roadmap_showing_subprojects_versions - @subproject_version = Version.generate!(:project => Project.find(3)) - get :roadmap, :id => 1, :with_subprojects => 1 - assert_response :success - assert_template 'roadmap' - assert_not_nil assigns(:versions) - - assert assigns(:versions).include?(Version.find(4)), "Shared version not found" - assert assigns(:versions).include?(@subproject_version), "Subproject version not found" - end def test_archive @request.session[:user_id] = 1 # admin post :archive, :id => 1 diff --git a/test/functional/versions_controller_test.rb b/test/functional/versions_controller_test.rb index e4864c908..5fcb1d05a 100644 --- a/test/functional/versions_controller_test.rb +++ b/test/functional/versions_controller_test.rb @@ -31,6 +31,39 @@ class VersionsControllerTest < ActionController::TestCase User.current = nil end + def test_index + get :index, :project_id => 1 + assert_response :success + assert_template 'index' + assert_not_nil assigns(:versions) + # Version with no date set appears + assert assigns(:versions).include?(Version.find(3)) + # Completed version doesn't appear + assert !assigns(:versions).include?(Version.find(1)) + end + + def test_index_with_completed_versions + get :index, :project_id => 1, :completed => 1 + assert_response :success + assert_template 'index' + assert_not_nil assigns(:versions) + # Version with no date set appears + assert assigns(:versions).include?(Version.find(3)) + # Completed version appears + assert assigns(:versions).include?(Version.find(1)) + end + + def test_index_showing_subprojects_versions + @subproject_version = Version.generate!(:project => Project.find(3)) + get :index, :project_id => 1, :with_subprojects => 1 + assert_response :success + assert_template 'index' + assert_not_nil assigns(:versions) + + assert assigns(:versions).include?(Version.find(4)), "Shared version not found" + assert assigns(:versions).include?(@subproject_version), "Subproject version not found" + end + def test_show get :show, :id => 2 assert_response :success diff --git a/test/integration/routing_test.rb b/test/integration/routing_test.rb index 62fdaf47a..590bd9119 100644 --- a/test/integration/routing_test.rb +++ b/test/integration/routing_test.rb @@ -174,7 +174,7 @@ class RoutingTest < ActionController::IntegrationTest should_route :get, "/projects/567/destroy", :controller => 'projects', :action => 'destroy', :id => '567' should_route :get, "/projects/33/files", :controller => 'projects', :action => 'list_files', :id => '33' should_route :get, "/projects/33/files/new", :controller => 'projects', :action => 'add_file', :id => '33' - should_route :get, "/projects/33/roadmap", :controller => 'projects', :action => 'roadmap', :id => '33' + should_route :get, "/projects/33/roadmap", :controller => 'versions', :action => 'index', :project_id => '33' should_route :get, "/projects/33/activity", :controller => 'activities', :action => 'index', :id => '33' should_route :get, "/projects/33/activity.atom", :controller => 'activities', :action => 'index', :id => '33', :format => 'atom' From daa8eaa9aebcc284e2904d1258cbfdd78ea23876 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Tue, 31 Aug 2010 15:12:58 +0000 Subject: [PATCH 15/72] Refactor: move method, ProjectsController#list_files to FilesController#index. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4051 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/files_controller.rb | 22 ++++++++++++++ app/controllers/projects_controller.rb | 16 ++-------- .../list_files.rhtml => files/index.html.erb} | 0 config/routes.rb | 2 +- lib/redmine.rb | 4 +-- test/functional/files_controller_test.rb | 29 +++++++++++++++++++ test/functional/projects_controller_test.rb | 15 ---------- test/integration/routing_test.rb | 2 +- 8 files changed, 57 insertions(+), 33 deletions(-) create mode 100644 app/controllers/files_controller.rb rename app/views/{projects/list_files.rhtml => files/index.html.erb} (100%) create mode 100644 test/functional/files_controller_test.rb diff --git a/app/controllers/files_controller.rb b/app/controllers/files_controller.rb new file mode 100644 index 000000000..c84ce5f51 --- /dev/null +++ b/app/controllers/files_controller.rb @@ -0,0 +1,22 @@ +class FilesController < ApplicationController + menu_item :files + + before_filter :find_project + before_filter :authorize + + helper :sort + include SortHelper + + def index + sort_init 'filename', 'asc' + sort_update 'filename' => "#{Attachment.table_name}.filename", + 'created_on' => "#{Attachment.table_name}.created_on", + 'size' => "#{Attachment.table_name}.filesize", + 'downloads' => "#{Attachment.table_name}.downloads" + + @containers = [ Project.find(@project.id, :include => :attachments, :order => sort_clause)] + @containers += @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse + render :layout => !request.xhr? + end + +end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 845ab3c15..05c8d969d 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -18,7 +18,7 @@ class ProjectsController < ApplicationController menu_item :overview menu_item :roadmap, :only => :roadmap - menu_item :files, :only => [:list_files, :add_file] + menu_item :files, :only => [:add_file] menu_item :settings, :only => :settings before_filter :find_project, :except => [ :index, :list, :add, :copy ] @@ -248,7 +248,7 @@ class ProjectsController < ApplicationController if !attachments.empty? && Setting.notified_events.include?('file_added') Mailer.deliver_attachments_added(attachments[:files]) end - redirect_to :controller => 'projects', :action => 'list_files', :id => @project + redirect_to :controller => 'files', :action => 'index', :id => @project return end @versions = @project.versions.sort @@ -275,18 +275,6 @@ class ProjectsController < ApplicationController redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project end - def list_files - sort_init 'filename', 'asc' - sort_update 'filename' => "#{Attachment.table_name}.filename", - 'created_on' => "#{Attachment.table_name}.created_on", - 'size' => "#{Attachment.table_name}.filesize", - 'downloads' => "#{Attachment.table_name}.downloads" - - @containers = [ Project.find(@project.id, :include => :attachments, :order => sort_clause)] - @containers += @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse - render :layout => !request.xhr? - end - private def find_optional_project return true unless params[:id] diff --git a/app/views/projects/list_files.rhtml b/app/views/files/index.html.erb similarity index 100% rename from app/views/projects/list_files.rhtml rename to app/views/files/index.html.erb diff --git a/config/routes.rb b/config/routes.rb index 1414e1000..270bab4a0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -181,7 +181,7 @@ ActionController::Routing::Routes.draw do |map| project_views.connect 'projects/:id', :action => 'show' project_views.connect 'projects/:id.:format', :action => 'show' project_views.connect 'projects/:id/:action', :action => /destroy|settings/ - project_views.connect 'projects/:id/files', :action => 'list_files' + project_views.connect 'projects/:id/files', :controller => 'files', :action => 'index' project_views.connect 'projects/:id/files/new', :action => 'add_file' project_views.connect 'projects/:id/settings/:tab', :action => 'settings' project_views.connect 'projects/:project_id/issues/:copy_from/copy', :controller => 'issues', :action => 'new' diff --git a/lib/redmine.rb b/lib/redmine.rb index ecfc46252..726a2ad92 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -103,7 +103,7 @@ Redmine::AccessControl.map do |map| map.project_module :files do |map| map.permission :manage_files, {:projects => :add_file}, :require => :loggedin - map.permission :view_files, :projects => :list_files, :versions => :download + map.permission :view_files, :files => :index, :versions => :download end map.project_module :wiki do |map| @@ -198,7 +198,7 @@ Redmine::MenuManager.map :project_menu do |menu| :if => Proc.new { |p| p.wiki && !p.wiki.new_record? } menu.push :boards, { :controller => 'boards', :action => 'index', :id => nil }, :param => :project_id, :if => Proc.new { |p| p.boards.any? }, :caption => :label_board_plural - menu.push :files, { :controller => 'projects', :action => 'list_files' }, :caption => :label_file_plural + menu.push :files, { :controller => 'files', :action => 'index' }, :caption => :label_file_plural menu.push :repository, { :controller => 'repositories', :action => 'show' }, :if => Proc.new { |p| p.repository && !p.repository.new_record? } menu.push :settings, { :controller => 'projects', :action => 'settings' }, :last => true diff --git a/test/functional/files_controller_test.rb b/test/functional/files_controller_test.rb new file mode 100644 index 000000000..908502183 --- /dev/null +++ b/test/functional/files_controller_test.rb @@ -0,0 +1,29 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class FilesControllerTest < ActionController::TestCase + fixtures :all + + def setup + @controller = FilesController.new + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + @request.session[:user_id] = nil + Setting.default_language = 'en' + end + + def test_index + get :index, :id => 1 + assert_response :success + assert_template 'index' + assert_not_nil assigns(:containers) + + # file attached to the project + assert_tag :a, :content => 'project_file.zip', + :attributes => { :href => '/attachments/download/8/project_file.zip' } + + # file attached to a project's version + assert_tag :a, :content => 'version_file.zip', + :attributes => { :href => '/attachments/download/9/version_file.zip' } + end + +end diff --git a/test/functional/projects_controller_test.rb b/test/functional/projects_controller_test.rb index 36d60c859..b22efd6ab 100644 --- a/test/functional/projects_controller_test.rb +++ b/test/functional/projects_controller_test.rb @@ -353,21 +353,6 @@ class ProjectsControllerTest < ActionController::TestCase assert_equal Version.find(2), a.container end - def test_list_files - get :list_files, :id => 1 - assert_response :success - assert_template 'list_files' - assert_not_nil assigns(:containers) - - # file attached to the project - assert_tag :a, :content => 'project_file.zip', - :attributes => { :href => '/attachments/download/8/project_file.zip' } - - # file attached to a project's version - assert_tag :a, :content => 'version_file.zip', - :attributes => { :href => '/attachments/download/9/version_file.zip' } - end - def test_archive @request.session[:user_id] = 1 # admin post :archive, :id => 1 diff --git a/test/integration/routing_test.rb b/test/integration/routing_test.rb index 590bd9119..cc1f451cf 100644 --- a/test/integration/routing_test.rb +++ b/test/integration/routing_test.rb @@ -172,7 +172,7 @@ class RoutingTest < ActionController::IntegrationTest should_route :get, "/projects/4223/settings", :controller => 'projects', :action => 'settings', :id => '4223' should_route :get, "/projects/4223/settings/members", :controller => 'projects', :action => 'settings', :id => '4223', :tab => 'members' should_route :get, "/projects/567/destroy", :controller => 'projects', :action => 'destroy', :id => '567' - should_route :get, "/projects/33/files", :controller => 'projects', :action => 'list_files', :id => '33' + should_route :get, "/projects/33/files", :controller => 'files', :action => 'index', :id => '33' should_route :get, "/projects/33/files/new", :controller => 'projects', :action => 'add_file', :id => '33' should_route :get, "/projects/33/roadmap", :controller => 'versions', :action => 'index', :project_id => '33' should_route :get, "/projects/33/activity", :controller => 'activities', :action => 'index', :id => '33' From b5e90972d88c69a1ef2c9e90879e8926d192acff Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Wed, 1 Sep 2010 15:17:45 +0000 Subject: [PATCH 16/72] Refactor: move method, ProjectsController#add_file to FilesController#new. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4052 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/files_controller.rb | 15 ++++++++ app/controllers/projects_controller.rb | 16 -------- app/views/files/index.html.erb | 2 +- .../add_file.rhtml => files/new.html.erb} | 2 +- config/routes.rb | 4 +- lib/redmine.rb | 2 +- test/functional/files_controller_test.rb | 38 +++++++++++++++++++ test/functional/projects_controller_test.rb | 36 ------------------ test/integration/routing_test.rb | 4 +- 9 files changed, 60 insertions(+), 59 deletions(-) rename app/views/{projects/add_file.rhtml => files/new.html.erb} (82%) diff --git a/app/controllers/files_controller.rb b/app/controllers/files_controller.rb index c84ce5f51..fe5eb48c8 100644 --- a/app/controllers/files_controller.rb +++ b/app/controllers/files_controller.rb @@ -19,4 +19,19 @@ class FilesController < ApplicationController render :layout => !request.xhr? end + # TODO: split method into new (GET) and create (POST) + def new + if request.post? + container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id])) + attachments = Attachment.attach_files(container, params[:attachments]) + render_attachment_warning_if_needed(container) + + if !attachments.empty? && Setting.notified_events.include?('file_added') + Mailer.deliver_attachments_added(attachments[:files]) + end + redirect_to :controller => 'files', :action => 'index', :id => @project + return + end + @versions = @project.versions.sort + end end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 05c8d969d..61c7a7c71 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -18,7 +18,6 @@ class ProjectsController < ApplicationController menu_item :overview menu_item :roadmap, :only => :roadmap - menu_item :files, :only => [:add_file] menu_item :settings, :only => :settings before_filter :find_project, :except => [ :index, :list, :add, :copy ] @@ -239,21 +238,6 @@ class ProjectsController < ApplicationController @project = nil end - def add_file - if request.post? - container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id])) - attachments = Attachment.attach_files(container, params[:attachments]) - render_attachment_warning_if_needed(container) - - if !attachments.empty? && Setting.notified_events.include?('file_added') - Mailer.deliver_attachments_added(attachments[:files]) - end - redirect_to :controller => 'files', :action => 'index', :id => @project - return - end - @versions = @project.versions.sort - end - def save_activities if request.post? && params[:enumerations] Project.transaction do diff --git a/app/views/files/index.html.erb b/app/views/files/index.html.erb index 2b2e5e870..66f5d12b9 100644 --- a/app/views/files/index.html.erb +++ b/app/views/files/index.html.erb @@ -1,5 +1,5 @@
-<%= link_to_if_authorized l(:label_attachment_new), {:controller => 'projects', :action => 'add_file', :id => @project}, :class => 'icon icon-add' %> +<%= link_to_if_authorized l(:label_attachment_new), {:controller => 'files', :action => 'new', :id => @project}, :class => 'icon icon-add' %>

<%=l(:label_attachment_plural)%>

diff --git a/app/views/projects/add_file.rhtml b/app/views/files/new.html.erb similarity index 82% rename from app/views/projects/add_file.rhtml rename to app/views/files/new.html.erb index ab9c7352d..bbb3b1733 100644 --- a/app/views/projects/add_file.rhtml +++ b/app/views/files/new.html.erb @@ -2,7 +2,7 @@ <%= error_messages_for 'attachment' %>
-<% form_tag({ :action => 'add_file', :id => @project }, :multipart => true, :class => "tabular") do %> +<% form_tag({ :action => 'new', :id => @project }, :multipart => true, :class => "tabular") do %> <% if @versions.any? %>

diff --git a/config/routes.rb b/config/routes.rb index 270bab4a0..9c8a2f6ae 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -182,7 +182,7 @@ ActionController::Routing::Routes.draw do |map| project_views.connect 'projects/:id.:format', :action => 'show' project_views.connect 'projects/:id/:action', :action => /destroy|settings/ project_views.connect 'projects/:id/files', :controller => 'files', :action => 'index' - project_views.connect 'projects/:id/files/new', :action => 'add_file' + project_views.connect 'projects/:id/files/new', :controller => 'files', :action => 'new' project_views.connect 'projects/:id/settings/:tab', :action => 'settings' project_views.connect 'projects/:project_id/issues/:copy_from/copy', :controller => 'issues', :action => 'new' end @@ -199,7 +199,7 @@ ActionController::Routing::Routes.draw do |map| project_actions.connect 'projects', :action => 'add' project_actions.connect 'projects.:format', :action => 'add', :format => /xml/ project_actions.connect 'projects/:id/:action', :action => /edit|destroy|archive|unarchive/ - project_actions.connect 'projects/:id/files/new', :action => 'add_file' + project_actions.connect 'projects/:id/files/new', :controller => 'files', :action => 'new' project_actions.connect 'projects/:id/activities/save', :action => 'save_activities' end diff --git a/lib/redmine.rb b/lib/redmine.rb index 726a2ad92..745c6d750 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -102,7 +102,7 @@ Redmine::AccessControl.map do |map| end map.project_module :files do |map| - map.permission :manage_files, {:projects => :add_file}, :require => :loggedin + map.permission :manage_files, {:files => :new}, :require => :loggedin map.permission :view_files, :files => :index, :versions => :download end diff --git a/test/functional/files_controller_test.rb b/test/functional/files_controller_test.rb index 908502183..838caee70 100644 --- a/test/functional/files_controller_test.rb +++ b/test/functional/files_controller_test.rb @@ -26,4 +26,42 @@ class FilesControllerTest < ActionController::TestCase :attributes => { :href => '/attachments/download/9/version_file.zip' } end + def test_add_file + set_tmp_attachments_directory + @request.session[:user_id] = 2 + Setting.notified_events = ['file_added'] + ActionMailer::Base.deliveries.clear + + assert_difference 'Attachment.count' do + post :new, :id => 1, :version_id => '', + :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}} + assert_response :redirect + end + assert_redirected_to 'projects/ecookbook/files' + a = Attachment.find(:first, :order => 'created_on DESC') + assert_equal 'testfile.txt', a.filename + assert_equal Project.find(1), a.container + + mail = ActionMailer::Base.deliveries.last + assert_kind_of TMail::Mail, mail + assert_equal "[eCookbook] New file", mail.subject + assert mail.body.include?('testfile.txt') + end + + def test_add_version_file + set_tmp_attachments_directory + @request.session[:user_id] = 2 + Setting.notified_events = ['file_added'] + + assert_difference 'Attachment.count' do + post :new, :id => 1, :version_id => '2', + :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}} + assert_response :redirect + end + assert_redirected_to 'projects/ecookbook/files' + a = Attachment.find(:first, :order => 'created_on DESC') + assert_equal 'testfile.txt', a.filename + assert_equal Version.find(2), a.container + end + end diff --git a/test/functional/projects_controller_test.rb b/test/functional/projects_controller_test.rb index b22efd6ab..9d1582af0 100644 --- a/test/functional/projects_controller_test.rb +++ b/test/functional/projects_controller_test.rb @@ -317,42 +317,6 @@ class ProjectsControllerTest < ActionController::TestCase assert_nil Project.find_by_id(1) end - def test_add_file - set_tmp_attachments_directory - @request.session[:user_id] = 2 - Setting.notified_events = ['file_added'] - ActionMailer::Base.deliveries.clear - - assert_difference 'Attachment.count' do - post :add_file, :id => 1, :version_id => '', - :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}} - end - assert_redirected_to 'projects/ecookbook/files' - a = Attachment.find(:first, :order => 'created_on DESC') - assert_equal 'testfile.txt', a.filename - assert_equal Project.find(1), a.container - - mail = ActionMailer::Base.deliveries.last - assert_kind_of TMail::Mail, mail - assert_equal "[eCookbook] New file", mail.subject - assert mail.body.include?('testfile.txt') - end - - def test_add_version_file - set_tmp_attachments_directory - @request.session[:user_id] = 2 - Setting.notified_events = ['file_added'] - - assert_difference 'Attachment.count' do - post :add_file, :id => 1, :version_id => '2', - :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}} - end - assert_redirected_to 'projects/ecookbook/files' - a = Attachment.find(:first, :order => 'created_on DESC') - assert_equal 'testfile.txt', a.filename - assert_equal Version.find(2), a.container - end - def test_archive @request.session[:user_id] = 1 # admin post :archive, :id => 1 diff --git a/test/integration/routing_test.rb b/test/integration/routing_test.rb index cc1f451cf..66836c4f9 100644 --- a/test/integration/routing_test.rb +++ b/test/integration/routing_test.rb @@ -173,7 +173,7 @@ class RoutingTest < ActionController::IntegrationTest should_route :get, "/projects/4223/settings/members", :controller => 'projects', :action => 'settings', :id => '4223', :tab => 'members' should_route :get, "/projects/567/destroy", :controller => 'projects', :action => 'destroy', :id => '567' should_route :get, "/projects/33/files", :controller => 'files', :action => 'index', :id => '33' - should_route :get, "/projects/33/files/new", :controller => 'projects', :action => 'add_file', :id => '33' + should_route :get, "/projects/33/files/new", :controller => 'files', :action => 'new', :id => '33' should_route :get, "/projects/33/roadmap", :controller => 'versions', :action => 'index', :project_id => '33' should_route :get, "/projects/33/activity", :controller => 'activities', :action => 'index', :id => '33' should_route :get, "/projects/33/activity.atom", :controller => 'activities', :action => 'index', :id => '33', :format => 'atom' @@ -182,7 +182,7 @@ class RoutingTest < ActionController::IntegrationTest should_route :post, "/projects.xml", :controller => 'projects', :action => 'add', :format => 'xml' should_route :post, "/projects/4223/edit", :controller => 'projects', :action => 'edit', :id => '4223' should_route :post, "/projects/64/destroy", :controller => 'projects', :action => 'destroy', :id => '64' - should_route :post, "/projects/33/files/new", :controller => 'projects', :action => 'add_file', :id => '33' + should_route :post, "/projects/33/files/new", :controller => 'files', :action => 'new', :id => '33' should_route :post, "/projects/64/archive", :controller => 'projects', :action => 'archive', :id => '64' should_route :post, "/projects/64/unarchive", :controller => 'projects', :action => 'unarchive', :id => '64' should_route :post, "/projects/64/activities/save", :controller => 'projects', :action => 'save_activities', :id => '64' From 83b4343d2d403a21de4614016e165091e4c5914f Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Thu, 2 Sep 2010 17:39:56 +0000 Subject: [PATCH 17/72] Refactor: move method, ProjectsController#save_activities to ProjectEnumerationsController#save git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4053 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- .../project_enumerations_controller.rb | 18 +++ app/controllers/projects_controller.rb | 13 -- app/views/projects/settings/_activities.rhtml | 2 +- config/routes.rb | 2 +- lib/redmine.rb | 2 +- .../project_enumerations_controller_test.rb | 142 ++++++++++++++++++ test/functional/projects_controller_test.rb | 132 ---------------- test/integration/routing_test.rb | 2 +- 8 files changed, 164 insertions(+), 149 deletions(-) create mode 100644 app/controllers/project_enumerations_controller.rb create mode 100644 test/functional/project_enumerations_controller_test.rb diff --git a/app/controllers/project_enumerations_controller.rb b/app/controllers/project_enumerations_controller.rb new file mode 100644 index 000000000..c4b48e476 --- /dev/null +++ b/app/controllers/project_enumerations_controller.rb @@ -0,0 +1,18 @@ +class ProjectEnumerationsController < ApplicationController + before_filter :find_project + before_filter :authorize + + def save + if request.post? && params[:enumerations] + Project.transaction do + params[:enumerations].each do |id, activity| + @project.update_or_create_time_entry_activity(id, activity) + end + end + flash[:notice] = l(:notice_successful_update) + end + + redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project + end + +end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 61c7a7c71..0670cb1ec 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -238,19 +238,6 @@ class ProjectsController < ApplicationController @project = nil end - def save_activities - if request.post? && params[:enumerations] - Project.transaction do - params[:enumerations].each do |id, activity| - @project.update_or_create_time_entry_activity(id, activity) - end - end - flash[:notice] = l(:notice_successful_update) - end - - redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project - end - def reset_activities @project.time_entry_activities.each do |time_entry_activity| time_entry_activity.destroy(time_entry_activity.parent) diff --git a/app/views/projects/settings/_activities.rhtml b/app/views/projects/settings/_activities.rhtml index d82bd96fb..e39bf9b91 100644 --- a/app/views/projects/settings/_activities.rhtml +++ b/app/views/projects/settings/_activities.rhtml @@ -1,4 +1,4 @@ -<% form_tag({:controller => 'projects', :action => 'save_activities', :id => @project}, :class => "tabular") do %> +<% form_tag({:controller => 'project_enumerations', :action => 'save', :id => @project}, :class => "tabular") do %> diff --git a/config/routes.rb b/config/routes.rb index 9c8a2f6ae..124115998 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -200,7 +200,7 @@ ActionController::Routing::Routes.draw do |map| project_actions.connect 'projects.:format', :action => 'add', :format => /xml/ project_actions.connect 'projects/:id/:action', :action => /edit|destroy|archive|unarchive/ project_actions.connect 'projects/:id/files/new', :controller => 'files', :action => 'new' - project_actions.connect 'projects/:id/activities/save', :action => 'save_activities' + project_actions.connect 'projects/:id/activities/save', :controller => 'project_enumerations', :action => 'save' end projects.with_options :conditions => {:method => :put} do |project_actions| diff --git a/lib/redmine.rb b/lib/redmine.rb index 745c6d750..fdd61225e 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -87,7 +87,7 @@ Redmine::AccessControl.map do |map| map.permission :view_time_entries, :timelog => [:details, :report] map.permission :edit_time_entries, {:timelog => [:edit, :destroy]}, :require => :member map.permission :edit_own_time_entries, {:timelog => [:edit, :destroy]}, :require => :loggedin - map.permission :manage_project_activities, {:projects => [:save_activities, :reset_activities]}, :require => :member + map.permission :manage_project_activities, {:projects => [:reset_activities], :project_enumerations => [:save]}, :require => :member end map.project_module :news do |map| diff --git a/test/functional/project_enumerations_controller_test.rb b/test/functional/project_enumerations_controller_test.rb new file mode 100644 index 000000000..23da42378 --- /dev/null +++ b/test/functional/project_enumerations_controller_test.rb @@ -0,0 +1,142 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class ProjectEnumerationsControllerTest < ActionController::TestCase + fixtures :all + + def setup + @request.session[:user_id] = nil + Setting.default_language = 'en' + end + + def test_save_to_override_system_activities + @request.session[:user_id] = 2 # manager + billable_field = TimeEntryActivityCustomField.find_by_name("Billable") + + post :save, :id => 1, :enumerations => { + "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # Design, De-activate + "10"=> {"parent_id"=>"10", "custom_field_values"=>{"7"=>"0"}, "active"=>"1"}, # Development, Change custom value + "14"=>{"parent_id"=>"14", "custom_field_values"=>{"7"=>"1"}, "active"=>"1"}, # Inactive Activity, Activate with custom value + "11"=>{"parent_id"=>"11", "custom_field_values"=>{"7"=>"1"}, "active"=>"1"} # QA, no changes + } + + assert_response :redirect + assert_redirected_to 'projects/ecookbook/settings/activities' + + # Created project specific activities... + project = Project.find('ecookbook') + + # ... Design + design = project.time_entry_activities.find_by_name("Design") + assert design, "Project activity not found" + + assert_equal 9, design.parent_id # Relate to the system activity + assert_not_equal design.parent.id, design.id # Different records + assert_equal design.parent.name, design.name # Same name + assert !design.active? + + # ... Development + development = project.time_entry_activities.find_by_name("Development") + assert development, "Project activity not found" + + assert_equal 10, development.parent_id # Relate to the system activity + assert_not_equal development.parent.id, development.id # Different records + assert_equal development.parent.name, development.name # Same name + assert development.active? + assert_equal "0", development.custom_value_for(billable_field).value + + # ... Inactive Activity + previously_inactive = project.time_entry_activities.find_by_name("Inactive Activity") + assert previously_inactive, "Project activity not found" + + assert_equal 14, previously_inactive.parent_id # Relate to the system activity + assert_not_equal previously_inactive.parent.id, previously_inactive.id # Different records + assert_equal previously_inactive.parent.name, previously_inactive.name # Same name + assert previously_inactive.active? + assert_equal "1", previously_inactive.custom_value_for(billable_field).value + + # ... QA + assert_equal nil, project.time_entry_activities.find_by_name("QA"), "Custom QA activity created when it wasn't modified" + end + + def test_save_will_update_project_specific_activities + @request.session[:user_id] = 2 # manager + + project_activity = TimeEntryActivity.new({ + :name => 'Project Specific', + :parent => TimeEntryActivity.find(:first), + :project => Project.find(1), + :active => true + }) + assert project_activity.save + project_activity_two = TimeEntryActivity.new({ + :name => 'Project Specific Two', + :parent => TimeEntryActivity.find(:last), + :project => Project.find(1), + :active => true + }) + assert project_activity_two.save + + + post :save, :id => 1, :enumerations => { + project_activity.id => {"custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # De-activate + project_activity_two.id => {"custom_field_values"=>{"7" => "1"}, "active"=>"0"} # De-activate + } + + assert_response :redirect + assert_redirected_to 'projects/ecookbook/settings/activities' + + # Created project specific activities... + project = Project.find('ecookbook') + assert_equal 2, project.time_entry_activities.count + + activity_one = project.time_entry_activities.find_by_name(project_activity.name) + assert activity_one, "Project activity not found" + assert_equal project_activity.id, activity_one.id + assert !activity_one.active? + + activity_two = project.time_entry_activities.find_by_name(project_activity_two.name) + assert activity_two, "Project activity not found" + assert_equal project_activity_two.id, activity_two.id + assert !activity_two.active? + end + + def test_save_when_creating_new_activities_will_convert_existing_data + assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size + + @request.session[:user_id] = 2 # manager + post :save, :id => 1, :enumerations => { + "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"} # Design, De-activate + } + assert_response :redirect + + # No more TimeEntries using the system activity + assert_equal 0, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size, "Time Entries still assigned to system activities" + # All TimeEntries using project activity + project_specific_activity = TimeEntryActivity.find_by_parent_id_and_project_id(9, 1) + assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(project_specific_activity.id, 1).size, "No Time Entries assigned to the project activity" + end + + def test_save_when_creating_new_activities_will_not_convert_existing_data_if_an_exception_is_raised + # TODO: Need to cause an exception on create but these tests + # aren't setup for mocking. Just create a record now so the + # second one is a dupicate + parent = TimeEntryActivity.find(9) + TimeEntryActivity.create!({:name => parent.name, :project_id => 1, :position => parent.position, :active => true}) + TimeEntry.create!({:project_id => 1, :hours => 1.0, :user => User.find(1), :issue_id => 3, :activity_id => 10, :spent_on => '2009-01-01'}) + + assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size + assert_equal 1, TimeEntry.find_all_by_activity_id_and_project_id(10, 1).size + + @request.session[:user_id] = 2 # manager + post :save, :id => 1, :enumerations => { + "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # Design + "10"=> {"parent_id"=>"10", "custom_field_values"=>{"7"=>"0"}, "active"=>"1"} # Development, Change custom value + } + assert_response :redirect + + # TimeEntries shouldn't have been reassigned on the failed record + assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size, "Time Entries are not assigned to system activities" + # TimeEntries shouldn't have been reassigned on the saved record either + assert_equal 1, TimeEntry.find_all_by_activity_id_and_project_id(10, 1).size, "Time Entries are not assigned to system activities" + end +end diff --git a/test/functional/projects_controller_test.rb b/test/functional/projects_controller_test.rb index 9d1582af0..152d0837b 100644 --- a/test/functional/projects_controller_test.rb +++ b/test/functional/projects_controller_test.rb @@ -427,138 +427,6 @@ class ProjectsControllerTest < ActionController::TestCase assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size, "TimeEntries still assigned to project specific activity" end - def test_save_activities_to_override_system_activities - @request.session[:user_id] = 2 # manager - billable_field = TimeEntryActivityCustomField.find_by_name("Billable") - - post :save_activities, :id => 1, :enumerations => { - "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # Design, De-activate - "10"=> {"parent_id"=>"10", "custom_field_values"=>{"7"=>"0"}, "active"=>"1"}, # Development, Change custom value - "14"=>{"parent_id"=>"14", "custom_field_values"=>{"7"=>"1"}, "active"=>"1"}, # Inactive Activity, Activate with custom value - "11"=>{"parent_id"=>"11", "custom_field_values"=>{"7"=>"1"}, "active"=>"1"} # QA, no changes - } - - assert_response :redirect - assert_redirected_to 'projects/ecookbook/settings/activities' - - # Created project specific activities... - project = Project.find('ecookbook') - - # ... Design - design = project.time_entry_activities.find_by_name("Design") - assert design, "Project activity not found" - - assert_equal 9, design.parent_id # Relate to the system activity - assert_not_equal design.parent.id, design.id # Different records - assert_equal design.parent.name, design.name # Same name - assert !design.active? - - # ... Development - development = project.time_entry_activities.find_by_name("Development") - assert development, "Project activity not found" - - assert_equal 10, development.parent_id # Relate to the system activity - assert_not_equal development.parent.id, development.id # Different records - assert_equal development.parent.name, development.name # Same name - assert development.active? - assert_equal "0", development.custom_value_for(billable_field).value - - # ... Inactive Activity - previously_inactive = project.time_entry_activities.find_by_name("Inactive Activity") - assert previously_inactive, "Project activity not found" - - assert_equal 14, previously_inactive.parent_id # Relate to the system activity - assert_not_equal previously_inactive.parent.id, previously_inactive.id # Different records - assert_equal previously_inactive.parent.name, previously_inactive.name # Same name - assert previously_inactive.active? - assert_equal "1", previously_inactive.custom_value_for(billable_field).value - - # ... QA - assert_equal nil, project.time_entry_activities.find_by_name("QA"), "Custom QA activity created when it wasn't modified" - end - - def test_save_activities_will_update_project_specific_activities - @request.session[:user_id] = 2 # manager - - project_activity = TimeEntryActivity.new({ - :name => 'Project Specific', - :parent => TimeEntryActivity.find(:first), - :project => Project.find(1), - :active => true - }) - assert project_activity.save - project_activity_two = TimeEntryActivity.new({ - :name => 'Project Specific Two', - :parent => TimeEntryActivity.find(:last), - :project => Project.find(1), - :active => true - }) - assert project_activity_two.save - - - post :save_activities, :id => 1, :enumerations => { - project_activity.id => {"custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # De-activate - project_activity_two.id => {"custom_field_values"=>{"7" => "1"}, "active"=>"0"} # De-activate - } - - assert_response :redirect - assert_redirected_to 'projects/ecookbook/settings/activities' - - # Created project specific activities... - project = Project.find('ecookbook') - assert_equal 2, project.time_entry_activities.count - - activity_one = project.time_entry_activities.find_by_name(project_activity.name) - assert activity_one, "Project activity not found" - assert_equal project_activity.id, activity_one.id - assert !activity_one.active? - - activity_two = project.time_entry_activities.find_by_name(project_activity_two.name) - assert activity_two, "Project activity not found" - assert_equal project_activity_two.id, activity_two.id - assert !activity_two.active? - end - - def test_save_activities_when_creating_new_activities_will_convert_existing_data - assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size - - @request.session[:user_id] = 2 # manager - post :save_activities, :id => 1, :enumerations => { - "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"} # Design, De-activate - } - assert_response :redirect - - # No more TimeEntries using the system activity - assert_equal 0, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size, "Time Entries still assigned to system activities" - # All TimeEntries using project activity - project_specific_activity = TimeEntryActivity.find_by_parent_id_and_project_id(9, 1) - assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(project_specific_activity.id, 1).size, "No Time Entries assigned to the project activity" - end - - def test_save_activities_when_creating_new_activities_will_not_convert_existing_data_if_an_exception_is_raised - # TODO: Need to cause an exception on create but these tests - # aren't setup for mocking. Just create a record now so the - # second one is a dupicate - parent = TimeEntryActivity.find(9) - TimeEntryActivity.create!({:name => parent.name, :project_id => 1, :position => parent.position, :active => true}) - TimeEntry.create!({:project_id => 1, :hours => 1.0, :user => User.find(1), :issue_id => 3, :activity_id => 10, :spent_on => '2009-01-01'}) - - assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size - assert_equal 1, TimeEntry.find_all_by_activity_id_and_project_id(10, 1).size - - @request.session[:user_id] = 2 # manager - post :save_activities, :id => 1, :enumerations => { - "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # Design - "10"=> {"parent_id"=>"10", "custom_field_values"=>{"7"=>"0"}, "active"=>"1"} # Development, Change custom value - } - assert_response :redirect - - # TimeEntries shouldn't have been reassigned on the failed record - assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size, "Time Entries are not assigned to system activities" - # TimeEntries shouldn't have been reassigned on the saved record either - assert_equal 1, TimeEntry.find_all_by_activity_id_and_project_id(10, 1).size, "Time Entries are not assigned to system activities" - end - # A hook that is manually registered later class ProjectBasedTemplate < Redmine::Hook::ViewListener def view_layouts_base_html_head(context) diff --git a/test/integration/routing_test.rb b/test/integration/routing_test.rb index 66836c4f9..74a0f7a71 100644 --- a/test/integration/routing_test.rb +++ b/test/integration/routing_test.rb @@ -185,7 +185,7 @@ class RoutingTest < ActionController::IntegrationTest should_route :post, "/projects/33/files/new", :controller => 'files', :action => 'new', :id => '33' should_route :post, "/projects/64/archive", :controller => 'projects', :action => 'archive', :id => '64' should_route :post, "/projects/64/unarchive", :controller => 'projects', :action => 'unarchive', :id => '64' - should_route :post, "/projects/64/activities/save", :controller => 'projects', :action => 'save_activities', :id => '64' + should_route :post, "/projects/64/activities/save", :controller => 'project_enumerations', :action => 'save', :id => '64' should_route :put, "/projects/1.xml", :controller => 'projects', :action => 'edit', :id => '1', :format => 'xml' From c1068bf0cd32df1edf9b1b69e76e30af84692b7c Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Fri, 3 Sep 2010 15:04:03 +0000 Subject: [PATCH 18/72] Refactor: move method, ProjectsController#reset_activities to ProjectEnumerationsController#destroy. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4054 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- .../project_enumerations_controller.rb | 8 ++++ app/controllers/projects_controller.rb | 8 ---- app/views/projects/settings/_activities.rhtml | 2 +- config/routes.rb | 2 +- lib/redmine.rb | 2 +- .../project_enumerations_controller_test.rb | 47 +++++++++++++++++++ test/functional/projects_controller_test.rb | 46 ------------------ test/integration/routing_test.rb | 2 +- 8 files changed, 59 insertions(+), 58 deletions(-) diff --git a/app/controllers/project_enumerations_controller.rb b/app/controllers/project_enumerations_controller.rb index c4b48e476..d63e23641 100644 --- a/app/controllers/project_enumerations_controller.rb +++ b/app/controllers/project_enumerations_controller.rb @@ -15,4 +15,12 @@ class ProjectEnumerationsController < ApplicationController redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project end + def destroy + @project.time_entry_activities.each do |time_entry_activity| + time_entry_activity.destroy(time_entry_activity.parent) + end + flash[:notice] = l(:notice_successful_update) + redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project + end + end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 0670cb1ec..90eddd7b2 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -238,14 +238,6 @@ class ProjectsController < ApplicationController @project = nil end - def reset_activities - @project.time_entry_activities.each do |time_entry_activity| - time_entry_activity.destroy(time_entry_activity.parent) - end - flash[:notice] = l(:notice_successful_update) - redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project - end - private def find_optional_project return true unless params[:id] diff --git a/app/views/projects/settings/_activities.rhtml b/app/views/projects/settings/_activities.rhtml index e39bf9b91..5909ac64c 100644 --- a/app/views/projects/settings/_activities.rhtml +++ b/app/views/projects/settings/_activities.rhtml @@ -32,7 +32,7 @@

-<%= link_to(l(:button_reset), {:controller => 'projects', :action => 'reset_activities', :id => @project}, +<%= link_to(l(:button_reset), {:controller => 'project_enumerations', :action => 'destroy', :id => @project}, :method => :delete, :confirm => l(:text_are_you_sure), :class => 'icon icon-del') %> diff --git a/config/routes.rb b/config/routes.rb index 124115998..5448b5f58 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -209,7 +209,7 @@ ActionController::Routing::Routes.draw do |map| projects.with_options :conditions => {:method => :delete} do |project_actions| project_actions.conditions 'projects/:id.:format', :action => 'destroy', :format => /xml/ - project_actions.conditions 'projects/:id/reset_activities', :action => 'reset_activities' + project_actions.conditions 'projects/:id/reset_activities', :controller => 'project_enumerations', :action => 'destroy' end end diff --git a/lib/redmine.rb b/lib/redmine.rb index fdd61225e..fc750d110 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -87,7 +87,7 @@ Redmine::AccessControl.map do |map| map.permission :view_time_entries, :timelog => [:details, :report] map.permission :edit_time_entries, {:timelog => [:edit, :destroy]}, :require => :member map.permission :edit_own_time_entries, {:timelog => [:edit, :destroy]}, :require => :loggedin - map.permission :manage_project_activities, {:projects => [:reset_activities], :project_enumerations => [:save]}, :require => :member + map.permission :manage_project_activities, {:project_enumerations => [:save, :destroy]}, :require => :member end map.project_module :news do |map| diff --git a/test/functional/project_enumerations_controller_test.rb b/test/functional/project_enumerations_controller_test.rb index 23da42378..c03be04fb 100644 --- a/test/functional/project_enumerations_controller_test.rb +++ b/test/functional/project_enumerations_controller_test.rb @@ -139,4 +139,51 @@ class ProjectEnumerationsControllerTest < ActionController::TestCase # TimeEntries shouldn't have been reassigned on the saved record either assert_equal 1, TimeEntry.find_all_by_activity_id_and_project_id(10, 1).size, "Time Entries are not assigned to system activities" end + + def test_destroy + @request.session[:user_id] = 2 # manager + project_activity = TimeEntryActivity.new({ + :name => 'Project Specific', + :parent => TimeEntryActivity.find(:first), + :project => Project.find(1), + :active => true + }) + assert project_activity.save + project_activity_two = TimeEntryActivity.new({ + :name => 'Project Specific Two', + :parent => TimeEntryActivity.find(:last), + :project => Project.find(1), + :active => true + }) + assert project_activity_two.save + + delete :destroy, :id => 1 + assert_response :redirect + assert_redirected_to 'projects/ecookbook/settings/activities' + + assert_nil TimeEntryActivity.find_by_id(project_activity.id) + assert_nil TimeEntryActivity.find_by_id(project_activity_two.id) + end + + def test_destroy_should_reassign_time_entries_back_to_the_system_activity + @request.session[:user_id] = 2 # manager + project_activity = TimeEntryActivity.new({ + :name => 'Project Specific Design', + :parent => TimeEntryActivity.find(9), + :project => Project.find(1), + :active => true + }) + assert project_activity.save + assert TimeEntry.update_all("activity_id = '#{project_activity.id}'", ["project_id = ? AND activity_id = ?", 1, 9]) + assert 3, TimeEntry.find_all_by_activity_id_and_project_id(project_activity.id, 1).size + + delete :destroy, :id => 1 + assert_response :redirect + assert_redirected_to 'projects/ecookbook/settings/activities' + + assert_nil TimeEntryActivity.find_by_id(project_activity.id) + assert_equal 0, TimeEntry.find_all_by_activity_id_and_project_id(project_activity.id, 1).size, "TimeEntries still assigned to project specific activity" + assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size, "TimeEntries still assigned to project specific activity" + end + end diff --git a/test/functional/projects_controller_test.rb b/test/functional/projects_controller_test.rb index 152d0837b..4decb060f 100644 --- a/test/functional/projects_controller_test.rb +++ b/test/functional/projects_controller_test.rb @@ -381,52 +381,6 @@ class ProjectsControllerTest < ActionController::TestCase assert_template 'show' end - def test_reset_activities - @request.session[:user_id] = 2 # manager - project_activity = TimeEntryActivity.new({ - :name => 'Project Specific', - :parent => TimeEntryActivity.find(:first), - :project => Project.find(1), - :active => true - }) - assert project_activity.save - project_activity_two = TimeEntryActivity.new({ - :name => 'Project Specific Two', - :parent => TimeEntryActivity.find(:last), - :project => Project.find(1), - :active => true - }) - assert project_activity_two.save - - delete :reset_activities, :id => 1 - assert_response :redirect - assert_redirected_to 'projects/ecookbook/settings/activities' - - assert_nil TimeEntryActivity.find_by_id(project_activity.id) - assert_nil TimeEntryActivity.find_by_id(project_activity_two.id) - end - - def test_reset_activities_should_reassign_time_entries_back_to_the_system_activity - @request.session[:user_id] = 2 # manager - project_activity = TimeEntryActivity.new({ - :name => 'Project Specific Design', - :parent => TimeEntryActivity.find(9), - :project => Project.find(1), - :active => true - }) - assert project_activity.save - assert TimeEntry.update_all("activity_id = '#{project_activity.id}'", ["project_id = ? AND activity_id = ?", 1, 9]) - assert 3, TimeEntry.find_all_by_activity_id_and_project_id(project_activity.id, 1).size - - delete :reset_activities, :id => 1 - assert_response :redirect - assert_redirected_to 'projects/ecookbook/settings/activities' - - assert_nil TimeEntryActivity.find_by_id(project_activity.id) - assert_equal 0, TimeEntry.find_all_by_activity_id_and_project_id(project_activity.id, 1).size, "TimeEntries still assigned to project specific activity" - assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size, "TimeEntries still assigned to project specific activity" - end - # A hook that is manually registered later class ProjectBasedTemplate < Redmine::Hook::ViewListener def view_layouts_base_html_head(context) diff --git a/test/integration/routing_test.rb b/test/integration/routing_test.rb index 74a0f7a71..71be6c3c7 100644 --- a/test/integration/routing_test.rb +++ b/test/integration/routing_test.rb @@ -190,7 +190,7 @@ class RoutingTest < ActionController::IntegrationTest should_route :put, "/projects/1.xml", :controller => 'projects', :action => 'edit', :id => '1', :format => 'xml' should_route :delete, "/projects/1.xml", :controller => 'projects', :action => 'destroy', :id => '1', :format => 'xml' - should_route :delete, "/projects/64/reset_activities", :controller => 'projects', :action => 'reset_activities', :id => '64' + should_route :delete, "/projects/64/reset_activities", :controller => 'project_enumerations', :action => 'destroy', :id => '64' end context "repositories" do From a2ce6e236c397a527deb18cc2196371855d27c41 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Barth Date: Fri, 3 Sep 2010 19:54:24 +0000 Subject: [PATCH 19/72] Allow mass status update through context menu. #3411 NB: it cannot be done with issues from different projects, same as other fields. This will be addressed separately, see #5332. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4055 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/context_menus_controller.rb | 6 ++++++ app/views/context_menus/issues.html.erb | 21 +++++++++++-------- .../context_menus_controller_test.rb | 5 ++++- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/app/controllers/context_menus_controller.rb b/app/controllers/context_menus_controller.rb index 442f85b37..e437a18dc 100644 --- a/app/controllers/context_menus_controller.rb +++ b/app/controllers/context_menus_controller.rb @@ -6,6 +6,12 @@ class ContextMenusController < ApplicationController if (@issues.size == 1) @issue = @issues.first @allowed_statuses = @issue.new_statuses_allowed_to(User.current) + else + @allowed_statuses = @issues.map do |i| + i.new_statuses_allowed_to(User.current) + end.inject do |memo,s| + memo & s + end end projects = @issues.collect(&:project).compact.uniq @project = projects.first if projects.size == 1 diff --git a/app/views/context_menus/issues.html.erb b/app/views/context_menus/issues.html.erb index dc11b5fdb..efd8cde0e 100644 --- a/app/views/context_menus/issues.html.erb +++ b/app/views/context_menus/issues.html.erb @@ -4,20 +4,23 @@ <% if !@issue.nil? -%>
  • <%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue}, :class => 'icon-edit', :disabled => !@can[:edit] %>
  • -
  • - <%= l(:field_status) %> -
      - <% @statuses.each do |s| -%> -
    • <%= context_menu_link s.name, {:controller => 'issues', :action => 'update', :id => @issue, :issue => {:status_id => s}, :back_url => @back}, :method => :put, - :selected => (s == @issue.status), :disabled => !(@can[:update] && @allowed_statuses.include?(s)) %>
    • - <% end -%> -
    -
  • <% else %>
  • <%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id)}, :class => 'icon-edit', :disabled => !@can[:edit] %>
  • <% end %> + <% unless @allowed_statuses.empty? %> +
  • + <%= l(:field_status) %> +
      + <% @statuses.each do |s| -%> +
    • <%= context_menu_link s.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {:status_id => s}, :back_url => @back}, :method => :post, + :selected => (@issue && s == @issue.status), :disabled => !(@can[:update] && @allowed_statuses.include?(s)) %>
    • + <% end -%> +
    +
  • + <% end %> + <% unless @trackers.nil? %>
  • <%= l(:field_tracker) %> diff --git a/test/functional/context_menus_controller_test.rb b/test/functional/context_menus_controller_test.rb index 764350879..0ebae695a 100644 --- a/test/functional/context_menus_controller_test.rb +++ b/test/functional/context_menus_controller_test.rb @@ -12,7 +12,7 @@ class ContextMenusControllerTest < ActionController::TestCase :attributes => { :href => '/issues/1/edit', :class => 'icon-edit' } assert_tag :tag => 'a', :content => 'Closed', - :attributes => { :href => '/issues/1?issue%5Bstatus_id%5D=5', + :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&issue%5Bstatus_id%5D=5', :class => '' } assert_tag :tag => 'a', :content => 'Immediate', :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&issue%5Bpriority_id%5D=8', @@ -59,6 +59,9 @@ class ContextMenusControllerTest < ActionController::TestCase assert_tag :tag => 'a', :content => 'Edit', :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&ids%5B%5D=2', :class => 'icon-edit' } + assert_tag :tag => 'a', :content => 'Closed', + :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&ids%5B%5D=2&issue%5Bstatus_id%5D=5', + :class => '' } assert_tag :tag => 'a', :content => 'Immediate', :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&ids%5B%5D=2&issue%5Bpriority_id%5D=8', :class => '' } From 4776a5a42716181aaa51edf3fc6fc4bc0d19231e Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Barth Date: Fri, 3 Sep 2010 19:59:49 +0000 Subject: [PATCH 20/72] Hide checkboxes in issues list when printing git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4056 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/views/issues/_list.rhtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/issues/_list.rhtml b/app/views/issues/_list.rhtml index 2722b9e7b..1a2953bf5 100644 --- a/app/views/issues/_list.rhtml +++ b/app/views/issues/_list.rhtml @@ -3,7 +3,7 @@
    - <%= sort_header_tag('id', :caption => '#', :default_order => 'desc') %> @@ -25,7 +25,7 @@ <% previous_group = group %> <% end %> "> - + <% query.columns.each do |column| %><%= content_tag 'td', column_content(column, issue), :class => column.name %><% end %> From b6d9f2bddf40eed35c4172342516542640304ac6 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Barth Date: Fri, 3 Sep 2010 20:05:51 +0000 Subject: [PATCH 21/72] Add css classes to journals display to facilitate theming git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4057 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/helpers/journals_helper.rb | 7 +++++++ app/views/issues/_history.rhtml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/helpers/journals_helper.rb b/app/helpers/journals_helper.rb index c8d53f253..990bfb1e5 100644 --- a/app/helpers/journals_helper.rb +++ b/app/helpers/journals_helper.rb @@ -39,4 +39,11 @@ module JournalsHelper onclick = "new Ajax.Request('#{url_for(url)}', {asynchronous:true, evalScripts:true, method:'get'}); return false;" link_to text, '#', options.merge(:onclick => onclick) end + + def css_journal_classes(journal) + s = 'journal' + s << ' has-notes' unless journal.notes.blank? + s << ' has-details' unless journal.details.blank? + s + end end diff --git a/app/views/issues/_history.rhtml b/app/views/issues/_history.rhtml index a95cbf81c..4ea2dd2ac 100644 --- a/app/views/issues/_history.rhtml +++ b/app/views/issues/_history.rhtml @@ -1,6 +1,6 @@ <% reply_links = authorize_for('issues', 'edit') -%> <% for journal in journals %> -
    +

    <%= avatar(journal.user, :size => "24") %> <%= content_tag('a', '', :name => "note-#{journal.indice}")%> From c799d03eceab362bac7e99313a255f6569c9756a Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Barth Date: Fri, 3 Sep 2010 20:16:00 +0000 Subject: [PATCH 22/72] Added missing tests for User#allowed_to? #6291 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4058 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- test/unit/user_test.rb | 43 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb index ea40ccff6..c263eafc0 100644 --- a/test/unit/user_test.rb +++ b/test/unit/user_test.rb @@ -355,6 +355,49 @@ class UserTest < ActiveSupport::TestCase end + context "#allowed_to?" do + context "with a unique project" do + should "return false if project is archived" do + project = Project.find(1) + Project.any_instance.stubs(:status).returns(Project::STATUS_ARCHIVED) + assert ! @admin.allowed_to?(:view_issues, Project.find(1)) + end + + should "return false if related module is disabled" do + project = Project.find(1) + project.enabled_module_names = ["issue_tracking"] + assert @admin.allowed_to?(:add_issues, project) + assert ! @admin.allowed_to?(:view_wiki_pages, project) + end + + should "authorize nearly everything for admin users" do + project = Project.find(1) + assert ! @admin.member_of?(project) + %w(edit_issues delete_issues manage_news manage_documents manage_wiki).each do |p| + assert @admin.allowed_to?(p.to_sym, project) + end + end + + should "authorize normal users depending on their roles" do + project = Project.find(1) + assert @jsmith.allowed_to?(:delete_messages, project) #Manager + assert ! @dlopper.allowed_to?(:delete_messages, project) #Developper + end + end + + context "with options[:global]" do + should "authorize if user has at least one role that has this permission" do + @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere + @anonymous = User.find(6) + assert @jsmith.allowed_to?(:delete_issue_watchers, nil, :global => true) + assert ! @dlopper2.allowed_to?(:delete_issue_watchers, nil, :global => true) + assert @dlopper2.allowed_to?(:add_issues, nil, :global => true) + assert ! @anonymous.allowed_to?(:add_issues, nil, :global => true) + assert @anonymous.allowed_to?(:view_issues, nil, :global => true) + end + end + end + if Object.const_defined?(:OpenID) def test_setting_identity_url From a9f5a17c678d6ebc495d42d858db0ce5727a5381 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Barth Date: Fri, 3 Sep 2010 21:43:07 +0000 Subject: [PATCH 23/72] Do not display items without valid selection in context menu when on different projects. #4998 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4059 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/context_menus_controller.rb | 4 ++-- app/views/context_menus/issues.html.erb | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/controllers/context_menus_controller.rb b/app/controllers/context_menus_controller.rb index e437a18dc..5f4b02ca2 100644 --- a/app/controllers/context_menus_controller.rb +++ b/app/controllers/context_menus_controller.rb @@ -13,8 +13,8 @@ class ContextMenusController < ApplicationController memo & s end end - projects = @issues.collect(&:project).compact.uniq - @project = projects.first if projects.size == 1 + @projects = @issues.collect(&:project).compact.uniq + @project = @projects.first if @projects.size == 1 @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)), :log_time => (@project && User.current.allowed_to?(:log_time, @project)), diff --git a/app/views/context_menus/issues.html.erb b/app/views/context_menus/issues.html.erb index efd8cde0e..94d4e802f 100644 --- a/app/views/context_menus/issues.html.erb +++ b/app/views/context_menus/issues.html.erb @@ -32,6 +32,8 @@ <% end %> + + <% if @projects.size == 1 %>
  • <%= l(:field_priority) %>
      @@ -41,6 +43,8 @@ <% end -%>
  • + <% end %> + <% unless @project.nil? || @project.shared_versions.open.empty? -%>
  • <%= l(:field_fixed_version) %> @@ -80,7 +84,8 @@
  • <% end -%> - <% if Issue.use_field_for_done_ratio? %> + + <% if Issue.use_field_for_done_ratio? && @projects.size == 1 %>
  • <%= l(:field_done_ratio) %>
      @@ -91,6 +96,7 @@
  • <% end %> + <% if !@issue.nil? %> <% if @can[:log_time] -%>
  • <%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue}, From c5071cd091ae5eda0dea5e9ba23a42b58462a117 Mon Sep 17 00:00:00 2001 From: Azamat Hackimov Date: Sun, 5 Sep 2010 11:33:08 +0000 Subject: [PATCH 24/72] Translation updates: * ca (#6263) * nl (#6248) git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4060 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- config/locales/ca.yml | 235 +++++++++--------- config/locales/nl.yml | 36 +-- .../javascripts/calendar/lang/calendar-ca.js | 22 +- 3 files changed, 150 insertions(+), 143 deletions(-) diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 80d857e9e..b5c28d98e 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -1,4 +1,8 @@ +# Redmine catalan translation: +# by Joan Duran + ca: + # Text direction: Left-to-Right (ltr) or Right-to-Left (rtl) direction: ltr date: formats: @@ -65,6 +69,7 @@ ca: other: "almost {{count}} years" number: + # Default format for numbers format: separator: "." delimiter: "" @@ -83,6 +88,7 @@ ca: mb: "MB" gb: "GB" tb: "TB" + # Used in array.to_sentence. support: @@ -116,6 +122,7 @@ ca: greater_than_start_date: "ha de ser superior que la data inicial" not_same_project: "no pertany al mateix projecte" circular_dependency: "Aquesta relació crearia una dependència circular" + cant_link_an_issue_with_a_descendant: "Un assumpte no es pot enllaçar a una de les seves subtasques" actionview_instancetag_blank_option: Seleccioneu @@ -149,18 +156,33 @@ ca: notice_email_sent: "S'ha enviat un correu electrònic a {{value}}" notice_email_error: "S'ha produït un error en enviar el correu ({{value}})" notice_feeds_access_key_reseted: "S'ha reiniciat la clau d'accés del RSS." + notice_api_access_key_reseted: "S'ha reiniciat la clau d'accés a l'API." notice_failed_to_save_issues: "No s'han pogut desar %s assumptes de {{count}} seleccionats: {{ids}}." + notice_failed_to_save_members: "No s'han pogut desar els membres: {{errors}}." notice_no_issue_selected: "No s'ha seleccionat cap assumpte. Activeu els assumptes que voleu editar." notice_account_pending: "S'ha creat el compte i ara està pendent de l'aprovació de l'administrador." notice_default_data_loaded: "S'ha carregat correctament la configuració predeterminada." notice_unable_delete_version: "No s'ha pogut suprimir la versió." + notice_unable_delete_time_entry: "No s'ha pogut suprimir l'entrada del registre de temps." + notice_issue_done_ratios_updated: "S'ha actualitzat el tant per cent dels assumptes." error_can_t_load_default_data: "No s'ha pogut carregar la configuració predeterminada: {{value}} " error_scm_not_found: "No s'ha trobat l'entrada o la revisió en el dipòsit." error_scm_command_failed: "S'ha produït un error en intentar accedir al dipòsit: {{value}}" error_scm_annotate: "L'entrada no existeix o no s'ha pogut anotar." error_issue_not_found_in_project: "No s'ha trobat l'assumpte o no pertany a aquest projecte" - + error_no_tracker_in_project: "Aquest projecte no té seguidor associat. Comproveu els paràmetres del projecte." + error_no_default_issue_status: "No s'ha definit cap estat d'assumpte predeterminat. Comproveu la configuració (aneu a «Administració -> Estats de l'assumpte»)." + error_can_not_delete_custom_field: "No s'ha pogut suprimir el camp personalitat" + error_can_not_delete_tracker: "Aquest seguidor conté assumptes i no es pot suprimir." + error_can_not_remove_role: "Aquest rol s'està utilitzant i no es pot suprimir." + error_can_not_reopen_issue_on_closed_version: "Un assumpte assignat a una versió tancada no es pot tornar a obrir" + error_can_not_archive_project: "Aquest projecte no es pot arxivar" + error_issue_done_ratios_not_updated: "No s'ha actualitza el tant per cent dels assumptes." + error_workflow_copy_source: "Seleccioneu un seguidor o rol font" + error_workflow_copy_target: "Seleccioneu seguidors i rols objectiu" + error_unable_delete_issue_status: "No s'ha pogut suprimir l'estat de l'assumpte" + error_unable_to_connect: "No s'ha pogut connectar ({{value}})" warning_attachments_not_saved: "No s'han pogut desar {{count}} fitxers." mail_subject_lost_password: "Contrasenya de {{value}}" @@ -173,6 +195,10 @@ ca: mail_body_account_activation_request: "S'ha registrat un usuari nou ({{value}}). El seu compte està pendent d'aprovació:" mail_subject_reminder: "{{count}} assumptes venceran els següents {{days}} dies" mail_body_reminder: "{{count}} assumptes que teniu assignades venceran els següents {{days}} dies:" + mail_subject_wiki_content_added: "S'ha afegit la pàgina wiki «{{page}}»" + mail_body_wiki_content_added: "En {{author}} ha afegit la pàgina wiki «{{page}}»." + mail_subject_wiki_content_updated: "S'ha actualitzat la pàgina wiki «{{page}}»" + mail_body_wiki_content_updated: "En {{author}} ha actualitzat la pàgina wiki «{{page}}»." gui_validation_error: 1 error gui_validation_error_plural: "{{count}} errors" @@ -212,6 +238,7 @@ ca: field_priority: Prioritat field_fixed_version: Versió objectiu field_user: Usuari + field_principal: Principal field_role: Rol field_homepage: Pàgina web field_is_public: Públic @@ -256,6 +283,7 @@ ca: field_redirect_existing_links: Redirigeix els enllaços existents field_estimated_hours: Temps previst field_column_names: Columnes + field_time_entries: "Registre de temps" field_time_zone: Zona horària field_searchable: Es pot cercar field_default_value: Valor predeterminat @@ -265,6 +293,9 @@ ca: field_watcher: Vigilància field_identity_url: URL OpenID field_content: Contingut + field_group_by: "Agrupa els resultats per" + field_sharing: Compartició + field_parent_issue: "Tasca pare" setting_app_title: "Títol de l'aplicació" setting_app_subtitle: "Subtítol de l'aplicació" @@ -300,20 +331,35 @@ ca: setting_activity_days_default: "Dies a mostrar l'activitat del projecte" setting_display_subprojects_issues: "Mostra els assumptes d'un subprojecte en el projecte pare per defecte" setting_enabled_scm: "Habilita l'SCM" + setting_mail_handler_body_delimiters: "Trunca els correus electrònics després d'una d'aquestes línies" setting_mail_handler_api_enabled: "Habilita el WS per correus electrònics d'entrada" setting_mail_handler_api_key: Clau API setting_sequential_project_identifiers: Genera identificadors de projecte seqüencials setting_gravatar_enabled: "Utilitza les icones d'usuari Gravatar" + setting_gravatar_default: "Imatge Gravatar predeterminada" setting_diff_max_lines_displayed: Número màxim de línies amb diferències mostrades setting_file_max_size_displayed: Mida màxima dels fitxers de text mostrats en línia setting_repository_log_display_limit: Número màxim de revisions que es mostren al registre de fitxers setting_openid: "Permet entrar i registrar-se amb l'OpenID" - + setting_password_min_length: "Longitud mínima de la contrasenya" + setting_new_project_user_role_id: "Aquest rol es dóna a un usuari no administrador per a crear projectes" + setting_default_projects_modules: "Mòduls activats per defecte en els projectes nous" + setting_issue_done_ratio: "Calcula tant per cent realitzat de l'assumpte amb" + setting_issue_done_ratio_issue_status: "Utilitza l'estat de l'assumpte" + setting_issue_done_ratio_issue_field: "Utilitza el camp de l'assumpte" + setting_start_of_week: "Inicia les setmanes en" + setting_rest_api_enabled: "Habilita el servei web REST" + setting_cache_formatted_text: Cache formatted text + + permission_add_project: "Crea projectes" + permission_add_subprojects: "Crea subprojectes" permission_edit_project: Edita el projecte permission_select_project_modules: Selecciona els mòduls del projecte permission_manage_members: Gestiona els membres + permission_manage_project_activities: "Gestiona les activitats del projecte" permission_manage_versions: Gestiona les versions permission_manage_categories: Gestiona les categories dels assumptes + permission_view_issues: "Visualitza els assumptes" permission_add_issues: Afegeix assumptes permission_edit_issues: Edita els assumptes permission_manage_issue_relations: Gestiona les relacions dels assumptes @@ -328,6 +374,7 @@ ca: permission_view_calendar: Visualitza el calendari permission_view_issue_watchers: Visualitza la llista de vigilàncies permission_add_issue_watchers: Afegeix vigilàncies + permission_delete_issue_watchers: Suprimeix els vigilants permission_log_time: Registra el temps invertit permission_view_time_entries: Visualitza el temps invertit permission_edit_time_entries: Edita els registres de temps @@ -357,6 +404,8 @@ ca: permission_edit_own_messages: Edita els missatges propis permission_delete_messages: Suprimeix els missatges permission_delete_own_messages: Suprimeix els missatges propis + permission_export_wiki_pages: "Exporta les pàgines wiki" + permission_manage_subtasks: "Gestiona subtasques" project_module_issue_tracking: "Seguidor d'assumptes" project_module_time_tracking: Seguidor de temps @@ -366,10 +415,13 @@ ca: project_module_wiki: Wiki project_module_repository: Dipòsit project_module_boards: Taulers + project_module_calendar: Calendari + project_module_gantt: Gantt label_user: Usuari label_user_plural: Usuaris label_user_new: Usuari nou + label_user_anonymous: Anònim label_project: Projecte label_project_new: Projecte nou label_project_plural: Projectes @@ -416,12 +468,13 @@ ca: label_information_plural: Informació label_please_login: Entreu label_register: Registre - label_login_with_open_id_option: o entra amb l'OpenID + label_login_with_open_id_option: "o entra amb l'OpenID" label_password_lost: Contrasenya perduda label_home: Inici label_my_page: La meva pàgina label_my_account: El meu compte label_my_projects: Els meus projectes + label_my_page_block: "Els meus blocs de pàgina" label_administration: Administració label_login: Entra label_logout: Surt @@ -441,6 +494,7 @@ ca: label_auth_source_new: "Mode d'autenticació nou" label_auth_source_plural: "Modes d'autenticació" label_subproject_plural: Subprojectes + label_subproject_new: "Subprojecte nou" label_and_its_subprojects: "{{value}} i els seus subprojectes" label_min_max_length: Longitud mín - max label_list: Llist @@ -475,8 +529,9 @@ ca: label_version: Versió label_version_new: Versió nova label_version_plural: Versions + label_close_versions: "Tanca les versions completades" label_confirmation: Confirmació - label_export_to: 'També disponible a:' + label_export_to: "També disponible a:" label_read: Llegeix... label_public_projects: Projectes públics label_open_issues: obert @@ -533,6 +588,8 @@ ca: label_not_equals: no és label_in_less_than: en menys de label_in_more_than: en més de + label_greater_or_equal: ">=" + label_less_or_equal: <= label_in: en label_today: avui label_all_time: tot el temps @@ -555,17 +612,21 @@ ca: label_browse: Navega label_modification: "{{count}} canvi" label_modification_plural: "{{count}} canvis" + label_branch: Branca + label_tag: Etiqueta label_revision: Revisió label_revision_plural: Revisions + label_revision_id: "Revisió {{value}}" label_associated_revisions: Revisions associades label_added: afegit label_modified: modificat - label_renamed: reanomenat label_copied: copiat + label_renamed: reanomenat label_deleted: suprimit label_latest_revision: Última revisió label_latest_revision_plural: Últimes revisions label_view_revisions: Visualitza les revisions + label_view_all_revisions: "Visualitza totes les revisions" label_max_size: Mida màxima label_sort_highest: Mou a la part superior label_sort_higher: Mou cap amunt @@ -591,6 +652,7 @@ ca: label_changes_details: Detalls de tots els canvis label_issue_tracking: "Seguiment d'assumptes" label_spent_time: Temps invertit + label_overall_spent_time: "Temps total invertit" label_f_hour: "{{value}} hora" label_f_hour_plural: "{{value}} hores" label_time_tracking: Temps de seguiment @@ -628,6 +690,8 @@ ca: label_board: Fòrum label_board_new: Fòrum nou label_board_plural: Fòrums + label_board_locked: Bloquejat + label_board_sticky: Sticky label_topic_plural: Temes label_message_plural: Missatges label_message_last: Últim missatge @@ -643,6 +707,8 @@ ca: label_language_based: "Basat en l'idioma de l'usuari" label_sort_by: "Ordena per {{value}}" label_send_test_email: Envia un correu electrònic de prova + label_feeds_access_key: "Clau d'accés del RSS" + label_missing_feeds_access_key: "Falta una clau d'accés del RSS" label_feeds_access_key_created_on: "Clau d'accés del RSS creada fa {{value}}" label_module_plural: Mòduls label_added_time_by: "Afegit per {{author}} fa {{age}}" @@ -688,6 +754,28 @@ ca: label_ascending: Ascendent label_descending: Descendent label_date_from_to: Des de {{start}} a {{end}} + label_wiki_content_added: "S'ha afegit la pàgina wiki" + label_wiki_content_updated: "S'ha actualitzat la pàgina wiki" + label_group: Grup + label_group_plural: Grups + label_group_new: Grup nou + label_time_entry_plural: Temps invertit + label_version_sharing_hierarchy: "Amb la jerarquia del projecte" + label_version_sharing_system: "Amb tots els projectes" + label_version_sharing_descendants: "Amb tots els subprojectes" + label_version_sharing_tree: "Amb l'arbre del projecte" + label_version_sharing_none: "Sense compartir" + label_update_issue_done_ratios: "Actualitza el tant per cent dels assumptes realitzats" + label_copy_source: Font + label_copy_target: Objectiu + label_copy_same_as_target: "El mateix que l'objectiu" + label_display_used_statuses_only: "Mostra només els estats que utilitza aquest seguidor" + label_api_access_key: "Clau d'accés a l'API" + label_missing_api_access_key: "Falta una clau d'accés de l'API" + label_api_access_key_created_on: "Clau d'accés de l'API creada fa {{value}}" + label_profile: Perfil + label_subtask_plural: Subtasques + label_project_copy_notifications: "Envia notificacions de correu electrònic durant la còpia del projecte" button_login: Entra button_submit: Tramet @@ -709,11 +797,12 @@ ca: button_list: Llista button_view: Visualitza button_move: Mou + button_move_and_follow: "Mou i segueix" button_back: Enrere button_cancel: Cancel·la button_activate: Activa button_sort: Ordena - button_log_time: "Hora d'entrada" + button_log_time: "Registre de temps" button_rollback: Torna a aquesta versió button_watch: Vigila button_unwatch: No vigilis @@ -724,15 +813,24 @@ ca: button_rename: Reanomena button_change_password: Canvia la contrasenya button_copy: Copia + button_copy_and_follow: "Copia i segueix" button_annotate: Anota button_update: Actualitza button_configure: Configura button_quote: Cita + button_duplicate: Duplica + button_show: Mostra status_active: actiu status_registered: informat status_locked: bloquejat + version_status_open: oberta + version_status_locked: bloquejada + version_status_closed: tancada + + field_active: Actiu + text_select_mail_notifications: "Seleccioneu les accions per les quals s'hauria d'enviar una notificació per correu electrònic." text_regexp_info: ex. ^[A-Z0-9]+$ text_min_max_length_info: 0 significa sense restricció @@ -740,6 +838,10 @@ ca: text_subprojects_destroy_warning: "També seran suprimits els seus subprojectes: {{value}}." text_workflow_edit: Seleccioneu un rol i un seguidor per a editar el flux de treball text_are_you_sure: Segur? + text_journal_changed: "{{label}} ha canviat de {{old}} a {{new}}" + text_journal_set_to: "{{label}} s'ha establert a {{value}}" + text_journal_deleted: "{{label}} s'ha suprimit ({{old}})" + text_journal_added: "S'ha afegit {{label}} {{value}}" text_tip_task_begin_day: "tasca que s'inicia aquest dia" text_tip_task_end_day: tasca que finalitza aquest dia text_tip_task_begin_end_day: "tasca que s'inicia i finalitza aquest dia" @@ -750,6 +852,7 @@ ca: text_tracker_no_workflow: "No s'ha definit cap flux de treball per a aquest seguidor" text_unallowed_characters: Caràcters no permesos text_comma_separated: Es permeten valors múltiples (separats per una coma). + text_line_separated: "Es permeten diversos valors (una línia per cada valor)." text_issues_ref_in_commit_messages: Referència i soluciona els assumptes en els missatges publicats text_issue_added: "L'assumpte {{id}} ha sigut informat per {{author}}." text_issue_updated: "L'assumpte {{id}} ha sigut actualitzat per {{author}}." @@ -770,14 +873,21 @@ ca: text_destroy_time_entries_question: "S'han informat {{hours}} hores en els assumptes que aneu a suprimir. Què voleu fer?" text_destroy_time_entries: Suprimeix les hores informades text_assign_time_entries_to_project: Assigna les hores informades al projecte - text_reassign_time_entries: 'Torna a assignar les hores informades a aquest assumpte:' + text_reassign_time_entries: "Torna a assignar les hores informades a aquest assumpte:" text_user_wrote: "{{value}} va escriure:" text_enumeration_destroy_question: "{{count}} objectes estan assignats a aquest valor." - text_enumeration_category_reassign_to: 'Torna a assignar-los a aquest valor:' + text_enumeration_category_reassign_to: "Torna a assignar-los a aquest valor:" text_email_delivery_not_configured: "El lliurament per correu electrònic no està configurat i les notificacions estan inhabilitades.\nConfigureu el servidor SMTP a config/email.yml i reinicieu l'aplicació per habilitar-lo." text_repository_usernames_mapping: "Seleccioneu l'assignació entre els usuaris del Redmine i cada nom d'usuari trobat al dipòsit.\nEls usuaris amb el mateix nom d'usuari o correu del Redmine i del dipòsit s'assignaran automàticament." text_diff_truncated: "... Aquestes diferències s'han trucat perquè excedeixen la mida màxima que es pot mostrar." - text_custom_field_possible_values_info: 'Una línia per a cada valor' + text_custom_field_possible_values_info: "Una línia per a cada valor" + text_wiki_page_destroy_question: "Aquesta pàgina té {{descendants}} pàgines fill i descendents. Què voleu fer?" + text_wiki_page_nullify_children: "Deixa les pàgines fill com a pàgines arrel" + text_wiki_page_destroy_children: "Suprimeix les pàgines fill i tots els seus descendents" + text_wiki_page_reassign_children: "Reasigna les pàgines fill a aquesta pàgina pare" + text_own_membership_delete_confirmation: "Esteu a punt de suprimir algun o tots els vostres permisos i potser no podreu editar més aquest projecte.\nSegur que voleu continuar?" + text_zoom_in: Redueix + text_zoom_out: Amplia default_role_manager: Gestor default_role_developer: Desenvolupador @@ -804,108 +914,5 @@ ca: enumeration_issue_priorities: Prioritat dels assumptes enumeration_doc_categories: Categories del document enumeration_activities: Activitats (seguidor de temps) - label_greater_or_equal: ">=" - label_less_or_equal: <= - text_wiki_page_destroy_question: This page has {{descendants}} child page(s) and descendant(s). What do you want to do? - text_wiki_page_reassign_children: Reassign child pages to this parent page - text_wiki_page_nullify_children: Keep child pages as root pages - text_wiki_page_destroy_children: Delete child pages and all their descendants - setting_password_min_length: Minimum password length - field_group_by: Group results by - mail_subject_wiki_content_updated: "'{{page}}' wiki page has been updated" - label_wiki_content_added: Wiki page added - mail_subject_wiki_content_added: "'{{page}}' wiki page has been added" - mail_body_wiki_content_added: The '{{page}}' wiki page has been added by {{author}}. - label_wiki_content_updated: Wiki page updated - mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}. - permission_add_project: Create project - setting_new_project_user_role_id: Role given to a non-admin user who creates a project - label_view_all_revisions: View all revisions - label_tag: Tag - label_branch: Branch - error_no_tracker_in_project: No tracker is associated to this project. Please check the Project settings. - error_no_default_issue_status: No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses"). - text_journal_changed: "{{label}} changed from {{old}} to {{new}}" - text_journal_set_to: "{{label}} set to {{value}}" - text_journal_deleted: "{{label}} deleted ({{old}})" - label_group_plural: Groups - label_group: Group - label_group_new: New group - label_time_entry_plural: Spent time - text_journal_added: "{{label}} {{value}} added" - field_active: Active - enumeration_system_activity: System Activity - permission_delete_issue_watchers: Delete watchers - version_status_closed: closed - version_status_locked: locked - version_status_open: open - error_can_not_reopen_issue_on_closed_version: An issue assigned to a closed version can not be reopened - label_user_anonymous: Anonymous - button_move_and_follow: Move and follow - setting_default_projects_modules: Default enabled modules for new projects - setting_gravatar_default: Default Gravatar image - field_sharing: Sharing - label_version_sharing_hierarchy: With project hierarchy - label_version_sharing_system: With all projects - label_version_sharing_descendants: With subprojects - label_version_sharing_tree: With project tree - label_version_sharing_none: Not shared - error_can_not_archive_project: This project can not be archived - button_duplicate: Duplicate - button_copy_and_follow: Copy and follow - label_copy_source: Source - setting_issue_done_ratio: Calculate the issue done ratio with - setting_issue_done_ratio_issue_status: Use the issue status - error_issue_done_ratios_not_updated: Issue done ratios not updated. - error_workflow_copy_target: Please select target tracker(s) and role(s) - setting_issue_done_ratio_issue_field: Use the issue field - label_copy_same_as_target: Same as target - label_copy_target: Target - notice_issue_done_ratios_updated: Issue done ratios updated. - error_workflow_copy_source: Please select a source tracker or role - label_update_issue_done_ratios: Update issue done ratios - setting_start_of_week: Start calendars on - permission_view_issues: View Issues - label_display_used_statuses_only: Only display statuses that are used by this tracker - label_revision_id: Revision {{value}} - label_api_access_key: API access key - label_api_access_key_created_on: API access key created {{value}} ago - label_feeds_access_key: RSS access key - notice_api_access_key_reseted: Your API access key was reset. - setting_rest_api_enabled: Enable REST web service - label_missing_api_access_key: Missing an API access key - label_missing_feeds_access_key: Missing a RSS access key - button_show: Show - text_line_separated: Multiple values allowed (one line for each value). - setting_mail_handler_body_delimiters: Truncate emails after one of these lines - permission_add_subprojects: Create subprojects - label_subproject_new: New subproject - text_own_membership_delete_confirmation: |- - You are about to remove some or all of your permissions and may no longer be able to edit this project after that. - Are you sure you want to continue? - label_close_versions: Close completed versions - label_board_sticky: Sticky - label_board_locked: Locked - permission_export_wiki_pages: Export wiki pages - setting_cache_formatted_text: Cache formatted text - permission_manage_project_activities: Manage project activities - error_unable_delete_issue_status: Unable to delete issue status - label_profile: Profile - permission_manage_subtasks: Manage subtasks - field_parent_issue: Parent task - label_subtask_plural: Subtasks - label_project_copy_notifications: Send email notifications during the project copy - error_can_not_delete_custom_field: Unable to delete custom field - error_unable_to_connect: Unable to connect ({{value}}) - error_can_not_remove_role: This role is in use and can not be deleted. - error_can_not_delete_tracker: This tracker contains issues and can't be deleted. - field_principal: Principal - label_my_page_block: My page block - notice_failed_to_save_members: "Failed to save member(s): {{errors}}." - text_zoom_out: Zoom out - text_zoom_in: Zoom in - notice_unable_delete_time_entry: Unable to delete time log entry. - label_overall_spent_time: Overall spent time - field_time_entries: Log time - project_module_gantt: Gantt - project_module_calendar: Calendar + enumeration_system_activity: Activitat del sistema + diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 1e1489940..6dfe989b5 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -701,7 +701,7 @@ nl: setting_date_format: Datumformaat setting_default_language: Standaard taal setting_default_projects_public: Nieuwe projecten zijn standaard publiek - setting_diff_max_lines_displayed: Max number of diff lines displayed + setting_diff_max_lines_displayed: Max aantal diff regels weer te geven setting_display_subprojects_issues: Standaard issues van subproject tonen setting_emails_footer: E-mails footer setting_enabled_scm: SCM ingeschakeld @@ -709,7 +709,7 @@ nl: setting_gravatar_enabled: Gebruik Gravatar gebruikersiconen setting_host_name: Hostnaam setting_issue_list_default_columns: Standaardkolommen getoond op de lijst met issues - setting_issues_export_limit: Limiet export issues + setting_issues_export_limit: Max aantal te exporteren issues setting_login_required: Authenticatie vereist setting_mail_from: Afzender e-mail adres setting_mail_handler_api_enabled: Schakel WS in voor inkomende mail. @@ -727,7 +727,7 @@ nl: setting_welcome_text: Welkomsttekst setting_wiki_compression: Wikigeschiedenis comprimeren status_active: actief - status_locked: gelockt + status_locked: vergrendeld status_registered: geregistreerd text_are_you_sure: Weet u het zeker? text_assign_time_entries_to_project: Gerapporteerde uren toevoegen aan dit project @@ -753,7 +753,7 @@ nl: text_load_default_configuration: Laad de standaardconfiguratie text_min_max_length_info: 0 betekent geen restrictie text_no_configuration_data: "Rollen, trackers, issue statussen en workflows zijn nog niet geconfigureerd.\nHet is ten zeerste aangeraden om de standaard configuratie in te laden. U kunt deze aanpassen nadat deze is ingeladen." - text_plugin_assets_writable: Plugin assets directory writable + text_plugin_assets_writable: Plugin assets directory beschrijfbaar text_project_destroy_confirmation: Weet u zeker dat u dit project en alle gerelateerde gegevens wilt verwijderen? text_project_identifier_info: 'kleine letters (a-z), cijfers en liggende streepjes toegestaan.
    Eenmaal bewaard kan de identificatiecode niet meer worden gewijzigd.' text_reassign_time_entries: 'Gerapporteerde uren opnieuw toewijzen:' @@ -778,7 +778,7 @@ nl: text_custom_field_possible_values_info: 'Per lijn een waarde' label_display: Toon field_editable: Bewerkbaar - setting_repository_log_display_limit: Maximum hoeveelheid van revisies zichbaar + setting_repository_log_display_limit: Max aantal revisies zichbaar setting_file_max_size_displayed: Max grootte van tekst bestanden inline zichtbaar field_watcher: Watcher setting_openid: Sta OpenID login en registratie toe @@ -824,7 +824,7 @@ nl: version_status_closed: gesloten version_status_locked: vergrendeld version_status_open: open - error_can_not_reopen_issue_on_closed_version: An issue assigned to a closed version can not be reopened + error_can_not_reopen_issue_on_closed_version: Een issue toegewezen aan een gesloten versie kan niet heropend worden label_user_anonymous: Anoniem button_move_and_follow: Verplaats en volg setting_default_projects_modules: Standaard geactiveerde modules voor nieuwe projecten @@ -833,36 +833,36 @@ nl: label_version_sharing_hierarchy: Met project hiërarchie label_version_sharing_system: Met alle projecten label_version_sharing_descendants: Met subprojecten - label_version_sharing_tree: With project tree + label_version_sharing_tree: Met project boom label_version_sharing_none: Niet gedeeld error_can_not_archive_project: Dit project kan niet worden gearchiveerd button_duplicate: Dupliceer button_copy_and_follow: Kopiëer en volg label_copy_source: Bron - setting_issue_done_ratio: Bereken issue done ratio met + setting_issue_done_ratio: Bereken issue percentage voldaan met setting_issue_done_ratio_issue_status: Gebruik de issue status - error_issue_done_ratios_not_updated: Issue done ratios niet geupdate. + error_issue_done_ratios_not_updated: Issue percentage voldaan niet geupdate. error_workflow_copy_target: Selecteer tracker(s) en rol(len) setting_issue_done_ratio_issue_field: Gebruik het issue veld label_copy_same_as_target: Zelfde als doel label_copy_target: Doel - notice_issue_done_ratios_updated: Issue done ratios updated. + notice_issue_done_ratios_updated: Issue percentage voldaan geupdate. error_workflow_copy_source: Selecteer een bron tracker of rol - label_update_issue_done_ratios: Update issue done ratios + label_update_issue_done_ratios: Update issue percentage voldaan setting_start_of_week: Week begint op permission_view_issues: Bekijk Issues label_display_used_statuses_only: Laat alleen statussen zien die gebruikt worden door deze tracker - label_revision_id: Revision {{value}} + label_revision_id: Revisie {{value}} label_api_access_key: API access key label_api_access_key_created_on: API access key gemaakt {{value}} geleden label_feeds_access_key: RSS access key notice_api_access_key_reseted: Uw API access key was gereset. - setting_rest_api_enabled: Enable REST web service + setting_rest_api_enabled: Activeer REST web service label_missing_api_access_key: Geen API access key label_missing_feeds_access_key: Geen RSS access key button_show: Laat zien text_line_separated: Meerdere waarden toegestaan (elke regel is een waarde). - setting_mail_handler_body_delimiters: Truncate emails after one of these lines + setting_mail_handler_body_delimiters: Breek email verwerking af na een van deze regels permission_add_subprojects: Maak subprojecten label_subproject_new: Nieuw subproject text_own_membership_delete_confirmation: |- @@ -873,10 +873,10 @@ nl: label_board_locked: Vergrendeld permission_export_wiki_pages: Exporteer wiki pagina's setting_cache_formatted_text: Cache opgemaakte tekst - permission_manage_project_activities: Manage project activities + permission_manage_project_activities: Beheer project activiteiten error_unable_delete_issue_status: Verwijderen van issue status niet gelukt label_profile: Profiel - permission_manage_subtasks: Manage subtasks + permission_manage_subtasks: Beheer subtasks field_parent_issue: Parent task label_subtask_plural: Subtasks label_project_copy_notifications: Stuur email notificaties voor de project kopie @@ -891,6 +891,6 @@ nl: text_zoom_in: Zoom in notice_unable_delete_time_entry: Verwijderen niet mogelijk van tijd log invoer. label_overall_spent_time: Totaal bestede tijd - field_time_entries: Log time + field_time_entries: Log tijd project_module_gantt: Gantt - project_module_calendar: Calendar + project_module_calendar: Kalender diff --git a/public/javascripts/calendar/lang/calendar-ca.js b/public/javascripts/calendar/lang/calendar-ca.js index 303f21dfd..9902680e5 100644 --- a/public/javascripts/calendar/lang/calendar-ca.js +++ b/public/javascripts/calendar/lang/calendar-ca.js @@ -45,7 +45,7 @@ Calendar._SDN = new Array // First day of the week. "0" means display Sunday first, "1" means display // Monday first, etc. -Calendar._FD = 0; +Calendar._FD = 1; // full month names Calendar._MN = new Array @@ -84,17 +84,17 @@ Calendar._TT["INFO"] = "Quant al calendari"; Calendar._TT["ABOUT"] = "Selector DHTML de data/hora\n" + "(c) dynarch.com 2002-2005 / Autor: Mihai Bazon\n" + // don't translate this this ;-) -"Per a aconseguir l'última versió visiteu: http://www.dynarch.com/projects/calendar/\n" + -"Distribuït sota la llicència GNU LGPL. Vegeu http://gnu.org/licenses/lgpl.html per a més detalls." + +"Per aconseguir l'última versió visiteu: http://www.dynarch.com/projects/calendar/\n" + +"Distribuït sota la llicència GNU LGPL. Vegeu http://gnu.org/licenses/lgpl.html per obtenir més detalls." + "\n\n" + "Selecció de la data:\n" + -"- Utilitzeu els botons \xab, \xbb per a seleccionar l'any\n" + -"- Utilitzeu els botons " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " per a selecciona el mes\n" + -"- Mantingueu premut el botó del ratolí sobre qualsevol d'aquests botons per a uns selecció més ràpida."; +"- Utilitzeu els botons \xab, \xbb per seleccionar l'any\n" + +"- Utilitzeu els botons " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " per seleccionar el mes\n" + +"- Mantingueu premut el botó del ratolí sobre qualsevol d'aquests botons per a una selecció més ràpida."; Calendar._TT["ABOUT_TIME"] = "\n\n" + "Selecció de l'hora:\n" + -"- Feu clic en qualsevol part de l'hora per a incrementar-la\n" + -"- o premeu majúscules per a disminuir-la\n" + +"- Feu clic en qualsevol part de l'hora per incrementar-la\n" + +"- o premeu majúscules per disminuir-la\n" + "- o feu clic i arrossegueu per a una selecció més ràpida."; Calendar._TT["PREV_YEAR"] = "Any anterior (mantenir per menú)"; @@ -102,8 +102,8 @@ Calendar._TT["PREV_MONTH"] = "Mes anterior (mantenir per menú)"; Calendar._TT["GO_TODAY"] = "Anar a avui"; Calendar._TT["NEXT_MONTH"] = "Mes següent (mantenir per menú)"; Calendar._TT["NEXT_YEAR"] = "Any següent (mantenir per menú)"; -Calendar._TT["SEL_DATE"] = "Sel·lecciona data"; -Calendar._TT["DRAG_TO_MOVE"] = "Arrossega per a moure"; +Calendar._TT["SEL_DATE"] = "Sel·lecciona la data"; +Calendar._TT["DRAG_TO_MOVE"] = "Arrossega per moure"; Calendar._TT["PART_TODAY"] = " (avui)"; // the following is to inform that "%s" is to be the first day of week @@ -117,7 +117,7 @@ Calendar._TT["WEEKEND"] = "0,6"; Calendar._TT["CLOSE"] = "Tanca"; Calendar._TT["TODAY"] = "Avui"; -Calendar._TT["TIME_PART"] = "(Majúscules-)Feu clic o arrossegueu per a canviar el valor"; +Calendar._TT["TIME_PART"] = "(Majúscules-)Feu clic o arrossegueu per canviar el valor"; // date formats Calendar._TT["DEF_DATE_FORMAT"] = "%d-%m-%Y"; From cbe266079e9105c61bbda84c5cef89927548fcfe Mon Sep 17 00:00:00 2001 From: Azamat Hackimov Date: Sun, 5 Sep 2010 13:01:38 +0000 Subject: [PATCH 25/72] Added string "cant_link_an_issue_with_a_descendant" to translations missed in #443 Solves #6278 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4061 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- config/locales/bg.yml | 1 + config/locales/bs.yml | 1 + config/locales/cs.yml | 1 + config/locales/da.yml | 1 + config/locales/el.yml | 1 + config/locales/en-GB.yml | 1 + config/locales/es.yml | 1 + config/locales/eu.yml | 1 + config/locales/fi.yml | 2 +- config/locales/gl.yml | 1 + config/locales/he.yml | 1 + config/locales/hr.yml | 1 + config/locales/hu.yml | 1 + config/locales/id.yml | 1 + config/locales/it.yml | 1 + config/locales/ja.yml | 1 + config/locales/ko.yml | 1 + config/locales/lt.yml | 3 ++- config/locales/lv.yml | 1 + config/locales/mn.yml | 1 + config/locales/nl.yml | 1 + config/locales/no.yml | 1 + config/locales/pl.yml | 1 + config/locales/pt-BR.yml | 1 + config/locales/pt.yml | 1 + config/locales/ro.yml | 1 + config/locales/ru.yml | 7 ++++--- config/locales/sk.yml | 1 + config/locales/sl.yml | 1 + config/locales/sv.yml | 1 + config/locales/th.yml | 1 + config/locales/tr.yml | 1 + config/locales/uk.yml | 1 + config/locales/vi.yml | 1 + config/locales/zh-TW.yml | 1 + config/locales/zh.yml | 1 + 36 files changed, 40 insertions(+), 5 deletions(-) diff --git a/config/locales/bg.yml b/config/locales/bg.yml index d4fd50c93..f334633e2 100644 --- a/config/locales/bg.yml +++ b/config/locales/bg.yml @@ -116,6 +116,7 @@ bg: greater_than_start_date: "трябва да е след началната дата" not_same_project: "не е от същия проект" circular_dependency: "Тази релация ще доведе до безкрайна зависимост" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: Изберете diff --git a/config/locales/bs.yml b/config/locales/bs.yml index 6d729b0ce..90ec9dea2 100644 --- a/config/locales/bs.yml +++ b/config/locales/bs.yml @@ -130,6 +130,7 @@ bs: greater_than_start_date: "mora biti veći nego početni datum" not_same_project: "ne pripada istom projektu" circular_dependency: "Ova relacija stvar cirkularnu zavisnost" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: Molimo odaberite diff --git a/config/locales/cs.yml b/config/locales/cs.yml index f1abeff5b..f2b114605 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -116,6 +116,7 @@ cs: greater_than_start_date: "musí být větší než počáteční datum" not_same_project: "nepatří stejnému projektu" circular_dependency: "Tento vztah by vytvořil cyklickou závislost" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" # Updated by Josef Liška # CZ translation by Maxim Krušina | Massimo Filippi, s.r.o. | maxim@mxm.cz diff --git a/config/locales/da.yml b/config/locales/da.yml index 45b33d810..4d30dc6f3 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -130,6 +130,7 @@ da: greater_than_start_date: "skal være senere end startdatoen" not_same_project: "hører ikke til samme projekt" circular_dependency: "Denne relation vil skabe et afhængighedsforhold" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" template: header: diff --git a/config/locales/el.yml b/config/locales/el.yml index d9e224e9f..e4487c59f 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -119,6 +119,7 @@ el: greater_than_start_date: "πρέπει να είναι αργότερα από την ημερομηνία έναρξης" not_same_project: "δεν ανήκει στο ίδιο έργο" circular_dependency: "Αυτή η σχέση θα δημιουργήσει κυκλικές εξαρτήσεις" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: Παρακαλώ επιλέξτε diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index bc4cb08e8..6a8faa746 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -122,6 +122,7 @@ en-GB: greater_than_start_date: "must be greater than start date" not_same_project: "doesn't belong to the same project" circular_dependency: "This relation would create a circular dependency" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: Please select diff --git a/config/locales/es.yml b/config/locales/es.yml index 4783db721..4ffdaf3f6 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -132,6 +132,7 @@ es: greater_than_start_date: "debe ser posterior a la fecha de comienzo" not_same_project: "no pertenece al mismo proyecto" circular_dependency: "Esta relación podría crear una dependencia circular" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" # Append your own errors here or at the model/attributes scope. diff --git a/config/locales/eu.yml b/config/locales/eu.yml index 80fcc63d0..236c5bb7d 100644 --- a/config/locales/eu.yml +++ b/config/locales/eu.yml @@ -121,6 +121,7 @@ eu: greater_than_start_date: "hasiera data baino handiagoa izan behar du" not_same_project: "ez dago proiektu berdinean" circular_dependency: "Erlazio honek mendekotasun zirkular bat sortuko luke" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: Hautatu mesedez diff --git a/config/locales/fi.yml b/config/locales/fi.yml index 3bde5975b..9aeee5f6e 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -147,7 +147,7 @@ fi: greater_than_start_date: "tulee olla aloituspäivän jälkeinen" not_same_project: "ei kuulu samaan projektiin" circular_dependency: "Tämä suhde loisi kehän." - + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: Valitse, ole hyvä diff --git a/config/locales/gl.yml b/config/locales/gl.yml index 7daebd074..db64fabdc 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -150,6 +150,7 @@ gl: greater_than_start_date: "debe ser posterior á data de comezo" not_same_project: "non pertence ao mesmo proxecto" circular_dependency: "Esta relación podería crear unha dependencia circular" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: Por favor seleccione diff --git a/config/locales/he.yml b/config/locales/he.yml index 59fa6888d..50f31c294 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -123,6 +123,7 @@ he: greater_than_start_date: "חייב להיות מאוחר יותר מתאריך ההתחלה" not_same_project: "לא שייך לאותו הפרויקט" circular_dependency: "הקשר הזה יצור תלות מעגלית" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: בחר בבקשה diff --git a/config/locales/hr.yml b/config/locales/hr.yml index 1c6d523c3..5727a629e 100644 --- a/config/locales/hr.yml +++ b/config/locales/hr.yml @@ -117,6 +117,7 @@ hr: greater_than_start_date: "mora biti veci nego pocetni datum" not_same_project: "ne pripada istom projektu" circular_dependency: "Ovaj relacija stvara kružnu ovisnost" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: Molimo odaberite diff --git a/config/locales/hu.yml b/config/locales/hu.yml index 974ddf76c..c895f4c96 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -143,6 +143,7 @@ greater_than_start_date: "nagyobbnak kell lennie, mint az indítás dátuma" not_same_project: "nem azonos projekthez tartozik" circular_dependency: "Ez a kapcsolat egy körkörös függőséget eredményez" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: Kérem válasszon diff --git a/config/locales/id.yml b/config/locales/id.yml index 345fba454..65140c2d0 100644 --- a/config/locales/id.yml +++ b/config/locales/id.yml @@ -119,6 +119,7 @@ id: greater_than_start_date: "harus lebih besar dari tanggal mulai" not_same_project: "tidak tergabung dalam proyek yang sama" circular_dependency: "kaitan ini akan menghasilkan circular dependency" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: Silakan pilih diff --git a/config/locales/it.yml b/config/locales/it.yml index f5fb21fd2..34a7820d2 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -126,6 +126,7 @@ it: greater_than_start_date: "deve essere maggiore della data di partenza" not_same_project: "non appartiene allo stesso progetto" circular_dependency: "Questa relazione creerebbe una dipendenza circolare" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: Scegli diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 50ccc8729..68c4d24aa 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -144,6 +144,7 @@ ja: greater_than_start_date: "を開始日より後にしてください" not_same_project: "同じプロジェクトに属していません" circular_dependency: "この関係では、循環依存になります" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: 選んでください diff --git a/config/locales/ko.yml b/config/locales/ko.yml index b30d7518f..0a5fa44e1 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -173,6 +173,7 @@ ko: greater_than_start_date: "는 시작날짜보다 커야 합니다" not_same_project: "는 같은 프로젝트에 속해 있지 않습니다" circular_dependency: "이 관계는 순환 의존관계를 만들 수 있습니다" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: 선택하세요 diff --git a/config/locales/lt.yml b/config/locales/lt.yml index f0c9551a3..9a3684e91 100644 --- a/config/locales/lt.yml +++ b/config/locales/lt.yml @@ -143,7 +143,7 @@ lt: other: "Išsaugant objektą {{model}} rastos {{count}} klaidos" body: "Šiuose laukuose yra klaidų:" - pranešimus: + messages: inclusion: "nenumatyta reikšmė" exclusion: "užimtas" invalid: "neteisingas" @@ -179,6 +179,7 @@ lt: greater_than_start_date: "turi būti didesnė negu pradžios data" not_same_project: "nepriklauso tam pačiam projektui" circular_dependency: "Šis ryšys sukurtų ciklinę priklausomybę" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: prašom parinkti diff --git a/config/locales/lv.yml b/config/locales/lv.yml index 4b2421fe4..0ec9b4f4f 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -113,6 +113,7 @@ lv: greater_than_start_date: "jābūt vēlākam par sākuma datumu" not_same_project: "nepieder pie tā paša projekta" circular_dependency: "Šī relācija radītu ciklisku atkarību" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: Izvēlieties diff --git a/config/locales/mn.yml b/config/locales/mn.yml index 55d7cf168..73791bb74 100644 --- a/config/locales/mn.yml +++ b/config/locales/mn.yml @@ -117,6 +117,7 @@ mn: greater_than_start_date: "must be greater than start date" not_same_project: "нэг ижил төсөлд хамаарахгүй байна" circular_dependency: "Энэ харьцаа нь гинжин(рекурсив) харьцаа үүсгэх юм байна" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: Сонгоно уу diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 6dfe989b5..c55dadb47 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -116,6 +116,7 @@ nl: greater_than_start_date: "moet na de startdatum liggen" not_same_project: "hoort niet bij hetzelfde project" circular_dependency: "Deze relatie zou een circulaire afhankelijkheid tot gevolg hebben" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: Selecteer diff --git a/config/locales/no.yml b/config/locales/no.yml index 8845d12b8..b1746fd41 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -111,6 +111,7 @@ greater_than_start_date: "må være større enn startdato" not_same_project: "hører ikke til samme prosjekt" circular_dependency: "Denne relasjonen ville lagd en sirkulær avhengighet" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: Vennligst velg diff --git a/config/locales/pl.yml b/config/locales/pl.yml index f36bb1277..d48b3d935 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -129,6 +129,7 @@ pl: greater_than_start_date: "musi być większe niż początkowa data" not_same_project: "nie należy do tego samego projektu" circular_dependency: "Ta relacja może wytworzyć kołową zależność" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" support: array: diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index d5b54e509..c41e4e0dc 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -143,6 +143,7 @@ pt-BR: greater_than_start_date: "deve ser maior que a data inicial" not_same_project: "não pertence ao mesmo projeto" circular_dependency: "Esta relação geraria uma dependência circular" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: Selecione diff --git a/config/locales/pt.yml b/config/locales/pt.yml index b2a2c08c8..3127f4044 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -128,6 +128,7 @@ pt: greater_than_start_date: "deve ser maior que a data inicial" not_same_project: "não pertence ao mesmo projecto" circular_dependency: "Esta relação iria criar uma dependência circular" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" ## Translated by: Pedro Araújo actionview_instancetag_blank_option: Seleccione diff --git a/config/locales/ro.yml b/config/locales/ro.yml index fd6e2b942..a0458932c 100644 --- a/config/locales/ro.yml +++ b/config/locales/ro.yml @@ -110,6 +110,7 @@ ro: greater_than_start_date: "trebuie să fie după data de început" not_same_project: "trebuie să aparțină aceluiași proiect" circular_dependency: "Această relație ar crea o dependență circulară" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: Selectați diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 913c81c81..586e2fbcc 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -192,11 +192,12 @@ ru: equal_to: "может иметь лишь значение, равное {{count}}" less_than: "может иметь значение меньшее чем {{count}}" less_than_or_equal_to: "может иметь значение меньшее или равное {{count}}" - odd: "может иметь лишь четное значение" - even: "может иметь лишь нечетное значение" + odd: "может иметь лишь нечетное значение" + even: "может иметь лишь четное значение" greater_than_start_date: "должна быть позднее даты начала" - not_same_project: "не относятся к одному проекту" + not_same_project: "не относится к одному проекту" circular_dependency: "Такая связь приведет к циклической зависимости" + cant_link_an_issue_with_a_descendant: "Задача не может быть связана со своей подзадачей" support: array: diff --git a/config/locales/sk.yml b/config/locales/sk.yml index 94facc047..52c57c79c 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -112,6 +112,7 @@ sk: greater_than_start_date: "musí byť neskôr ako počiatočný dátum" not_same_project: "nepatrí rovnakému projektu" circular_dependency: "Tento vzťah by vytvoril cyklickú závislosť" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" # SK translation by Stanislav Pach | stano.pach@seznam.cz diff --git a/config/locales/sl.yml b/config/locales/sl.yml index 7307d0d93..8ee68f15d 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -116,6 +116,7 @@ sl: greater_than_start_date: "mora biti kasnejši kot začeten datum" not_same_project: "ne pripada istemu projektu" circular_dependency: "Ta odnos bi povzročil krožno odvisnost" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: Prosimo izberite diff --git a/config/locales/sv.yml b/config/locales/sv.yml index e02ee7867..657a9adcd 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -130,6 +130,7 @@ sv: greater_than_start_date: "måste vara senare än startdatumet" not_same_project: "tillhör inte samma projekt" circular_dependency: "Denna relation skulle skapa ett cirkulärt beroende" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" direction: ltr date: diff --git a/config/locales/th.yml b/config/locales/th.yml index 01d19c4d0..67bf3c4cc 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -116,6 +116,7 @@ th: greater_than_start_date: "ต้องมากกว่าวันเริ่ม" not_same_project: "ไม่ได้อยู่ในโครงการเดียวกัน" circular_dependency: "ความสัมพันธ์อ้างอิงเป็นวงกลม" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: กรุณาเลือก diff --git a/config/locales/tr.yml b/config/locales/tr.yml index f3f9788b1..e0f23078d 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -143,6 +143,7 @@ tr: greater_than_start_date: "başlangıç tarihinden büyük olmalı" not_same_project: "aynı projeye ait değil" circular_dependency: "Bu ilişki döngüsel bağımlılık meydana getirecektir" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" models: actionview_instancetag_blank_option: Lütfen Seçin diff --git a/config/locales/uk.yml b/config/locales/uk.yml index c5846db2a..d0191618b 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -116,6 +116,7 @@ uk: greater_than_start_date: "повинна бути пізніша за дату початку" not_same_project: "не відносяться до одного проекту" circular_dependency: "Такий зв'язок приведе до циклічної залежності" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: Оберіть diff --git a/config/locales/vi.yml b/config/locales/vi.yml index 5e964896d..66fdbc115 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -139,6 +139,7 @@ vi: greater_than_start_date: "phải đi sau ngày bắt đầu" not_same_project: "không thuộc cùng dự án" circular_dependency: "quan hệ có thể gây ra lặp vô tận" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" direction: ltr date: diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 0216525b0..56d8c9b29 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -177,6 +177,7 @@ greater_than_start_date: "必須在起始日期之後" not_same_project: "不屬於同一個專案" circular_dependency: "這個關聯會導致環狀相依" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" # You can define own errors for models or model attributes. # The values :model, :attribute and :value are always available for interpolation. diff --git a/config/locales/zh.yml b/config/locales/zh.yml index 0ffdcdb3b..a05d96cae 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -137,6 +137,7 @@ zh: greater_than_start_date: "必须在起始日期之后" not_same_project: "不属于同一个项目" circular_dependency: "此关联将导致循环依赖" + cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" actionview_instancetag_blank_option: 请选择 From db110304105a7758ee2d3be496c91d037f16425f Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Sun, 5 Sep 2010 22:57:20 +0000 Subject: [PATCH 26/72] Refactor: move method to model. (references r4057) git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4062 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/helpers/journals_helper.rb | 7 ------- app/models/journal.rb | 8 ++++++++ app/views/issues/_history.rhtml | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/helpers/journals_helper.rb b/app/helpers/journals_helper.rb index 990bfb1e5..c8d53f253 100644 --- a/app/helpers/journals_helper.rb +++ b/app/helpers/journals_helper.rb @@ -39,11 +39,4 @@ module JournalsHelper onclick = "new Ajax.Request('#{url_for(url)}', {asynchronous:true, evalScripts:true, method:'get'}); return false;" link_to text, '#', options.merge(:onclick => onclick) end - - def css_journal_classes(journal) - s = 'journal' - s << ' has-notes' unless journal.notes.blank? - s << ' has-details' unless journal.details.blank? - s - end end diff --git a/app/models/journal.rb b/app/models/journal.rb index a0e1ae877..3e846aeb8 100644 --- a/app/models/journal.rb +++ b/app/models/journal.rb @@ -65,4 +65,12 @@ class Journal < ActiveRecord::Base def attachments journalized.respond_to?(:attachments) ? journalized.attachments : nil end + + # Returns a string of css classes + def css_classes + s = 'journal' + s << ' has-notes' unless notes.blank? + s << ' has-details' unless details.blank? + s + end end diff --git a/app/views/issues/_history.rhtml b/app/views/issues/_history.rhtml index 4ea2dd2ac..4851e5f22 100644 --- a/app/views/issues/_history.rhtml +++ b/app/views/issues/_history.rhtml @@ -1,6 +1,6 @@ <% reply_links = authorize_for('issues', 'edit') -%> <% for journal in journals %> -
    +

    <%= avatar(journal.user, :size => "24") %> <%= content_tag('a', '', :name => "note-#{journal.indice}")%> From 563c879e44237eff4846ef44ae93ec43c27ba47f Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Mon, 6 Sep 2010 00:26:02 +0000 Subject: [PATCH 27/72] Use the built in Rails ActionView::TestCase for testing helpers. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4063 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- test/unit/helpers/application_helper_test.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/unit/helpers/application_helper_test.rb b/test/unit/helpers/application_helper_test.rb index 6fd21fe37..533311b1a 100644 --- a/test/unit/helpers/application_helper_test.rb +++ b/test/unit/helpers/application_helper_test.rb @@ -17,10 +17,7 @@ require File.dirname(__FILE__) + '/../../test_helper' -class ApplicationHelperTest < HelperTestCase - include ApplicationHelper - include ActionView::Helpers::TextHelper - include ActionView::Helpers::DateHelper +class ApplicationHelperTest < ActionView::TestCase fixtures :projects, :roles, :enabled_modules, :users, :repositories, :changesets, From d771fa92892a8835168856b8a148d05b550440dc Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Mon, 6 Sep 2010 00:26:08 +0000 Subject: [PATCH 28/72] Change link_to_if_authorized to allow url paths. (Fixes #6195) Both url paths (/issues/1234) and params hashes (:controller => 'issues') are now supported by link_to_if_authorized. The authorize_for method requires a controller/action pair so urls need to be parsed against the routes to find their controller/action. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4064 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/helpers/application_helper.rb | 21 +++++++++++++- test/unit/helpers/application_helper_test.rb | 29 ++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 19dd654db..0fb44a22b 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -32,8 +32,27 @@ module ApplicationHelper end # Display a link if user is authorized + # + # @param [String] name Anchor text (passed to link_to) + # @param [Hash, String] options Hash params or url for the link target (passed to link_to). + # This will checked by authorize_for to see if the user is authorized + # @param [optional, Hash] html_options Options passed to link_to + # @param [optional, Hash] parameters_for_method_reference Extra parameters for link_to def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference) - link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action]) + if options.is_a?(String) + begin + route = ActionController::Routing::Routes.recognize_path(options.gsub(/\?.*/,''), :method => options[:method] || :get) + link_controller = route[:controller] + link_action = route[:action] + rescue ActionController::RoutingError # Parse failed, not a route + link_controller, link_action = nil, nil + end + else + link_controller = options[:controller] || params[:controller] + link_action = options[:action] + end + + link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(link_controller, link_action) end # Display a link to remote if user is authorized diff --git a/test/unit/helpers/application_helper_test.rb b/test/unit/helpers/application_helper_test.rb index 533311b1a..1936a981b 100644 --- a/test/unit/helpers/application_helper_test.rb +++ b/test/unit/helpers/application_helper_test.rb @@ -30,6 +30,35 @@ class ApplicationHelperTest < ActionView::TestCase def setup super end + + context "#link_to_if_authorized" do + context "authorized user" do + should "be tested" + end + + context "unauthorized user" do + should "be tested" + end + + should "allow using the :controller and :action for the target link" do + User.current = User.find_by_login('admin') + + @project = Issue.first.project # Used by helper + response = link_to_if_authorized("By controller/action", + {:controller => 'issues', :action => 'edit', :id => Issue.first.id}) + assert_match /href/, response + end + + should "allow using the url for the target link" do + User.current = User.find_by_login('admin') + + @project = Issue.first.project # Used by helper + response = link_to_if_authorized("By url", + new_issue_move_path(:id => Issue.first.id)) + assert_match /href/, response + end + + end def test_auto_links to_test = { From 270b559d362a39a8a9594f5fe6a10804e4653af1 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Mon, 6 Sep 2010 00:48:44 +0000 Subject: [PATCH 29/72] Refocus the related issue field after submitting an issue. #6275 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4065 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/views/issues/_relations.rhtml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/issues/_relations.rhtml b/app/views/issues/_relations.rhtml index 71eaaa673..5b27fa6a5 100644 --- a/app/views/issues/_relations.rhtml +++ b/app/views/issues/_relations.rhtml @@ -28,6 +28,7 @@ <% remote_form_for(:relation, @relation, :url => {:controller => 'issue_relations', :action => 'new', :issue_id => @issue}, :method => :post, + :complete => "Form.Element.focus('relation_issue_to_id');", :html => {:id => 'new-relation-form', :style => (@relation ? '' : 'display: none;')}) do |f| %> <%= render :partial => 'issue_relations/form', :locals => {:f => f}%> <% end %> From 9da4ee5fcce2d20e125acc64377564a2797d1cbb Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Mon, 6 Sep 2010 01:02:52 +0000 Subject: [PATCH 30/72] Allow user password changes when changing to Internal authentication. #6267 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4066 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/users_controller.rb | 4 +++- test/functional/users_controller_test.rb | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 0354d165d..b854850a3 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -95,7 +95,9 @@ class UsersController < ApplicationController if request.post? @user.admin = params[:user][:admin] if params[:user][:admin] @user.login = params[:user][:login] if params[:user][:login] - @user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless params[:password].nil? or params[:password].empty? or @user.auth_source_id + if params[:password].present? && (@user.auth_source_id.nil? || params[:user][:auth_source_id].blank?) + @user.password, @user.password_confirmation = params[:password], params[:password_confirmation] + end @user.group_ids = params[:user][:group_ids] if params[:user][:group_ids] @user.attributes = params[:user] # Was the account actived ? (do it before User#save clears the change) diff --git a/test/functional/users_controller_test.rb b/test/functional/users_controller_test.rb index 640ce8685..0e4c14c79 100644 --- a/test/functional/users_controller_test.rb +++ b/test/functional/users_controller_test.rb @@ -143,6 +143,18 @@ class UsersControllerTest < ActionController::TestCase assert_equal [u.mail], mail.bcc assert mail.body.include?('newpass') end + + test "POST :edit with a password change to an AuthSource user switching to Internal authentication" do + # Configure as auth source + u = User.find(2) + u.auth_source = AuthSource.find(1) + u.save! + + post :edit, :id => u.id, :user => {:auth_source_id => ''}, :password => 'newpass', :password_confirmation => 'newpass' + + assert_equal nil, u.reload.auth_source + assert_equal User.hash_password('newpass'), u.reload.hashed_password + end def test_edit_membership post :edit_membership, :id => 2, :membership_id => 1, From 763ab079424eb286d781abb9d8f5adb682b0ce9e Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Mon, 6 Sep 2010 14:53:08 +0000 Subject: [PATCH 31/72] Refactor: split ProjectsController#add into #add (GET) and #create (POST). git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4067 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/projects_controller.rb | 70 +++++++++-------- config/routes.rb | 6 +- lib/redmine.rb | 4 +- test/functional/projects_controller_test.rb | 85 +++++++++++++-------- test/integration/routing_test.rb | 4 +- 5 files changed, 100 insertions(+), 69 deletions(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 90eddd7b2..65f346d5f 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -20,13 +20,13 @@ class ProjectsController < ApplicationController menu_item :roadmap, :only => :roadmap menu_item :settings, :only => :settings - before_filter :find_project, :except => [ :index, :list, :add, :copy ] - before_filter :authorize, :except => [ :index, :list, :add, :copy, :archive, :unarchive, :destroy] - before_filter :authorize_global, :only => :add + before_filter :find_project, :except => [ :index, :list, :add, :create, :copy ] + before_filter :authorize, :except => [ :index, :list, :add, :create, :copy, :archive, :unarchive, :destroy] + before_filter :authorize_global, :only => [:add, :create] before_filter :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ] accept_key_auth :index - after_filter :only => [:add, :edit, :archive, :unarchive, :destroy] do |controller| + after_filter :only => [:create, :edit, :archive, :unarchive, :destroy] do |controller| if controller.request.post? controller.send :expire_action, :controller => 'welcome', :action => 'robots.txt' end @@ -65,35 +65,41 @@ class ProjectsController < ApplicationController @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position") @trackers = Tracker.all @project = Project.new(params[:project]) - if request.get? - @project.identifier = Project.next_identifier if Setting.sequential_project_identifiers? - @project.trackers = Tracker.all - @project.is_public = Setting.default_projects_public? - @project.enabled_module_names = Setting.default_projects_modules - else - @project.enabled_module_names = params[:enabled_modules] - if validate_parent_id && @project.save - @project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id') - # Add current user as a project member if he is not admin - unless User.current.admin? - r = Role.givable.find_by_id(Setting.new_project_user_role_id.to_i) || Role.givable.first - m = Member.new(:user => User.current, :roles => [r]) - @project.members << m - end - respond_to do |format| - format.html { - flash[:notice] = l(:notice_successful_create) - redirect_to :controller => 'projects', :action => 'settings', :id => @project - } - format.xml { head :created, :location => url_for(:controller => 'projects', :action => 'show', :id => @project.id) } - end - else - respond_to do |format| - format.html - format.xml { render :xml => @project.errors, :status => :unprocessable_entity } - end + + @project.identifier = Project.next_identifier if Setting.sequential_project_identifiers? + @project.trackers = Tracker.all + @project.is_public = Setting.default_projects_public? + @project.enabled_module_names = Setting.default_projects_modules + end + + def create + @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position") + @trackers = Tracker.all + @project = Project.new(params[:project]) + + @project.enabled_module_names = params[:enabled_modules] + if validate_parent_id && @project.save + @project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id') + # Add current user as a project member if he is not admin + unless User.current.admin? + r = Role.givable.find_by_id(Setting.new_project_user_role_id.to_i) || Role.givable.first + m = Member.new(:user => User.current, :roles => [r]) + @project.members << m end - end + respond_to do |format| + format.html { + flash[:notice] = l(:notice_successful_create) + redirect_to :controller => 'projects', :action => 'settings', :id => @project + } + format.xml { head :created, :location => url_for(:controller => 'projects', :action => 'show', :id => @project.id) } + end + else + respond_to do |format| + format.html { render :action => 'add' } + format.xml { render :xml => @project.errors, :status => :unprocessable_entity } + end + end + end def copy diff --git a/config/routes.rb b/config/routes.rb index 5448b5f58..8bcdd91d6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -195,9 +195,9 @@ ActionController::Routing::Routes.draw do |map| end projects.with_options :conditions => {:method => :post} do |project_actions| - project_actions.connect 'projects/new', :action => 'add' - project_actions.connect 'projects', :action => 'add' - project_actions.connect 'projects.:format', :action => 'add', :format => /xml/ + project_actions.connect 'projects/new', :action => 'create' + project_actions.connect 'projects', :action => 'create' + project_actions.connect 'projects.:format', :action => 'create', :format => /xml/ project_actions.connect 'projects/:id/:action', :action => /edit|destroy|archive|unarchive/ project_actions.connect 'projects/:id/files/new', :controller => 'files', :action => 'new' project_actions.connect 'projects/:id/activities/save', :controller => 'project_enumerations', :action => 'save' diff --git a/lib/redmine.rb b/lib/redmine.rb index fc750d110..aa15770db 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -46,12 +46,12 @@ end Redmine::AccessControl.map do |map| map.permission :view_project, {:projects => [:show], :activities => [:index]}, :public => true map.permission :search_project, {:search => :index}, :public => true - map.permission :add_project, {:projects => :add}, :require => :loggedin + map.permission :add_project, {:projects => [:add, :create]}, :require => :loggedin map.permission :edit_project, {:projects => [:settings, :edit]}, :require => :member map.permission :select_project_modules, {:projects => :modules}, :require => :member map.permission :manage_members, {:projects => :settings, :members => [:new, :edit, :destroy, :autocomplete_for_member]}, :require => :member map.permission :manage_versions, {:projects => :settings, :versions => [:new, :edit, :close_completed, :destroy]}, :require => :member - map.permission :add_subprojects, {:projects => :add}, :require => :member + map.permission :add_subprojects, {:projects => [:add, :create]}, :require => :member map.project_module :issue_tracking do |map| # Issue categories diff --git a/test/functional/projects_controller_test.rb b/test/functional/projects_controller_test.rb index 4decb060f..6b8c84728 100644 --- a/test/functional/projects_controller_test.rb +++ b/test/functional/projects_controller_test.rb @@ -98,9 +98,53 @@ class ProjectsControllerTest < ActionController::TestCase assert_response :success assert_template 'add' end + + end + + context "by non-admin user with add_project permission" do + setup do + Role.non_member.add_permission! :add_project + @request.session[:user_id] = 9 + end + + should "accept get" do + get :add + assert_response :success + assert_template 'add' + assert_no_tag :select, :attributes => {:name => 'project[parent_id]'} + end + end + + context "by non-admin user with add_subprojects permission" do + setup do + Role.find(1).remove_permission! :add_project + Role.find(1).add_permission! :add_subprojects + @request.session[:user_id] = 2 + end - should "accept post" do - post :add, :project => { :name => "blog", + should "accept get" do + get :add, :parent_id => 'ecookbook' + assert_response :success + assert_template 'add' + # parent project selected + assert_tag :select, :attributes => {:name => 'project[parent_id]'}, + :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}} + # no empty value + assert_no_tag :select, :attributes => {:name => 'project[parent_id]'}, + :child => {:tag => 'option', :attributes => {:value => ''}} + end + end + + end + + context "POST :create" do + context "by admin user" do + setup do + @request.session[:user_id] = 1 + end + + should "create a new project" do + post :create, :project => { :name => "blog", :description => "weblog", :identifier => "blog", :is_public => 1, @@ -115,8 +159,8 @@ class ProjectsControllerTest < ActionController::TestCase assert_nil project.parent end - should "accept post with parent" do - post :add, :project => { :name => "blog", + should "create a new subproject" do + post :create, :project => { :name => "blog", :description => "weblog", :identifier => "blog", :is_public => 1, @@ -137,15 +181,8 @@ class ProjectsControllerTest < ActionController::TestCase @request.session[:user_id] = 9 end - should "accept get" do - get :add - assert_response :success - assert_template 'add' - assert_no_tag :select, :attributes => {:name => 'project[parent_id]'} - end - - should "accept post" do - post :add, :project => { :name => "blog", + should "accept create a Project" do + post :create, :project => { :name => "blog", :description => "weblog", :identifier => "blog", :is_public => 1, @@ -166,7 +203,7 @@ class ProjectsControllerTest < ActionController::TestCase should "fail with parent_id" do assert_no_difference 'Project.count' do - post :add, :project => { :name => "blog", + post :create, :project => { :name => "blog", :description => "weblog", :identifier => "blog", :is_public => 1, @@ -188,20 +225,8 @@ class ProjectsControllerTest < ActionController::TestCase @request.session[:user_id] = 2 end - should "accept get" do - get :add, :parent_id => 'ecookbook' - assert_response :success - assert_template 'add' - # parent project selected - assert_tag :select, :attributes => {:name => 'project[parent_id]'}, - :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}} - # no empty value - assert_no_tag :select, :attributes => {:name => 'project[parent_id]'}, - :child => {:tag => 'option', :attributes => {:value => ''}} - end - - should "accept post with parent_id" do - post :add, :project => { :name => "blog", + should "create a project with a parent_id" do + post :create, :project => { :name => "blog", :description => "weblog", :identifier => "blog", :is_public => 1, @@ -214,7 +239,7 @@ class ProjectsControllerTest < ActionController::TestCase should "fail without parent_id" do assert_no_difference 'Project.count' do - post :add, :project => { :name => "blog", + post :create, :project => { :name => "blog", :description => "weblog", :identifier => "blog", :is_public => 1, @@ -230,7 +255,7 @@ class ProjectsControllerTest < ActionController::TestCase should "fail with unauthorized parent_id" do assert !User.find(2).member_of?(Project.find(6)) assert_no_difference 'Project.count' do - post :add, :project => { :name => "blog", + post :create, :project => { :name => "blog", :description => "weblog", :identifier => "blog", :is_public => 1, diff --git a/test/integration/routing_test.rb b/test/integration/routing_test.rb index 71be6c3c7..e75cf4721 100644 --- a/test/integration/routing_test.rb +++ b/test/integration/routing_test.rb @@ -178,8 +178,8 @@ class RoutingTest < ActionController::IntegrationTest should_route :get, "/projects/33/activity", :controller => 'activities', :action => 'index', :id => '33' should_route :get, "/projects/33/activity.atom", :controller => 'activities', :action => 'index', :id => '33', :format => 'atom' - should_route :post, "/projects/new", :controller => 'projects', :action => 'add' - should_route :post, "/projects.xml", :controller => 'projects', :action => 'add', :format => 'xml' + should_route :post, "/projects/new", :controller => 'projects', :action => 'create' + should_route :post, "/projects.xml", :controller => 'projects', :action => 'create', :format => 'xml' should_route :post, "/projects/4223/edit", :controller => 'projects', :action => 'edit', :id => '4223' should_route :post, "/projects/64/destroy", :controller => 'projects', :action => 'destroy', :id => '64' should_route :post, "/projects/33/files/new", :controller => 'files', :action => 'new', :id => '33' From 06878e50041117baa1b4bde40671ab422dd9bc0a Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Mon, 6 Sep 2010 15:09:52 +0000 Subject: [PATCH 32/72] Change project add form to use #create. (From r4067) git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4068 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/views/projects/add.rhtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/add.rhtml b/app/views/projects/add.rhtml index d2ec5db92..c8a9c7600 100644 --- a/app/views/projects/add.rhtml +++ b/app/views/projects/add.rhtml @@ -1,6 +1,6 @@

    <%=l(:label_project_new)%>

    -<% labelled_tabular_form_for :project, @project, :url => { :action => "add" } do |f| %> +<% labelled_tabular_form_for :project, @project, :url => { :action => "create" } do |f| %> <%= render :partial => 'form', :locals => { :f => f } %>
    <%= l(:label_module_plural) %> From 2295b61cb6fde4187875478170da2ae70f114497 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Tue, 7 Sep 2010 15:00:27 +0000 Subject: [PATCH 33/72] Refactor: rename method ProjectsController#add to ProjectsController#new git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4069 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/projects_controller.rb | 11 +++++------ app/views/admin/projects.rhtml | 2 +- app/views/projects/index.rhtml | 2 +- app/views/projects/{add.rhtml => new.html.erb} | 0 config/routes.rb | 2 +- lib/redmine.rb | 4 ++-- test/functional/projects_controller_test.rb | 14 +++++++------- test/integration/routing_test.rb | 2 +- 8 files changed, 18 insertions(+), 19 deletions(-) rename app/views/projects/{add.rhtml => new.html.erb} (100%) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 65f346d5f..376034097 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -20,9 +20,9 @@ class ProjectsController < ApplicationController menu_item :roadmap, :only => :roadmap menu_item :settings, :only => :settings - before_filter :find_project, :except => [ :index, :list, :add, :create, :copy ] - before_filter :authorize, :except => [ :index, :list, :add, :create, :copy, :archive, :unarchive, :destroy] - before_filter :authorize_global, :only => [:add, :create] + before_filter :find_project, :except => [ :index, :list, :new, :create, :copy ] + before_filter :authorize, :except => [ :index, :list, :new, :create, :copy, :archive, :unarchive, :destroy] + before_filter :authorize_global, :only => [:new, :create] before_filter :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ] accept_key_auth :index @@ -60,8 +60,7 @@ class ProjectsController < ApplicationController end end - # Add a new project - def add + def new @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position") @trackers = Tracker.all @project = Project.new(params[:project]) @@ -95,7 +94,7 @@ class ProjectsController < ApplicationController end else respond_to do |format| - format.html { render :action => 'add' } + format.html { render :action => 'new' } format.xml { render :xml => @project.errors, :status => :unprocessable_entity } end end diff --git a/app/views/admin/projects.rhtml b/app/views/admin/projects.rhtml index 46b68e4cc..b37fec57b 100644 --- a/app/views/admin/projects.rhtml +++ b/app/views/admin/projects.rhtml @@ -1,5 +1,5 @@
    -<%= link_to l(:label_project_new), {:controller => 'projects', :action => 'add'}, :class => 'icon icon-add' %> +<%= link_to l(:label_project_new), {:controller => 'projects', :action => 'new'}, :class => 'icon icon-add' %>

    <%=l(:label_project_plural)%>

    diff --git a/app/views/projects/index.rhtml b/app/views/projects/index.rhtml index a2ba1c389..2b506388a 100644 --- a/app/views/projects/index.rhtml +++ b/app/views/projects/index.rhtml @@ -3,7 +3,7 @@ <% end %>
    - <%= link_to(l(:label_project_new), {:controller => 'projects', :action => 'add'}, :class => 'icon icon-add') + ' |' if User.current.allowed_to?(:add_project, nil, :global => true) %> + <%= link_to(l(:label_project_new), {:controller => 'projects', :action => 'new'}, :class => 'icon icon-add') + ' |' if User.current.allowed_to?(:add_project, nil, :global => true) %> <%= link_to(l(:label_issue_view_all), { :controller => 'issues' }) + ' |' if User.current.allowed_to?(:view_issues, nil, :global => true) %> <%= link_to(l(:label_overall_spent_time), { :controller => 'time_entries' }) + ' |' if User.current.allowed_to?(:view_time_entries, nil, :global => true) %> <%= link_to l(:label_overall_activity), { :controller => 'activities', :action => 'index' }%> diff --git a/app/views/projects/add.rhtml b/app/views/projects/new.html.erb similarity index 100% rename from app/views/projects/add.rhtml rename to app/views/projects/new.html.erb diff --git a/config/routes.rb b/config/routes.rb index 8bcdd91d6..7f971da7c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -177,7 +177,7 @@ ActionController::Routing::Routes.draw do |map| projects.with_options :conditions => {:method => :get} do |project_views| project_views.connect 'projects', :action => 'index' project_views.connect 'projects.:format', :action => 'index' - project_views.connect 'projects/new', :action => 'add' + project_views.connect 'projects/new', :action => 'new' project_views.connect 'projects/:id', :action => 'show' project_views.connect 'projects/:id.:format', :action => 'show' project_views.connect 'projects/:id/:action', :action => /destroy|settings/ diff --git a/lib/redmine.rb b/lib/redmine.rb index aa15770db..dcddeb902 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -46,12 +46,12 @@ end Redmine::AccessControl.map do |map| map.permission :view_project, {:projects => [:show], :activities => [:index]}, :public => true map.permission :search_project, {:search => :index}, :public => true - map.permission :add_project, {:projects => [:add, :create]}, :require => :loggedin + map.permission :add_project, {:projects => [:new, :create]}, :require => :loggedin map.permission :edit_project, {:projects => [:settings, :edit]}, :require => :member map.permission :select_project_modules, {:projects => :modules}, :require => :member map.permission :manage_members, {:projects => :settings, :members => [:new, :edit, :destroy, :autocomplete_for_member]}, :require => :member map.permission :manage_versions, {:projects => :settings, :versions => [:new, :edit, :close_completed, :destroy]}, :require => :member - map.permission :add_subprojects, {:projects => [:add, :create]}, :require => :member + map.permission :add_subprojects, {:projects => [:new, :create]}, :require => :member map.project_module :issue_tracking do |map| # Issue categories diff --git a/test/functional/projects_controller_test.rb b/test/functional/projects_controller_test.rb index 6b8c84728..e661ca29e 100644 --- a/test/functional/projects_controller_test.rb +++ b/test/functional/projects_controller_test.rb @@ -87,16 +87,16 @@ class ProjectsControllerTest < ActionController::TestCase end end - context "#add" do + context "#new" do context "by admin user" do setup do @request.session[:user_id] = 1 end should "accept get" do - get :add + get :new assert_response :success - assert_template 'add' + assert_template 'new' end end @@ -108,9 +108,9 @@ class ProjectsControllerTest < ActionController::TestCase end should "accept get" do - get :add + get :new assert_response :success - assert_template 'add' + assert_template 'new' assert_no_tag :select, :attributes => {:name => 'project[parent_id]'} end end @@ -123,9 +123,9 @@ class ProjectsControllerTest < ActionController::TestCase end should "accept get" do - get :add, :parent_id => 'ecookbook' + get :new, :parent_id => 'ecookbook' assert_response :success - assert_template 'add' + assert_template 'new' # parent project selected assert_tag :select, :attributes => {:name => 'project[parent_id]'}, :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}} diff --git a/test/integration/routing_test.rb b/test/integration/routing_test.rb index e75cf4721..f43dc8a61 100644 --- a/test/integration/routing_test.rb +++ b/test/integration/routing_test.rb @@ -166,7 +166,7 @@ class RoutingTest < ActionController::IntegrationTest should_route :get, "/projects", :controller => 'projects', :action => 'index' should_route :get, "/projects.atom", :controller => 'projects', :action => 'index', :format => 'atom' should_route :get, "/projects.xml", :controller => 'projects', :action => 'index', :format => 'xml' - should_route :get, "/projects/new", :controller => 'projects', :action => 'add' + should_route :get, "/projects/new", :controller => 'projects', :action => 'new' should_route :get, "/projects/test", :controller => 'projects', :action => 'show', :id => 'test' should_route :get, "/projects/1.xml", :controller => 'projects', :action => 'show', :id => '1', :format => 'xml' should_route :get, "/projects/4223/settings", :controller => 'projects', :action => 'settings', :id => '4223' From bf7476af5b6b2ec70e20254c8f2fe346ca22c089 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Wed, 8 Sep 2010 16:01:51 +0000 Subject: [PATCH 34/72] Refactor: split method ProjectsController#edit to ProjectsController#update. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4070 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/projects_controller.rb | 48 +++++++++++---------- app/views/projects/_edit.rhtml | 2 +- config/routes.rb | 5 ++- lib/redmine.rb | 2 +- test/functional/projects_controller_test.rb | 4 +- test/integration/routing_test.rb | 4 +- 6 files changed, 34 insertions(+), 31 deletions(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 376034097..f16349329 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -25,13 +25,16 @@ class ProjectsController < ApplicationController before_filter :authorize_global, :only => [:new, :create] before_filter :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ] accept_key_auth :index - - after_filter :only => [:create, :edit, :archive, :unarchive, :destroy] do |controller| + + after_filter :only => [:create, :edit, :update, :archive, :unarchive, :destroy] do |controller| if controller.request.post? controller.send :expire_action, :controller => 'welcome', :action => 'robots.txt' end end - + + # TODO: convert to PUT only + verify :method => [:post, :put], :only => :update, :render => {:nothing => true, :status => :method_not_allowed } + helper :sort include SortHelper helper :custom_fields @@ -179,28 +182,27 @@ class ProjectsController < ApplicationController @wiki ||= @project.wiki end - # Edit @project def edit - if request.get? + end + + def update + @project.attributes = params[:project] + if validate_parent_id && @project.save + @project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id') + respond_to do |format| + format.html { + flash[:notice] = l(:notice_successful_update) + redirect_to :action => 'settings', :id => @project + } + format.xml { head :ok } + end else - @project.attributes = params[:project] - if validate_parent_id && @project.save - @project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id') - respond_to do |format| - format.html { - flash[:notice] = l(:notice_successful_update) - redirect_to :action => 'settings', :id => @project - } - format.xml { head :ok } - end - else - respond_to do |format| - format.html { - settings - render :action => 'settings' - } - format.xml { render :xml => @project.errors, :status => :unprocessable_entity } - end + respond_to do |format| + format.html { + settings + render :action => 'settings' + } + format.xml { render :xml => @project.errors, :status => :unprocessable_entity } end end end diff --git a/app/views/projects/_edit.rhtml b/app/views/projects/_edit.rhtml index b7c2987d2..dc667f267 100644 --- a/app/views/projects/_edit.rhtml +++ b/app/views/projects/_edit.rhtml @@ -1,4 +1,4 @@ -<% labelled_tabular_form_for :project, @project, :url => { :action => "edit", :id => @project } do |f| %> +<% labelled_tabular_form_for :project, @project, :url => { :action => "update", :id => @project } do |f| %> <%= render :partial => 'form', :locals => { :f => f } %> <%= submit_tag l(:button_save) %> <% end %> diff --git a/config/routes.rb b/config/routes.rb index 7f971da7c..01d6a5062 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -198,13 +198,14 @@ ActionController::Routing::Routes.draw do |map| project_actions.connect 'projects/new', :action => 'create' project_actions.connect 'projects', :action => 'create' project_actions.connect 'projects.:format', :action => 'create', :format => /xml/ - project_actions.connect 'projects/:id/:action', :action => /edit|destroy|archive|unarchive/ + project_actions.connect 'projects/:id/edit', :action => 'update' + project_actions.connect 'projects/:id/:action', :action => /destroy|archive|unarchive/ project_actions.connect 'projects/:id/files/new', :controller => 'files', :action => 'new' project_actions.connect 'projects/:id/activities/save', :controller => 'project_enumerations', :action => 'save' end projects.with_options :conditions => {:method => :put} do |project_actions| - project_actions.conditions 'projects/:id.:format', :action => 'edit', :format => /xml/ + project_actions.conditions 'projects/:id.:format', :action => 'update', :format => /xml/ end projects.with_options :conditions => {:method => :delete} do |project_actions| diff --git a/lib/redmine.rb b/lib/redmine.rb index dcddeb902..534f85875 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -47,7 +47,7 @@ Redmine::AccessControl.map do |map| map.permission :view_project, {:projects => [:show], :activities => [:index]}, :public => true map.permission :search_project, {:search => :index}, :public => true map.permission :add_project, {:projects => [:new, :create]}, :require => :loggedin - map.permission :edit_project, {:projects => [:settings, :edit]}, :require => :member + map.permission :edit_project, {:projects => [:settings, :edit, :update]}, :require => :member map.permission :select_project_modules, {:projects => :modules}, :require => :member map.permission :manage_members, {:projects => :settings, :members => [:new, :edit, :destroy, :autocomplete_for_member]}, :require => :member map.permission :manage_versions, {:projects => :settings, :versions => [:new, :edit, :close_completed, :destroy]}, :require => :member diff --git a/test/functional/projects_controller_test.rb b/test/functional/projects_controller_test.rb index e661ca29e..636cb1751 100644 --- a/test/functional/projects_controller_test.rb +++ b/test/functional/projects_controller_test.rb @@ -318,9 +318,9 @@ class ProjectsControllerTest < ActionController::TestCase assert_template 'settings' end - def test_edit + def test_update @request.session[:user_id] = 2 # manager - post :edit, :id => 1, :project => {:name => 'Test changed name', + post :update, :id => 1, :project => {:name => 'Test changed name', :issue_custom_field_ids => ['']} assert_redirected_to 'projects/ecookbook/settings' project = Project.find(1) diff --git a/test/integration/routing_test.rb b/test/integration/routing_test.rb index f43dc8a61..0db45ce02 100644 --- a/test/integration/routing_test.rb +++ b/test/integration/routing_test.rb @@ -180,14 +180,14 @@ class RoutingTest < ActionController::IntegrationTest should_route :post, "/projects/new", :controller => 'projects', :action => 'create' should_route :post, "/projects.xml", :controller => 'projects', :action => 'create', :format => 'xml' - should_route :post, "/projects/4223/edit", :controller => 'projects', :action => 'edit', :id => '4223' + should_route :post, "/projects/4223/edit", :controller => 'projects', :action => 'update', :id => '4223' should_route :post, "/projects/64/destroy", :controller => 'projects', :action => 'destroy', :id => '64' should_route :post, "/projects/33/files/new", :controller => 'files', :action => 'new', :id => '33' should_route :post, "/projects/64/archive", :controller => 'projects', :action => 'archive', :id => '64' should_route :post, "/projects/64/unarchive", :controller => 'projects', :action => 'unarchive', :id => '64' should_route :post, "/projects/64/activities/save", :controller => 'project_enumerations', :action => 'save', :id => '64' - should_route :put, "/projects/1.xml", :controller => 'projects', :action => 'edit', :id => '1', :format => 'xml' + should_route :put, "/projects/1.xml", :controller => 'projects', :action => 'update', :id => '1', :format => 'xml' should_route :delete, "/projects/1.xml", :controller => 'projects', :action => 'destroy', :id => '1', :format => 'xml' should_route :delete, "/projects/64/reset_activities", :controller => 'project_enumerations', :action => 'destroy', :id => '64' From 8d52608dbad63d504ec4b48ffe5ea09cfbe95bd9 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Thu, 9 Sep 2010 18:57:21 +0000 Subject: [PATCH 35/72] Refactor: convert the Projects routes to resources. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4071 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/views/admin/projects.rhtml | 2 +- app/views/projects/_edit.rhtml | 2 +- app/views/projects/destroy.rhtml | 2 +- config/routes.rb | 55 +++++++++++++++----------------- test/integration/routing_test.rb | 7 ++-- 5 files changed, 32 insertions(+), 36 deletions(-) diff --git a/app/views/admin/projects.rhtml b/app/views/admin/projects.rhtml index b37fec57b..6cf933d11 100644 --- a/app/views/admin/projects.rhtml +++ b/app/views/admin/projects.rhtml @@ -35,7 +35,7 @@ <%= link_to(l(:button_archive), { :controller => 'projects', :action => 'archive', :id => project, :status => params[:status] }, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-lock') if project.active? %> <%= link_to(l(:button_unarchive), { :controller => 'projects', :action => 'unarchive', :id => project, :status => params[:status] }, :method => :post, :class => 'icon icon-unlock') if !project.active? && (project.parent.nil? || project.parent.active?) %> <%= link_to(l(:button_copy), { :controller => 'projects', :action => 'copy', :id => project }, :class => 'icon icon-copy') %> - <%= link_to(l(:button_delete), { :controller => 'projects', :action => 'destroy', :id => project }, :class => 'icon icon-del') %> + <%= link_to(l(:button_delete), project_destroy_confirm_path(project), :class => 'icon icon-del') %> <% end %> diff --git a/app/views/projects/_edit.rhtml b/app/views/projects/_edit.rhtml index dc667f267..2ecc822df 100644 --- a/app/views/projects/_edit.rhtml +++ b/app/views/projects/_edit.rhtml @@ -1,4 +1,4 @@ -<% labelled_tabular_form_for :project, @project, :url => { :action => "update", :id => @project } do |f| %> +<% labelled_tabular_form_for :project, @project, :url => project_path(@project), :html => {:method => (@project.new_record? ? :post : :put) } do |f| %> <%= render :partial => 'form', :locals => { :f => f } %> <%= submit_tag l(:button_save) %> <% end %> diff --git a/app/views/projects/destroy.rhtml b/app/views/projects/destroy.rhtml index 09d7d2a1c..23844ec67 100644 --- a/app/views/projects/destroy.rhtml +++ b/app/views/projects/destroy.rhtml @@ -8,7 +8,7 @@ <% end %>

    - <% form_tag({:controller => 'projects', :action => 'destroy', :id => @project_to_destroy}) do %> + <% form_tag(project_path(@project_to_destroy), :method => :delete) do %> <%= submit_tag l(:button_delete) %> <% end %> diff --git a/config/routes.rb b/config/routes.rb index 01d6a5062..9f12cd454 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -172,48 +172,45 @@ ActionController::Routing::Routes.draw do |map| user_actions.connect 'users/:id/memberships/:membership_id/destroy', :action => 'destroy_membership' end end - - map.with_options :controller => 'projects' do |projects| - projects.with_options :conditions => {:method => :get} do |project_views| - project_views.connect 'projects', :action => 'index' - project_views.connect 'projects.:format', :action => 'index' - project_views.connect 'projects/new', :action => 'new' - project_views.connect 'projects/:id', :action => 'show' - project_views.connect 'projects/:id.:format', :action => 'show' - project_views.connect 'projects/:id/:action', :action => /destroy|settings/ + + map.resources :projects, :member => { + :copy => [:get, :post], + :settings => :get, + :modules => :post, + :archive => :post, + :unarchive => :post + } + + # Destroy uses a get request to prompt the user before the actual DELETE request + map.project_destroy_confirm 'projects/:id/destroy', :controller => 'projects', :action => 'destroy', :conditions => {:method => :get} + + # TODO: port to be part of the resources route(s) + map.with_options :controller => 'projects' do |project_mapper| + project_mapper.with_options :conditions => {:method => :get} do |project_views| project_views.connect 'projects/:id/files', :controller => 'files', :action => 'index' project_views.connect 'projects/:id/files/new', :controller => 'files', :action => 'new' - project_views.connect 'projects/:id/settings/:tab', :action => 'settings' + project_views.connect 'projects/:id/settings/:tab', :controller => 'projects', :action => 'settings' project_views.connect 'projects/:project_id/issues/:copy_from/copy', :controller => 'issues', :action => 'new' end - projects.with_options :controller => 'activities', :action => 'index', :conditions => {:method => :get} do |activity| - activity.connect 'projects/:id/activity' - activity.connect 'projects/:id/activity.:format' - activity.connect 'activity', :id => nil - activity.connect 'activity.:format', :id => nil - end - - projects.with_options :conditions => {:method => :post} do |project_actions| - project_actions.connect 'projects/new', :action => 'create' - project_actions.connect 'projects', :action => 'create' - project_actions.connect 'projects.:format', :action => 'create', :format => /xml/ - project_actions.connect 'projects/:id/edit', :action => 'update' - project_actions.connect 'projects/:id/:action', :action => /destroy|archive|unarchive/ + project_mapper.with_options :conditions => {:method => :post} do |project_actions| project_actions.connect 'projects/:id/files/new', :controller => 'files', :action => 'new' project_actions.connect 'projects/:id/activities/save', :controller => 'project_enumerations', :action => 'save' end - projects.with_options :conditions => {:method => :put} do |project_actions| - project_actions.conditions 'projects/:id.:format', :action => 'update', :format => /xml/ - end - - projects.with_options :conditions => {:method => :delete} do |project_actions| - project_actions.conditions 'projects/:id.:format', :action => 'destroy', :format => /xml/ + project_mapper.with_options :conditions => {:method => :delete} do |project_actions| project_actions.conditions 'projects/:id/reset_activities', :controller => 'project_enumerations', :action => 'destroy' end + end + map.with_options :controller => 'activities', :action => 'index', :conditions => {:method => :get} do |activity| + activity.connect 'projects/:id/activity' + activity.connect 'projects/:id/activity.:format' + activity.connect 'activity', :id => nil + activity.connect 'activity.:format', :id => nil + end + map.with_options :controller => 'versions' do |versions| versions.connect 'projects/:project_id/versions/new', :action => 'new' versions.connect 'projects/:project_id/roadmap', :action => 'index' diff --git a/test/integration/routing_test.rb b/test/integration/routing_test.rb index 0db45ce02..5cd0b2d39 100644 --- a/test/integration/routing_test.rb +++ b/test/integration/routing_test.rb @@ -171,24 +171,23 @@ class RoutingTest < ActionController::IntegrationTest should_route :get, "/projects/1.xml", :controller => 'projects', :action => 'show', :id => '1', :format => 'xml' should_route :get, "/projects/4223/settings", :controller => 'projects', :action => 'settings', :id => '4223' should_route :get, "/projects/4223/settings/members", :controller => 'projects', :action => 'settings', :id => '4223', :tab => 'members' - should_route :get, "/projects/567/destroy", :controller => 'projects', :action => 'destroy', :id => '567' should_route :get, "/projects/33/files", :controller => 'files', :action => 'index', :id => '33' should_route :get, "/projects/33/files/new", :controller => 'files', :action => 'new', :id => '33' should_route :get, "/projects/33/roadmap", :controller => 'versions', :action => 'index', :project_id => '33' should_route :get, "/projects/33/activity", :controller => 'activities', :action => 'index', :id => '33' should_route :get, "/projects/33/activity.atom", :controller => 'activities', :action => 'index', :id => '33', :format => 'atom' - should_route :post, "/projects/new", :controller => 'projects', :action => 'create' + should_route :post, "/projects", :controller => 'projects', :action => 'create' should_route :post, "/projects.xml", :controller => 'projects', :action => 'create', :format => 'xml' - should_route :post, "/projects/4223/edit", :controller => 'projects', :action => 'update', :id => '4223' - should_route :post, "/projects/64/destroy", :controller => 'projects', :action => 'destroy', :id => '64' should_route :post, "/projects/33/files/new", :controller => 'files', :action => 'new', :id => '33' should_route :post, "/projects/64/archive", :controller => 'projects', :action => 'archive', :id => '64' should_route :post, "/projects/64/unarchive", :controller => 'projects', :action => 'unarchive', :id => '64' should_route :post, "/projects/64/activities/save", :controller => 'project_enumerations', :action => 'save', :id => '64' + should_route :put, "/projects/4223", :controller => 'projects', :action => 'update', :id => '4223' should_route :put, "/projects/1.xml", :controller => 'projects', :action => 'update', :id => '1', :format => 'xml' + should_route :delete, "/projects/64", :controller => 'projects', :action => 'destroy', :id => '64' should_route :delete, "/projects/1.xml", :controller => 'projects', :action => 'destroy', :id => '1', :format => 'xml' should_route :delete, "/projects/64/reset_activities", :controller => 'project_enumerations', :action => 'destroy', :id => '64' end From bdb3937e0f4c8faceb463e23cb28676930ddbd9e Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Fri, 10 Sep 2010 03:09:02 +0000 Subject: [PATCH 36/72] Rewrite the Gantt chart. #6276 This version of the Gantt chart supports nested charts. So Projects, Versions, and Issues will be nested underneath their parents correctly. Additional features: * Move all Gantt code to Redmine::Helpers::Gantt class instead of having it in the Gantt class, controller, and view * Recursive and nest sub-projects * Recursive and nest versions * Recursive and nest issues * Draw a line showing when a Project is active and it's progress * Draw a line showing when a Version is active and it's progress * Show a version's % complete * Change the color of Projects, Versions, and Issues if they are late or behind schedule * Added Project#start_date and #due_date * Added Project#completed_percent * Use a mini-gravatar on the Gantt chart * Added tests for the Gantt rendering git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4072 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/gantts_controller.rb | 24 +- app/controllers/issues_controller.rb | 1 + app/helpers/application_helper.rb | 7 + app/helpers/gantt_helper.rb | 24 + app/helpers/issues_helper.rb | 4 +- app/models/issue.rb | 27 +- app/models/project.rb | 44 + app/models/version.rb | 12 + app/views/gantts/show.html.erb | 80 +- lib/redmine/export/pdf.rb | 190 +--- lib/redmine/helpers/gantt.rb | 895 ++++++++++++++++-- public/images/milestone.png | Bin 122 -> 0 bytes public/images/milestone_done.png | Bin 0 -> 137 bytes public/images/milestone_late.png | Bin 0 -> 160 bytes public/images/milestone_todo.png | Bin 0 -> 155 bytes public/images/project_marker.png | Bin 0 -> 204 bytes public/images/task_done.png | Bin 855 -> 137 bytes public/images/version_marker.png | Bin 0 -> 174 bytes public/stylesheets/application.css | 23 +- test/functional/gantts_controller_test.rb | 16 +- test/object_daddy_helpers.rb | 3 +- test/unit/helpers/application_helper_test.rb | 2 +- test/unit/issue_test.rb | 22 + test/unit/lib/redmine/helpers/gantt_test.rb | 703 ++++++++++++++ test/unit/project_test.rb | 118 +++ test/unit/version_test.rb | 52 +- vendor/plugins/gravatar/Rakefile | 2 +- vendor/plugins/gravatar/lib/gravatar.rb | 9 +- vendor/plugins/gravatar/spec/gravatar_spec.rb | 24 +- 29 files changed, 1879 insertions(+), 403 deletions(-) create mode 100644 app/helpers/gantt_helper.rb delete mode 100644 public/images/milestone.png create mode 100644 public/images/milestone_done.png create mode 100644 public/images/milestone_late.png create mode 100644 public/images/milestone_todo.png create mode 100644 public/images/project_marker.png create mode 100644 public/images/version_marker.png create mode 100644 test/unit/lib/redmine/helpers/gantt_test.rb diff --git a/app/controllers/gantts_controller.rb b/app/controllers/gantts_controller.rb index 6a6071e86..50fd8c13d 100644 --- a/app/controllers/gantts_controller.rb +++ b/app/controllers/gantts_controller.rb @@ -4,6 +4,7 @@ class GanttsController < ApplicationController rescue_from Query::StatementInvalid, :with => :query_statement_invalid + helper :gantt helper :issues helper :projects helper :queries @@ -14,32 +15,17 @@ class GanttsController < ApplicationController def show @gantt = Redmine::Helpers::Gantt.new(params) + @gantt.project = @project retrieve_query @query.group_by = nil - if @query.valid? - events = [] - # Issues that have start and due dates - events += @query.issues(:include => [:tracker, :assigned_to, :priority], - :order => "start_date, due_date", - :conditions => ["(((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?) or (start_date?)) and start_date is not null and due_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to] - ) - # Issues that don't have a due date but that are assigned to a version with a date - events += @query.issues(:include => [:tracker, :assigned_to, :priority, :fixed_version], - :order => "start_date, effective_date", - :conditions => ["(((start_date>=? and start_date<=?) or (effective_date>=? and effective_date<=?) or (start_date?)) and start_date is not null and due_date is null and effective_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to] - ) - # Versions - events += @query.versions(:conditions => ["effective_date BETWEEN ? AND ?", @gantt.date_from, @gantt.date_to]) - - @gantt.events = events - end + @gantt.query = @query if @query.valid? basename = (@project ? "#{@project.identifier}-" : '') + 'gantt' respond_to do |format| format.html { render :action => "show", :layout => !request.xhr? } - format.png { send_data(@gantt.to_image(@project), :disposition => 'inline', :type => 'image/png', :filename => "#{basename}.png") } if @gantt.respond_to?('to_image') - format.pdf { send_data(gantt_to_pdf(@gantt, @project), :type => 'application/pdf', :filename => "#{basename}.pdf") } + format.png { send_data(@gantt.to_image, :disposition => 'inline', :type => 'image/png', :filename => "#{basename}.png") } if @gantt.respond_to?('to_image') + format.pdf { send_data(@gantt.to_pdf, :type => 'application/pdf', :filename => "#{basename}.pdf") } end end diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index 0364e307c..9f58cb0a1 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -47,6 +47,7 @@ class IssuesController < ApplicationController include SortHelper include IssuesHelper helper :timelog + helper :gantt include Redmine::Export::PDF verify :method => [:post, :delete], diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 0fb44a22b..34b17c760 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -121,6 +121,11 @@ module ApplicationHelper link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => revision}, :title => l(:label_revision_id, revision)) end + + def link_to_project(project, options={}) + options[:class] ||= 'project' + link_to(h(project), {:controller => 'projects', :action => 'show', :id => project}, :class => options[:class]) + end # Generates a link to a project if active # Examples: @@ -832,6 +837,8 @@ module ApplicationHelper email = $1 end return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil + else + '' end end diff --git a/app/helpers/gantt_helper.rb b/app/helpers/gantt_helper.rb new file mode 100644 index 000000000..38f3765e9 --- /dev/null +++ b/app/helpers/gantt_helper.rb @@ -0,0 +1,24 @@ +# redMine - project management software +# Copyright (C) 2006 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. + +module GanttHelper + def number_of_issues_on_versions(gantt) + versions = gantt.events.collect {|event| (event.is_a? Version) ? event : nil}.compact + + versions.sum {|v| v.fixed_issues.for_gantt.with_query(@query).count} + end +end diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 617822986..284aae91a 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -35,8 +35,10 @@ module IssuesHelper @cached_label_due_date ||= l(:field_due_date) @cached_label_assigned_to ||= l(:field_assigned_to) @cached_label_priority ||= l(:field_priority) - + @cached_label_project ||= l(:field_project) + link_to_issue(issue) + "

    " + + "#{@cached_label_project}: #{link_to_project(issue.project)}
    " + "#{@cached_label_status}: #{issue.status.name}
    " + "#{@cached_label_start_date}: #{format_date(issue.start_date)}
    " + "#{@cached_label_due_date}: #{format_date(issue.due_date)}
    " + diff --git a/app/models/issue.rb b/app/models/issue.rb index 7d0682df1..80db48108 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -62,10 +62,28 @@ class Issue < ActiveRecord::Base named_scope :open, :conditions => ["#{IssueStatus.table_name}.is_closed = ?", false], :include => :status - named_scope :recently_updated, :order => "#{self.table_name}.updated_on DESC" + named_scope :recently_updated, :order => "#{Issue.table_name}.updated_on DESC" named_scope :with_limit, lambda { |limit| { :limit => limit} } named_scope :on_active_project, :include => [:status, :project, :tracker], :conditions => ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"] + named_scope :for_gantt, lambda { + { + :include => [:tracker, :status, :assigned_to, :priority, :project, :fixed_version], + :order => "#{Issue.table_name}.due_date ASC, #{Issue.table_name}.start_date ASC, #{Issue.table_name}.id ASC" + } + } + + named_scope :without_version, lambda { + { + :conditions => { :fixed_version_id => nil} + } + } + + named_scope :with_query, lambda {|query| + { + :conditions => Query.merge_conditions(query.statement) + } + } before_create :default_assign before_save :reschedule_following_issues, :close_duplicates, :update_done_ratio_from_issue_status @@ -357,6 +375,13 @@ class Issue < ActiveRecord::Base def overdue? !due_date.nil? && (due_date < Date.today) && !status.is_closed? end + + # Is the amount of work done less than it should for the due date + def behind_schedule? + return false if start_date.nil? || due_date.nil? + done_date = start_date + ((due_date - start_date+1)* done_ratio/100).floor + return done_date <= Date.today + end # Users the issue can be assigned to def assignable_users diff --git a/app/models/project.rb b/app/models/project.rb index 931f89b55..5ef7915de 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -412,6 +412,50 @@ class Project < ActiveRecord::Base def short_description(length = 255) description.gsub(/^(.{#{length}}[^\n\r]*).*$/m, '\1...').strip if description end + + # The earliest start date of a project, based on it's issues and versions + def start_date + if module_enabled?(:issue_tracking) + [ + issues.minimum('start_date'), + shared_versions.collect(&:effective_date), + shared_versions.collect {|v| v.fixed_issues.minimum('start_date')} + ].flatten.compact.min + end + end + + # The latest due date of an issue or version + def due_date + if module_enabled?(:issue_tracking) + [ + issues.maximum('due_date'), + shared_versions.collect(&:effective_date), + shared_versions.collect {|v| v.fixed_issues.maximum('due_date')} + ].flatten.compact.max + end + end + + def overdue? + active? && !due_date.nil? && (due_date < Date.today) + end + + # Returns the percent completed for this project, based on the + # progress on it's versions. + def completed_percent(options={:include_subprojects => false}) + if options.delete(:include_subprojects) + total = self_and_descendants.collect(&:completed_percent).sum + + total / self_and_descendants.count + else + if versions.count > 0 + total = versions.collect(&:completed_pourcent).sum + + total / versions.count + else + 100 + end + end + end # Return true if this project is allowed to do the specified action. # action can be: diff --git a/app/models/version.rb b/app/models/version.rb index 07e66434d..c3969fe87 100644 --- a/app/models/version.rb +++ b/app/models/version.rb @@ -73,6 +73,18 @@ class Version < ActiveRecord::Base def completed? effective_date && (effective_date <= Date.today) && (open_issues_count == 0) end + + def behind_schedule? + if completed_pourcent == 100 + return false + elsif due_date && fixed_issues.present? && fixed_issues.minimum('start_date') # TODO: should use #start_date but that method is wrong... + start_date = fixed_issues.minimum('start_date') + done_date = start_date + ((due_date - start_date+1)* completed_pourcent/100).floor + return done_date <= Date.today + else + false # No issues so it's not late + end + end # Returns the completion percentage of this version based on the amount of open/closed issues # and the time spent on the open issues. diff --git a/app/views/gantts/show.html.erb b/app/views/gantts/show.html.erb index 5d4ef0dbf..ce8c67b26 100644 --- a/app/views/gantts/show.html.erb +++ b/app/views/gantts/show.html.erb @@ -1,3 +1,4 @@ +<% @gantt.view = self %>

    <%= l(:label_gantt) %>

    <% form_tag(gantt_path(:month => params[:month], :year => params[:year], :months => params[:months]), :method => :put, :id => 'query_form') do %> @@ -55,11 +56,12 @@ if @gantt.zoom >1 end end +# Width of the entire chart g_width = (@gantt.date_to - @gantt.date_from + 1)*zoom -g_height = [(20 * @gantt.events.length + 6)+150, 206].max +# Collect the number of issues on Versions +g_height = [(20 * (@gantt.number_of_rows + 6))+150, 206].max t_height = g_height + headers_height %> -

  • <%= link_to image_tag('toggle_check.png'), {}, :onclick => 'toggleIssuesSelection(Element.up(this, "form")); return false;', + <%= link_to image_tag('toggle_check.png'), {}, :onclick => 'toggleIssuesSelection(Element.up(this, "form")); return false;', :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %>
    <%= check_box_tag("ids[]", issue.id, false, :id => nil) %><%= check_box_tag("ids[]", issue.id, false, :id => nil) %> <%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %>
    <% project_tree(@projects) do |project, level| %> - <%= css_project_classes(project) %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>"> + <%= project.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>"> From f2a8057d635be2a844b00437cff6668fdd3e87a1 Mon Sep 17 00:00:00 2001 From: Azamat Hackimov Date: Sat, 11 Sep 2010 11:16:56 +0000 Subject: [PATCH 45/72] Translations update * ja (#6363) * ko (#6307) * po (#6458) * pt-BR (#6316) * ru * sr and sr-YU (#6339) * zh-TW (#6306) New strings to translate * field_member_of_group (r4077) * field_assigned_to_role (r4078) git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4081 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- config/locales/bg.yml | 2 + config/locales/bs.yml | 2 + config/locales/ca.yml | 2 + config/locales/cs.yml | 2 + config/locales/da.yml | 2 + config/locales/de.yml | 2 + config/locales/el.yml | 2 + config/locales/en-GB.yml | 2 + config/locales/es.yml | 2 + config/locales/eu.yml | 2 + config/locales/fi.yml | 2 + config/locales/fr.yml | 2 + config/locales/gl.yml | 2 + config/locales/he.yml | 2 + config/locales/hr.yml | 2 + config/locales/hu.yml | 2 + config/locales/id.yml | 2 + config/locales/it.yml | 2 + config/locales/ja.yml | 4 +- config/locales/ko.yml | 22 ++++---- config/locales/lt.yml | 2 + config/locales/lv.yml | 2 + config/locales/mk.yml | 2 + config/locales/mn.yml | 2 + config/locales/nl.yml | 2 + config/locales/no.yml | 2 + config/locales/pl.yml | 112 ++++++++++++++++++++------------------- config/locales/pt-BR.yml | 4 +- config/locales/pt.yml | 2 + config/locales/ro.yml | 2 + config/locales/ru.yml | 2 + config/locales/sk.yml | 2 + config/locales/sl.yml | 2 + config/locales/sr-YU.yml | 10 ++-- config/locales/sr.yml | 11 ++-- config/locales/sv.yml | 2 + config/locales/th.yml | 2 + config/locales/tr.yml | 2 + config/locales/uk.yml | 2 + config/locales/vi.yml | 2 + config/locales/zh-TW.yml | 4 +- config/locales/zh.yml | 2 + 42 files changed, 161 insertions(+), 76 deletions(-) diff --git a/config/locales/bg.yml b/config/locales/bg.yml index f334633e2..0a8c427ef 100644 --- a/config/locales/bg.yml +++ b/config/locales/bg.yml @@ -907,3 +907,5 @@ bg: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/bs.yml b/config/locales/bs.yml index 90ec9dea2..741f46232 100644 --- a/config/locales/bs.yml +++ b/config/locales/bs.yml @@ -927,3 +927,5 @@ bs: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/ca.yml b/config/locales/ca.yml index b5c28d98e..50a389156 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -916,3 +916,5 @@ ca: enumeration_activities: Activitats (seguidor de temps) enumeration_system_activity: Activitat del sistema + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/cs.yml b/config/locales/cs.yml index f2b114605..96fb608b6 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -913,3 +913,5 @@ cs: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/da.yml b/config/locales/da.yml index 4d30dc6f3..38d9d6646 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -929,3 +929,5 @@ da: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/de.yml b/config/locales/de.yml index 78cc99d4a..5ec7073b3 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -932,3 +932,5 @@ de: enumeration_activities: Aktivitäten (Zeiterfassung) enumeration_system_activity: System-Aktivität + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/el.yml b/config/locales/el.yml index e4487c59f..22bc6a459 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -913,3 +913,5 @@ el: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 6a8faa746..8c0b28cf3 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -917,3 +917,5 @@ en-GB: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/es.yml b/config/locales/es.yml index 4ffdaf3f6..3f848f1d1 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -953,3 +953,5 @@ es: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/eu.yml b/config/locales/eu.yml index 236c5bb7d..24ef38ea7 100644 --- a/config/locales/eu.yml +++ b/config/locales/eu.yml @@ -917,3 +917,5 @@ eu: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/fi.yml b/config/locales/fi.yml index 9aeee5f6e..985ab8b2a 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -938,3 +938,5 @@ fi: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 24b70ad21..7f96b6112 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -931,3 +931,5 @@ fr: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/gl.yml b/config/locales/gl.yml index db64fabdc..b717d8d60 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -929,3 +929,5 @@ gl: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/he.yml b/config/locales/he.yml index 50f31c294..86ebe7d3a 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -918,3 +918,5 @@ he: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/hr.yml b/config/locales/hr.yml index 5727a629e..f4a809a9a 100644 --- a/config/locales/hr.yml +++ b/config/locales/hr.yml @@ -920,3 +920,5 @@ hr: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/hu.yml b/config/locales/hu.yml index c895f4c96..fe1abdf36 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -936,3 +936,5 @@ field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/id.yml b/config/locales/id.yml index 65140c2d0..7c327833f 100644 --- a/config/locales/id.yml +++ b/config/locales/id.yml @@ -921,3 +921,5 @@ id: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/it.yml b/config/locales/it.yml index 34a7820d2..8d5aa4ef4 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -917,3 +917,5 @@ it: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 68c4d24aa..09eb36690 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -144,7 +144,7 @@ ja: greater_than_start_date: "を開始日より後にしてください" not_same_project: "同じプロジェクトに属していません" circular_dependency: "この関係では、循環依存になります" - cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" + cant_link_an_issue_with_a_descendant: "指定したチケットとは親子関係になっているため関連づけられません" actionview_instancetag_blank_option: 選んでください @@ -319,6 +319,8 @@ ja: field_group_by: グループ条件 field_sharing: 共有 field_parent_issue: 親チケット + field_member_of_group: 担当者のグループ + field_assigned_to_role: 担当者のロール setting_app_title: アプリケーションのタイトル setting_app_subtitle: アプリケーションのサブタイトル diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 0a5fa44e1..eea667d62 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -2,7 +2,7 @@ # by Kihyun Yoon(ddumbugie@gmail.com),http://plenum.textcube.com/ # by John Hwang (jhwang@tavon.org),http://github.com/tavon # by Yonghwan SO(please insert your email), last update at 2009-09-11 -# last update at 2010-01-23 by Kihyun Yoon +# last update at 2010-09-06 by Kihyun Yoon ko: direction: ltr date: @@ -959,13 +959,15 @@ ko: error_unable_to_connect: 연결할 수 없습니다(({{value}}) error_can_not_remove_role: 이 역할은 현재 사용 중이이서 삭제할 수 없습니다. error_can_not_delete_tracker: 이 유형의 일감들이 있에서 삭제할 수 없습니다. - field_principal: Principal - label_my_page_block: My page block + field_principal: 신원 + label_my_page_block: 내 페이지 출력화면 notice_failed_to_save_members: "Failed to save member(s): {{errors}}." - text_zoom_out: Zoom out - text_zoom_in: Zoom in - notice_unable_delete_time_entry: Unable to delete time log entry. - label_overall_spent_time: Overall spent time - field_time_entries: Log time - project_module_gantt: Gantt - project_module_calendar: Calendar + text_zoom_out: 더 작게 + text_zoom_in: 더 크게 + notice_unable_delete_time_entry: 시간 기록 항목을 삭제할 수 없습니다. + label_overall_spent_time: 총 소요시간 + field_time_entries: 기록된 시간 + project_module_gantt: Gantt 챠트 + project_module_calendar: 달력 + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/lt.yml b/config/locales/lt.yml index 9a3684e91..b12a1107b 100644 --- a/config/locales/lt.yml +++ b/config/locales/lt.yml @@ -977,3 +977,5 @@ lt: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/lv.yml b/config/locales/lv.yml index 0ec9b4f4f..271140cce 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -908,3 +908,5 @@ lv: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/mk.yml b/config/locales/mk.yml index c680f900c..c98761865 100644 --- a/config/locales/mk.yml +++ b/config/locales/mk.yml @@ -913,3 +913,5 @@ mk: enumeration_activities: Активности (следење на време) enumeration_system_activity: Системска активност + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/mn.yml b/config/locales/mn.yml index 73791bb74..b92538a47 100644 --- a/config/locales/mn.yml +++ b/config/locales/mn.yml @@ -914,3 +914,5 @@ mn: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/nl.yml b/config/locales/nl.yml index c55dadb47..e13e67ddf 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -895,3 +895,5 @@ nl: field_time_entries: Log tijd project_module_gantt: Gantt project_module_calendar: Kalender + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/no.yml b/config/locales/no.yml index b1746fd41..cc95dae7d 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -904,3 +904,5 @@ field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/pl.yml b/config/locales/pl.yml index d48b3d935..6621ad39e 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -96,8 +96,8 @@ pl: few: "ponad {{count}} lata" other: "ponad {{count}} lat" almost_x_years: - one: "almost 1 year" - other: "almost {{count}} years" + one: "prawie rok" + other: "prawie {{count}} lata" activerecord: errors: @@ -122,14 +122,14 @@ pl: greater_than: "musi być większe niż {{count}}" greater_than_or_equal_to: "musi być większe lub równe {{count}}" equal_to: "musi być równe {{count}}" - less_than: "musie być mniejsze niż {{count}}" + less_than: "musi być mniejsze niż {{count}}" less_than_or_equal_to: "musi być mniejsze lub równe {{count}}" odd: "musi być nieparzyste" even: "musi być parzyste" greater_than_start_date: "musi być większe niż początkowa data" not_same_project: "nie należy do tego samego projektu" circular_dependency: "Ta relacja może wytworzyć kołową zależność" - cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" + cant_link_an_issue_with_a_descendant: "Zagadnienie nie może zostać powiązane z jednym z własnych podzagadnień" support: array: @@ -160,19 +160,19 @@ pl: button_edit: Edytuj button_list: Lista button_lock: Zablokuj - button_log_time: Log czasu + button_log_time: Dziennik button_login: Login button_move: Przenieś button_quote: Cytuj button_rename: Zmień nazwę button_reply: Odpowiedz button_reset: Resetuj - button_rollback: Przywróc do tej wersji + button_rollback: Przywróć do tej wersji button_save: Zapisz button_sort: Sortuj button_submit: Wyślij button_test: Testuj - button_unarchive: Przywróc z archiwum + button_unarchive: Przywróć z archiwum button_uncheck_all: Odznacz wszystko button_unlock: Odblokuj button_unwatch: Nie obserwuj @@ -539,12 +539,12 @@ pl: label_project_latest: Ostatnie projekty label_project_new: Nowy projekt label_project_plural234: Projekty - label_project_plural5: Projekty + label_project_plural5: Projektów label_project_plural: Projekty label_x_projects: - zero: no projects - one: 1 project - other: "{{count}} projects" + zero: brak projektów + one: jeden projekt + other: "{{count}} projektów" label_public_projects: Projekty publiczne label_query: Kwerenda label_query_new: Nowa kwerenda @@ -590,7 +590,7 @@ pl: label_sort_highest: Przesuń na górę label_sort_lower: Do dołu label_sort_lowest: Przesuń na dół - label_spent_time: Spędzony czas + label_spent_time: Przepracowany czas label_start_to_end: początek do końca label_start_to_start: początek do początku label_statistics: Statystyki @@ -602,7 +602,7 @@ pl: label_this_month: ten miesiąc label_this_week: ten tydzień label_this_year: ten rok - label_time_tracking: Śledzenie czasu + label_time_tracking: Śledzenie czasu pracy label_today: dzisiaj label_topic_plural: Tematy label_total: Ogółem @@ -684,11 +684,11 @@ pl: permission_edit_messages: Edycja wiadomości permission_edit_own_issue_notes: Edycja własnych notatek permission_edit_own_messages: Edycja własnych wiadomości - permission_edit_own_time_entries: Edycja własnego logu czasu + permission_edit_own_time_entries: Edycja własnego dziennika permission_edit_project: Edycja projektów - permission_edit_time_entries: Edycja logów czasu + permission_edit_time_entries: Edycja wpisów dziennika permission_edit_wiki_pages: Edycja stron wiki - permission_log_time: Zapisywanie spędzonego czasu + permission_log_time: Zapisywanie przepracowanego czasu permission_manage_boards: Zarządzanie forami permission_manage_categories: Zarządzanie kategoriami zaganień permission_manage_documents: Zarządzanie dokumentami @@ -712,7 +712,7 @@ pl: permission_view_gantt: Podgląd diagramu Gantta permission_view_issue_watchers: Podgląd listy obserwatorów permission_view_messages: Podgląd wiadomości - permission_view_time_entries: Podgląd spędzonego czasu + permission_view_time_entries: Podgląd przepracowanego czasu permission_view_wiki_edits: Podgląd historii wiki permission_view_wiki_pages: Podgląd wiki project_module_boards: Fora @@ -721,7 +721,7 @@ pl: project_module_issue_tracking: Śledzenie zagadnień project_module_news: Komunikaty project_module_repository: Repozytorium - project_module_time_tracking: Śledzenie czasu + project_module_time_tracking: Śledzenie czasu pracy project_module_wiki: Wiki setting_activity_days_default: Dni wyświetlane w aktywności projektu setting_app_subtitle: Podtytuł aplikacji @@ -743,7 +743,7 @@ pl: setting_feeds_limit: Limit danych RSS setting_gravatar_enabled: Używaj ikon użytkowników Gravatar setting_host_name: Nazwa hosta i ścieżka - setting_issue_list_default_columns: Domyślne kolumny wiświetlane na liście zagadnień + setting_issue_list_default_columns: Domyślne kolumny wyświetlane na liście zagadnień setting_issues_export_limit: Limit eksportu zagadnień setting_login_required: Identyfikacja wymagana setting_mail_from: Adres email wysyłki @@ -765,20 +765,20 @@ pl: status_locked: zablokowany status_registered: zarejestrowany text_are_you_sure: Jesteś pewien ? - text_assign_time_entries_to_project: Przypisz logowany czas do projektu + text_assign_time_entries_to_project: Przypisz wpisy dziennika do projektu text_caracters_maximum: "{{count}} znaków maksymalnie." text_caracters_minimum: "Musi być nie krótsze niż {{count}} znaków." text_comma_separated: Wielokrotne wartości dozwolone (rozdzielone przecinkami). text_default_administrator_account_changed: Zmieniono domyślne hasło administratora - text_destroy_time_entries: Usuń zalogowany czas - text_destroy_time_entries_question: Zalogowano {{hours}} godzin przy zagadnieniu, które chcesz usunąć. Co chcesz zrobić? + text_destroy_time_entries: Usuń wpisy dziennika + text_destroy_time_entries_question: Przepracowano {{hours}} godzin przy zagadnieniu, które chcesz usunąć. Co chcesz zrobić? text_email_delivery_not_configured: "Dostarczanie poczty elektronicznej nie zostało skonfigurowane, więc powiadamianie jest nieaktywne.\nSkonfiguruj serwer SMTP w config/email.yml a następnie zrestartuj aplikację i uaktywnij to." text_enumeration_category_reassign_to: 'Zmień przypisanie na tą wartość:' text_enumeration_destroy_question: "{{count}} obiektów jest przypisana do tej wartości." text_file_repository_writable: Zapisywalne repozytorium plików text_issue_added: "Zagadnienie {{id}} zostało wprowadzone (by {{author}})." text_issue_category_destroy_assignments: Usuń przydziały kategorii - text_issue_category_destroy_question: "Zagadnienia ({{count}}) są przypisane do tej kategorii. Co chcesz uczynić?" + text_issue_category_destroy_question: "Zagadnienia ({{count}}) są przypisane do tej kategorii. Co chcesz zrobić?" text_issue_category_reassign_to: Przydziel zagadnienie do tej kategorii text_issue_updated: "Zagadnienie {{id}} zostało zaktualizowane (by {{author}})." text_issues_destroy_confirmation: 'Czy jestes pewien, że chcesz usunąć wskazane zagadnienia?' @@ -787,9 +787,9 @@ pl: text_load_default_configuration: Załaduj domyślną konfigurację text_min_max_length_info: 0 oznacza brak restrykcji text_no_configuration_data: "Role użytkowników, typy zagadnień, statusy zagadnień oraz przepływ pracy nie zostały jeszcze skonfigurowane.\nJest wysoce rekomendowane by załadować domyślną konfigurację. Po załadowaniu będzie możliwość edycji tych danych." - text_project_destroy_confirmation: Jesteś pewien, że chcesz usunąć ten projekt i wszyskie powiązane dane? + text_project_destroy_confirmation: Jesteś pewien, że chcesz usunąć ten projekt i wszystkie powiązane dane? text_project_identifier_info: 'Małe litery (a-z), liczby i myślniki dozwolone.
    Raz zapisany, identyfikator nie może być zmieniony.' - text_reassign_time_entries: 'Przepnij zalogowany czas do tego zagadnienia:' + text_reassign_time_entries: 'Przepnij przepracowany czas do tego zagadnienia:' text_regexp_info: np. ^[A-Z0-9]+$ text_repository_usernames_mapping: "Wybierz lub uaktualnij przyporządkowanie użytkowników Redmine do użytkowników repozytorium.\nUżytkownicy z taką samą nazwą lub adresem email są przyporządkowani automatycznie." text_rmagick_available: RMagick dostępne (opcjonalnie) @@ -800,9 +800,9 @@ pl: text_tip_task_begin_day: zadanie zaczynające się dzisiaj text_tip_task_begin_end_day: zadanie zaczynające i kończące się dzisiaj text_tip_task_end_day: zadanie kończące się dzisiaj - text_tracker_no_workflow: Brak przepływu zefiniowanego dla tego typu zagadnienia + text_tracker_no_workflow: Brak przepływu zdefiniowanego dla tego typu zagadnienia text_unallowed_characters: Niedozwolone znaki - text_user_mail_option: "W przypadku niezaznaczonych projektów, będziesz otrzymywał powiadomienia tylko na temat zagadnien, które obserwujesz, lub w których bierzesz udział (np. jesteś autorem lub adresatem)." + text_user_mail_option: "W przypadku niezaznaczonych projektów, będziesz otrzymywał powiadomienia tylko na temat zagadnień, które obserwujesz, lub w których bierzesz udział (np. jesteś autorem lub adresatem)." text_user_wrote: "{{value}} napisał:" text_wiki_destroy_confirmation: Jesteś pewien, że chcesz usunąć to wiki i całą jego zawartość ? text_workflow_edit: Zaznacz rolę i typ zagadnienia do edycji przepływu @@ -818,7 +818,7 @@ pl: button_create_and_continue: Stwórz i dodaj kolejne text_custom_field_possible_values_info: 'Każda wartość w osobnej linii' setting_repository_log_display_limit: Maksymalna liczba rewizji pokazywanych w logu pliku - setting_file_max_size_displayed: Maksymalny rozmiar plików tekstowych zagnieżdżanych w stronie + setting_file_max_size_displayed: Maksymalny rozmiar plików tekstowych osadzanych w stronie field_watcher: Obserwator setting_openid: Logowanie i rejestracja przy użyciu OpenID field_identity_url: Identyfikator OpenID (URL) @@ -830,10 +830,10 @@ pl: label_date_from_to: Od {{start}} do {{end}} label_greater_or_equal: ">=" label_less_or_equal: <= - text_wiki_page_destroy_question: This page has {{descendants}} child page(s) and descendant(s). What do you want to do? - text_wiki_page_reassign_children: Reassign child pages to this parent page - text_wiki_page_nullify_children: Keep child pages as root pages - text_wiki_page_destroy_children: Delete child pages and all their descendants + text_wiki_page_destroy_question: Ta strona posiada podstrony ({{descendants}}). Co chcesz zrobić? + text_wiki_page_reassign_children: Podepnij je do strony nadrzędnej względem usuwanej + text_wiki_page_nullify_children: Przesuń je na szczyt hierarchii + text_wiki_page_destroy_children: Usuń wszystkie podstrony setting_password_min_length: Minimalna długość hasła field_group_by: Grupuj wyniki wg mail_subject_wiki_content_updated: "Strona wiki '{{page}}' została uaktualniona" @@ -845,7 +845,7 @@ pl: permission_add_project: Tworzenie projektu setting_new_project_user_role_id: Rola nadawana twórcom projektów, którzy nie posiadają uprawnień administatora label_view_all_revisions: Pokaż wszystkie rewizje - label_tag: Tag + label_tag: Słowo kluczowe label_branch: Gałąź error_no_tracker_in_project: Projekt nie posiada powiązanych typów zagadnień. Sprawdź ustawienia projektu. error_no_default_issue_status: Nie zdefiniowano domyślnego statusu zagadnień. Sprawdź konfigurację (Przejdź do "Administracja -> Statusy zagadnień). @@ -855,7 +855,7 @@ pl: label_group_plural: Grupy label_group: Grupa label_group_new: Nowa grupa - label_time_entry_plural: Spędzony czas + label_time_entry_plural: Przepracowany czas text_journal_added: "Dodano {{label}} {{value}}" field_active: Aktywne enumeration_system_activity: Aktywność Systemowa @@ -913,24 +913,26 @@ pl: label_board_locked: Zamknięta permission_export_wiki_pages: Eksport stron wiki permission_manage_project_activities: Zarządzanie aktywnościami projektu - setting_cache_formatted_text: Cache formatted text - error_unable_delete_issue_status: Unable to delete issue status - label_profile: Profile - permission_manage_subtasks: Manage subtasks - field_parent_issue: Parent task - label_subtask_plural: Subtasks - label_project_copy_notifications: Send email notifications during the project copy - error_can_not_delete_custom_field: Unable to delete custom field - error_unable_to_connect: Unable to connect ({{value}}) - error_can_not_remove_role: This role is in use and can not be deleted. - error_can_not_delete_tracker: This tracker contains issues and can't be deleted. - field_principal: Principal - label_my_page_block: My page block - notice_failed_to_save_members: "Failed to save member(s): {{errors}}." - text_zoom_out: Zoom out - text_zoom_in: Zoom in - notice_unable_delete_time_entry: Unable to delete time log entry. - label_overall_spent_time: Overall spent time - field_time_entries: Log time - project_module_gantt: Gantt - project_module_calendar: Calendar + setting_cache_formatted_text: Buforuj sformatowany tekst + error_unable_delete_issue_status: Nie można usunąć statusu zagadnienia + label_profile: Profil + permission_manage_subtasks: Zarządzanie podzagadnieniami + field_parent_issue: Zagadnienie nadrzędne + label_subtask_plural: Podzagadnienia + label_project_copy_notifications: Wyślij powiadomienia mailowe przy kopiowaniu projektu + error_can_not_delete_custom_field: Nie można usunąć tego pola + error_unable_to_connect: Nie można połączyć ({{value}}) + error_can_not_remove_role: Ta rola przypisana jest niektórym użytkownikom i nie może zostać usunięta. + error_can_not_delete_tracker: Ten typ przypisany jest do części zagadnień i nie może zostać usunięty. + field_principal: Przełożony + label_my_page_block: Elementy + notice_failed_to_save_members: "Nie można zapisać uczestników: {{errors}}." + text_zoom_out: Zmniejsz czcionkę + text_zoom_in: Powiększ czcionkę + notice_unable_delete_time_entry: Nie można usunąć wpisu z dziennika. + label_overall_spent_time: Przepracowany czas + field_time_entries: Dziennik + project_module_gantt: Diagram Gantta + project_module_calendar: Kalendarz + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index c41e4e0dc..547074083 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -143,7 +143,7 @@ pt-BR: greater_than_start_date: "deve ser maior que a data inicial" not_same_project: "não pertence ao mesmo projeto" circular_dependency: "Esta relação geraria uma dependência circular" - cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" + cant_link_an_issue_with_a_descendant: "Uma tarefa não pode ser relaciona a uma de suas subtarefas" actionview_instancetag_blank_option: Selecione @@ -937,3 +937,5 @@ pt-BR: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/pt.yml b/config/locales/pt.yml index 3127f4044..28822061a 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -921,3 +921,5 @@ pt: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/ro.yml b/config/locales/ro.yml index a0458932c..85fea1399 100644 --- a/config/locales/ro.yml +++ b/config/locales/ro.yml @@ -906,3 +906,5 @@ ro: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 586e2fbcc..806b5c0ac 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -294,6 +294,7 @@ ru: field_admin: Администратор field_assignable: Задача может быть назначена этой роли field_assigned_to: Назначена + field_assigned_to_role: Роль участника field_attr_firstname: Имя field_attr_lastname: Фамилия field_attr_login: Атрибут Регистрация @@ -343,6 +344,7 @@ ru: field_mail: Email field_mail_notification: Уведомления по email field_max_length: Максимальная длина + field_member_of_group: Группа участника field_min_length: Минимальная длина field_name: Имя field_new_password: Новый пароль diff --git a/config/locales/sk.yml b/config/locales/sk.yml index 52c57c79c..3fd6c41b2 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -908,3 +908,5 @@ sk: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/sl.yml b/config/locales/sl.yml index 8ee68f15d..68d8bb089 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -909,3 +909,5 @@ sl: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/sr-YU.yml b/config/locales/sr-YU.yml index e5df0e427..c2ac531c9 100644 --- a/config/locales/sr-YU.yml +++ b/config/locales/sr-YU.yml @@ -68,7 +68,7 @@ sr-YU: number: format: - separator: "." + separator: "," delimiter: "" precision: 3 human: @@ -910,6 +910,8 @@ sr-YU: enumeration_activities: Aktivnosti (praćenje vremena) enumeration_system_activity: Sistemska aktivnost - field_time_entries: Log time - project_module_gantt: Gantt - project_module_calendar: Calendar + field_time_entries: Vreme evidencije + project_module_gantt: Gantov dijagram + project_module_calendar: Kalendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/sr.yml b/config/locales/sr.yml index 3641c7091..5b5dfd461 100644 --- a/config/locales/sr.yml +++ b/config/locales/sr.yml @@ -68,7 +68,7 @@ sr: number: format: - separator: "." + separator: "," delimiter: "" precision: 3 human: @@ -910,6 +910,9 @@ sr: enumeration_activities: Активности (праћење времена) enumeration_system_activity: Системска активност - field_time_entries: Log time - project_module_gantt: Gantt - project_module_calendar: Calendar + field_time_entries: Време евиденције + project_module_gantt: Гантов дијаграм + project_module_calendar: Календар + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role + diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 657a9adcd..8055c572d 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -958,3 +958,5 @@ sv: enumeration_doc_categories: Dokumentkategorier enumeration_activities: Aktiviteter (tidsuppföljning) enumeration_system_activity: Systemaktivitet + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/th.yml b/config/locales/th.yml index 67bf3c4cc..3e855995f 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -910,3 +910,5 @@ th: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/tr.yml b/config/locales/tr.yml index e0f23078d..43871ffe9 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -936,3 +936,5 @@ tr: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/uk.yml b/config/locales/uk.yml index d0191618b..89901d1ea 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -909,3 +909,5 @@ uk: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/vi.yml b/config/locales/vi.yml index 66fdbc115..28351ed26 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -968,3 +968,5 @@ vi: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 56d8c9b29..8dab78787 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -177,7 +177,7 @@ greater_than_start_date: "必須在起始日期之後" not_same_project: "不屬於同一個專案" circular_dependency: "這個關聯會導致環狀相依" - cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" + cant_link_an_issue_with_a_descendant: "項目無法被連結至自己的子項目" # You can define own errors for models or model attributes. # The values :model, :attribute and :value are always available for interpolation. @@ -998,3 +998,5 @@ enumeration_doc_categories: 文件分類 enumeration_activities: 活動 (時間追蹤) enumeration_system_activity: 系統活動 + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role diff --git a/config/locales/zh.yml b/config/locales/zh.yml index a05d96cae..b882d277a 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -931,3 +931,5 @@ zh: field_time_entries: Log time project_module_gantt: Gantt project_module_calendar: Calendar + field_member_of_group: Member of Group + field_assigned_to_role: Member of Role From 4b69a895df5b2bd5838b3c1e277446dd1cbc5395 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Barth Date: Sat, 11 Sep 2010 14:00:23 +0000 Subject: [PATCH 46/72] Fixed broken context_menu on roadmap. #6351 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4082 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/views/versions/index.html.erb | 2 +- test/functional/versions_controller_test.rb | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/views/versions/index.html.erb b/app/views/versions/index.html.erb index 100282a7a..d0c5dcac1 100644 --- a/app/views/versions/index.html.erb +++ b/app/views/versions/index.html.erb @@ -51,4 +51,4 @@ <% html_title(l(:label_roadmap)) %> -<%= context_menu :controller => 'issues', :action => 'context_menu' %> +<%= context_menu issues_context_menu_path %> diff --git a/test/functional/versions_controller_test.rb b/test/functional/versions_controller_test.rb index 5fcb1d05a..f738794ae 100644 --- a/test/functional/versions_controller_test.rb +++ b/test/functional/versions_controller_test.rb @@ -40,6 +40,8 @@ class VersionsControllerTest < ActionController::TestCase assert assigns(:versions).include?(Version.find(3)) # Completed version doesn't appear assert !assigns(:versions).include?(Version.find(1)) + # Context menu on issues + assert_select "script", :text => Regexp.new(Regexp.escape("new ContextMenu('/issues/context_menu')")) end def test_index_with_completed_versions From 12e10f6956fcd276fc3da19beb0c7bf4ade5f0da Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Barth Date: Sat, 11 Sep 2010 20:21:27 +0000 Subject: [PATCH 47/72] Fixed "Create and continue" redirection broken by recent changes. #6333 IssuesController#create is no more scoped under project, but IssuesController#new is, so we need to precise project_id when redirecting to "New issue" form. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4083 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/issues_controller.rb | 2 +- test/functional/issues_controller_test.rb | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index 9f58cb0a1..385bfa757 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -134,7 +134,7 @@ class IssuesController < ApplicationController call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue}) respond_to do |format| format.html { - redirect_to(params[:continue] ? { :action => 'new', :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } : + redirect_to(params[:continue] ? { :action => 'new', :project_id => @project, :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } : { :action => 'show', :id => @issue }) } format.xml { render :action => 'show', :status => :created, :location => url_for(:controller => 'issues', :action => 'show', :id => @issue) } diff --git a/test/functional/issues_controller_test.rb b/test/functional/issues_controller_test.rb index 7d69db8ea..9e70233b2 100644 --- a/test/functional/issues_controller_test.rb +++ b/test/functional/issues_controller_test.rb @@ -405,7 +405,8 @@ class IssuesControllerTest < ActionController::TestCase :subject => 'This is first issue', :priority_id => 5}, :continue => '' - assert_redirected_to :controller => 'issues', :action => 'new', :issue => {:tracker_id => 3} + assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook', + :issue => {:tracker_id => 3} end def test_post_create_without_custom_fields_param From 41c055363ee4826799b9ffe7d76fd399c89190d3 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Mon, 13 Sep 2010 20:35:03 +0000 Subject: [PATCH 48/72] Refactor: split FilesController#new into #new and #create. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4084 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/files_controller.rb | 23 +++++++++++------------ app/views/files/new.html.erb | 2 +- config/routes.rb | 2 +- lib/redmine.rb | 2 +- test/functional/files_controller_test.rb | 8 ++++---- test/integration/routing_test.rb | 2 +- 6 files changed, 19 insertions(+), 20 deletions(-) diff --git a/app/controllers/files_controller.rb b/app/controllers/files_controller.rb index fe5eb48c8..0a4903d08 100644 --- a/app/controllers/files_controller.rb +++ b/app/controllers/files_controller.rb @@ -19,19 +19,18 @@ class FilesController < ApplicationController render :layout => !request.xhr? end - # TODO: split method into new (GET) and create (POST) def new - if request.post? - container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id])) - attachments = Attachment.attach_files(container, params[:attachments]) - render_attachment_warning_if_needed(container) - - if !attachments.empty? && Setting.notified_events.include?('file_added') - Mailer.deliver_attachments_added(attachments[:files]) - end - redirect_to :controller => 'files', :action => 'index', :id => @project - return - end @versions = @project.versions.sort end + + def create + container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id])) + attachments = Attachment.attach_files(container, params[:attachments]) + render_attachment_warning_if_needed(container) + + if !attachments.empty? && Setting.notified_events.include?('file_added') + Mailer.deliver_attachments_added(attachments[:files]) + end + redirect_to :controller => 'files', :action => 'index', :id => @project + end end diff --git a/app/views/files/new.html.erb b/app/views/files/new.html.erb index bbb3b1733..870a315c9 100644 --- a/app/views/files/new.html.erb +++ b/app/views/files/new.html.erb @@ -2,7 +2,7 @@ <%= error_messages_for 'attachment' %>
    -<% form_tag({ :action => 'new', :id => @project }, :multipart => true, :class => "tabular") do %> +<% form_tag({ :action => 'create', :id => @project }, :multipart => true, :class => "tabular") do %> <% if @versions.any? %>

    diff --git a/config/routes.rb b/config/routes.rb index 129c3b456..152b90f54 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -196,7 +196,7 @@ ActionController::Routing::Routes.draw do |map| end project_mapper.with_options :conditions => {:method => :post} do |project_actions| - project_actions.connect 'projects/:id/files/new', :controller => 'files', :action => 'new' + project_actions.connect 'projects/:id/files/new', :controller => 'files', :action => 'create' end end diff --git a/lib/redmine.rb b/lib/redmine.rb index 526b83e65..9d59390c1 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -102,7 +102,7 @@ Redmine::AccessControl.map do |map| end map.project_module :files do |map| - map.permission :manage_files, {:files => :new}, :require => :loggedin + map.permission :manage_files, {:files => [:new, :create]}, :require => :loggedin map.permission :view_files, :files => :index, :versions => :download end diff --git a/test/functional/files_controller_test.rb b/test/functional/files_controller_test.rb index 838caee70..2f6f009e7 100644 --- a/test/functional/files_controller_test.rb +++ b/test/functional/files_controller_test.rb @@ -26,14 +26,14 @@ class FilesControllerTest < ActionController::TestCase :attributes => { :href => '/attachments/download/9/version_file.zip' } end - def test_add_file + def test_create_file set_tmp_attachments_directory @request.session[:user_id] = 2 Setting.notified_events = ['file_added'] ActionMailer::Base.deliveries.clear assert_difference 'Attachment.count' do - post :new, :id => 1, :version_id => '', + post :create, :id => 1, :version_id => '', :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}} assert_response :redirect end @@ -48,13 +48,13 @@ class FilesControllerTest < ActionController::TestCase assert mail.body.include?('testfile.txt') end - def test_add_version_file + def test_create_version_file set_tmp_attachments_directory @request.session[:user_id] = 2 Setting.notified_events = ['file_added'] assert_difference 'Attachment.count' do - post :new, :id => 1, :version_id => '2', + post :create, :id => 1, :version_id => '2', :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}} assert_response :redirect end diff --git a/test/integration/routing_test.rb b/test/integration/routing_test.rb index ac75d4d7c..1867a60b0 100644 --- a/test/integration/routing_test.rb +++ b/test/integration/routing_test.rb @@ -179,7 +179,7 @@ class RoutingTest < ActionController::IntegrationTest should_route :post, "/projects", :controller => 'projects', :action => 'create' should_route :post, "/projects.xml", :controller => 'projects', :action => 'create', :format => 'xml' - should_route :post, "/projects/33/files/new", :controller => 'files', :action => 'new', :id => '33' + should_route :post, "/projects/33/files/new", :controller => 'files', :action => 'create', :id => '33' should_route :post, "/projects/64/archive", :controller => 'projects', :action => 'archive', :id => '64' should_route :post, "/projects/64/unarchive", :controller => 'projects', :action => 'unarchive', :id => '64' From 1b90703157a182d37496ae4c8e3c430681abddc0 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Tue, 14 Sep 2010 16:24:07 +0000 Subject: [PATCH 49/72] Refactor: convert FilesController to a restful resource. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4085 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/files_controller.rb | 4 ++-- app/views/files/index.html.erb | 2 +- app/views/files/new.html.erb | 2 +- config/routes.rb | 7 +------ lib/redmine.rb | 2 +- test/functional/files_controller_test.rb | 6 +++--- test/integration/routing_test.rb | 6 +++--- 7 files changed, 12 insertions(+), 17 deletions(-) diff --git a/app/controllers/files_controller.rb b/app/controllers/files_controller.rb index 0a4903d08..72a4a2411 100644 --- a/app/controllers/files_controller.rb +++ b/app/controllers/files_controller.rb @@ -1,7 +1,7 @@ class FilesController < ApplicationController menu_item :files - before_filter :find_project + before_filter :find_project_by_project_id before_filter :authorize helper :sort @@ -31,6 +31,6 @@ class FilesController < ApplicationController if !attachments.empty? && Setting.notified_events.include?('file_added') Mailer.deliver_attachments_added(attachments[:files]) end - redirect_to :controller => 'files', :action => 'index', :id => @project + redirect_to project_files_path(@project) end end diff --git a/app/views/files/index.html.erb b/app/views/files/index.html.erb index 66f5d12b9..20710402b 100644 --- a/app/views/files/index.html.erb +++ b/app/views/files/index.html.erb @@ -1,5 +1,5 @@

    -<%= link_to_if_authorized l(:label_attachment_new), {:controller => 'files', :action => 'new', :id => @project}, :class => 'icon icon-add' %> +<%= link_to_if_authorized l(:label_attachment_new), new_project_file_path(@project), :class => 'icon icon-add' %>

    <%=l(:label_attachment_plural)%>

    diff --git a/app/views/files/new.html.erb b/app/views/files/new.html.erb index 870a315c9..d9d1b6ee1 100644 --- a/app/views/files/new.html.erb +++ b/app/views/files/new.html.erb @@ -2,7 +2,7 @@ <%= error_messages_for 'attachment' %>
    -<% form_tag({ :action => 'create', :id => @project }, :multipart => true, :class => "tabular") do %> +<% form_tag(project_files_path(@project), :multipart => true, :class => "tabular") do %> <% if @versions.any? %>

    diff --git a/config/routes.rb b/config/routes.rb index 152b90f54..c85f23eb2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -181,6 +181,7 @@ ActionController::Routing::Routes.draw do |map| :unarchive => :post } do |project| project.resource :project_enumerations, :as => 'enumerations', :only => [:update, :destroy] + project.resources :files, :only => [:index, :new, :create] end # Destroy uses a get request to prompt the user before the actual DELETE request @@ -189,15 +190,9 @@ ActionController::Routing::Routes.draw do |map| # TODO: port to be part of the resources route(s) map.with_options :controller => 'projects' do |project_mapper| project_mapper.with_options :conditions => {:method => :get} do |project_views| - project_views.connect 'projects/:id/files', :controller => 'files', :action => 'index' - project_views.connect 'projects/:id/files/new', :controller => 'files', :action => 'new' project_views.connect 'projects/:id/settings/:tab', :controller => 'projects', :action => 'settings' project_views.connect 'projects/:project_id/issues/:copy_from/copy', :controller => 'issues', :action => 'new' end - - project_mapper.with_options :conditions => {:method => :post} do |project_actions| - project_actions.connect 'projects/:id/files/new', :controller => 'files', :action => 'create' - end end map.with_options :controller => 'activities', :action => 'index', :conditions => {:method => :get} do |activity| diff --git a/lib/redmine.rb b/lib/redmine.rb index 9d59390c1..f8f564220 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -198,7 +198,7 @@ Redmine::MenuManager.map :project_menu do |menu| :if => Proc.new { |p| p.wiki && !p.wiki.new_record? } menu.push :boards, { :controller => 'boards', :action => 'index', :id => nil }, :param => :project_id, :if => Proc.new { |p| p.boards.any? }, :caption => :label_board_plural - menu.push :files, { :controller => 'files', :action => 'index' }, :caption => :label_file_plural + menu.push :files, { :controller => 'files', :action => 'index' }, :caption => :label_file_plural, :param => :project_id menu.push :repository, { :controller => 'repositories', :action => 'show' }, :if => Proc.new { |p| p.repository && !p.repository.new_record? } menu.push :settings, { :controller => 'projects', :action => 'settings' }, :last => true diff --git a/test/functional/files_controller_test.rb b/test/functional/files_controller_test.rb index 2f6f009e7..6035c4be5 100644 --- a/test/functional/files_controller_test.rb +++ b/test/functional/files_controller_test.rb @@ -12,7 +12,7 @@ class FilesControllerTest < ActionController::TestCase end def test_index - get :index, :id => 1 + get :index, :project_id => 1 assert_response :success assert_template 'index' assert_not_nil assigns(:containers) @@ -33,7 +33,7 @@ class FilesControllerTest < ActionController::TestCase ActionMailer::Base.deliveries.clear assert_difference 'Attachment.count' do - post :create, :id => 1, :version_id => '', + post :create, :project_id => 1, :version_id => '', :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}} assert_response :redirect end @@ -54,7 +54,7 @@ class FilesControllerTest < ActionController::TestCase Setting.notified_events = ['file_added'] assert_difference 'Attachment.count' do - post :create, :id => 1, :version_id => '2', + post :create, :project_id => 1, :version_id => '2', :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}} assert_response :redirect end diff --git a/test/integration/routing_test.rb b/test/integration/routing_test.rb index 1867a60b0..38c1b2a0e 100644 --- a/test/integration/routing_test.rb +++ b/test/integration/routing_test.rb @@ -171,15 +171,15 @@ class RoutingTest < ActionController::IntegrationTest should_route :get, "/projects/1.xml", :controller => 'projects', :action => 'show', :id => '1', :format => 'xml' should_route :get, "/projects/4223/settings", :controller => 'projects', :action => 'settings', :id => '4223' should_route :get, "/projects/4223/settings/members", :controller => 'projects', :action => 'settings', :id => '4223', :tab => 'members' - should_route :get, "/projects/33/files", :controller => 'files', :action => 'index', :id => '33' - should_route :get, "/projects/33/files/new", :controller => 'files', :action => 'new', :id => '33' + should_route :get, "/projects/33/files", :controller => 'files', :action => 'index', :project_id => '33' + should_route :get, "/projects/33/files/new", :controller => 'files', :action => 'new', :project_id => '33' should_route :get, "/projects/33/roadmap", :controller => 'versions', :action => 'index', :project_id => '33' should_route :get, "/projects/33/activity", :controller => 'activities', :action => 'index', :id => '33' should_route :get, "/projects/33/activity.atom", :controller => 'activities', :action => 'index', :id => '33', :format => 'atom' should_route :post, "/projects", :controller => 'projects', :action => 'create' should_route :post, "/projects.xml", :controller => 'projects', :action => 'create', :format => 'xml' - should_route :post, "/projects/33/files/new", :controller => 'files', :action => 'create', :id => '33' + should_route :post, "/projects/33/files", :controller => 'files', :action => 'create', :project_id => '33' should_route :post, "/projects/64/archive", :controller => 'projects', :action => 'archive', :id => '64' should_route :post, "/projects/64/unarchive", :controller => 'projects', :action => 'unarchive', :id => '64' From 8900797adaf29adc447925b800d5457ca941795f Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Tue, 14 Sep 2010 19:02:20 +0000 Subject: [PATCH 50/72] Refactor: move method to Model. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4086 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/timelog_controller.rb | 4 ++-- app/models/time_entry.rb | 8 ++++++++ test/unit/time_entry_test.rb | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/app/controllers/timelog_controller.rb b/app/controllers/timelog_controller.rb index e234848d0..726c69d5b 100644 --- a/app/controllers/timelog_controller.rb +++ b/app/controllers/timelog_controller.rb @@ -260,8 +260,8 @@ private end @from, @to = @to, @from if @from && @to && @from > @to - @from ||= (TimeEntry.minimum(:spent_on, :include => :project, :conditions => Project.allowed_to_condition(User.current, :view_time_entries)) || Date.today) - 1 - @to ||= (TimeEntry.maximum(:spent_on, :include => :project, :conditions => Project.allowed_to_condition(User.current, :view_time_entries)) || Date.today) + @from ||= (TimeEntry.earilest_date_for_project || Date.today) - 1 + @to ||= (TimeEntry.latest_date_for_project || Date.today) end def load_available_criterias diff --git a/app/models/time_entry.rb b/app/models/time_entry.rb index 73f39f949..56801a4ca 100644 --- a/app/models/time_entry.rb +++ b/app/models/time_entry.rb @@ -81,4 +81,12 @@ class TimeEntry < ActiveRecord::Base yield end end + + def self.earilest_date_for_project + TimeEntry.minimum(:spent_on, :include => :project, :conditions => Project.allowed_to_condition(User.current, :view_time_entries)) + end + + def self.latest_date_for_project + TimeEntry.maximum(:spent_on, :include => :project, :conditions => Project.allowed_to_condition(User.current, :view_time_entries)) + end end diff --git a/test/unit/time_entry_test.rb b/test/unit/time_entry_test.rb index 3c135510a..a541fc41f 100644 --- a/test/unit/time_entry_test.rb +++ b/test/unit/time_entry_test.rb @@ -48,4 +48,19 @@ class TimeEntryTest < ActiveSupport::TestCase def test_hours_should_default_to_nil assert_nil TimeEntry.new.hours end + + context "#earilest_date_for_project" do + should "return the lowest spent_on value that is visible to the current user" do + User.current = nil + assert_equal "2007-03-12", TimeEntry.earilest_date_for_project.to_s + end + end + + context "#latest_date_for_project" do + should "return the highest spent_on value that is visible to the current user" do + User.current = nil + assert_equal "2007-04-22", TimeEntry.latest_date_for_project.to_s + end + end + end From cdfc57d5442f72d62437f52af480049c943ecbf8 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Tue, 14 Sep 2010 19:02:25 +0000 Subject: [PATCH 51/72] Change the TimelogController's to/from dates based on the project time entries Instead of looking for the earliest and latest time entry system wide for the dates in the form, now TimelogController will only look at the time entries for the current project (and parent/sub projects). git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4087 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/timelog_controller.rb | 4 +- app/models/project.rb | 9 +++++ app/models/time_entry.rb | 16 ++++++-- test/functional/timelog_controller_test.rb | 6 +-- test/unit/time_entry_test.rb | 45 +++++++++++++++++++--- 5 files changed, 65 insertions(+), 15 deletions(-) diff --git a/app/controllers/timelog_controller.rb b/app/controllers/timelog_controller.rb index 726c69d5b..45f41ae5e 100644 --- a/app/controllers/timelog_controller.rb +++ b/app/controllers/timelog_controller.rb @@ -260,8 +260,8 @@ private end @from, @to = @to, @from if @from && @to && @from > @to - @from ||= (TimeEntry.earilest_date_for_project || Date.today) - 1 - @to ||= (TimeEntry.latest_date_for_project || Date.today) + @from ||= (TimeEntry.earilest_date_for_project(@project) || Date.today) + @to ||= (TimeEntry.latest_date_for_project(@project) || Date.today) end def load_available_criterias diff --git a/app/models/project.rb b/app/models/project.rb index 40898a34e..4b0236b37 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -493,6 +493,15 @@ class Project < ActiveRecord::Base enabled_modules.clear end end + + # Returns an array of projects that are in this project's hierarchy + # + # Example: parents, children, siblings + def hierarchy + parents = project.self_and_ancestors || [] + descendants = project.descendants || [] + project_hierarchy = parents | descendants # Set union + end # Returns an auto-generated project identifier based on the last identifier used def self.next_identifier diff --git a/app/models/time_entry.rb b/app/models/time_entry.rb index 56801a4ca..9bf970891 100644 --- a/app/models/time_entry.rb +++ b/app/models/time_entry.rb @@ -82,11 +82,19 @@ class TimeEntry < ActiveRecord::Base end end - def self.earilest_date_for_project - TimeEntry.minimum(:spent_on, :include => :project, :conditions => Project.allowed_to_condition(User.current, :view_time_entries)) + def self.earilest_date_for_project(project=nil) + finder_conditions = ARCondition.new(Project.allowed_to_condition(User.current, :view_time_entries)) + if project + finder_conditions << ["project_id IN (?)", project.hierarchy.collect(&:id)] + end + TimeEntry.minimum(:spent_on, :include => :project, :conditions => finder_conditions.conditions) end - def self.latest_date_for_project - TimeEntry.maximum(:spent_on, :include => :project, :conditions => Project.allowed_to_condition(User.current, :view_time_entries)) + def self.latest_date_for_project(project=nil) + finder_conditions = ARCondition.new(Project.allowed_to_condition(User.current, :view_time_entries)) + if project + finder_conditions << ["project_id IN (?)", project.hierarchy.collect(&:id)] + end + TimeEntry.maximum(:spent_on, :include => :project, :conditions => finder_conditions.conditions) end end diff --git a/test/functional/timelog_controller_test.rb b/test/functional/timelog_controller_test.rb index 9e30a0ae8..0559a95ca 100644 --- a/test/functional/timelog_controller_test.rb +++ b/test/functional/timelog_controller_test.rb @@ -283,7 +283,7 @@ class TimelogControllerTest < ActionController::TestCase assert_not_nil assigns(:total_hours) assert_equal "162.90", "%.2f" % assigns(:total_hours) # display all time by default - assert_equal '2007-03-11'.to_date, assigns(:from) + assert_equal '2007-03-12'.to_date, assigns(:from) assert_equal '2007-04-22'.to_date, assigns(:to) end @@ -325,8 +325,8 @@ class TimelogControllerTest < ActionController::TestCase assert_equal 2, assigns(:entries).size assert_not_nil assigns(:total_hours) assert_equal 154.25, assigns(:total_hours) - # display all time by default - assert_equal '2007-03-11'.to_date, assigns(:from) + # display all time based on what's been logged + assert_equal '2007-03-12'.to_date, assigns(:from) assert_equal '2007-04-22'.to_date, assigns(:to) end diff --git a/test/unit/time_entry_test.rb b/test/unit/time_entry_test.rb index a541fc41f..3069f9368 100644 --- a/test/unit/time_entry_test.rb +++ b/test/unit/time_entry_test.rb @@ -50,17 +50,50 @@ class TimeEntryTest < ActiveSupport::TestCase end context "#earilest_date_for_project" do - should "return the lowest spent_on value that is visible to the current user" do + setup do User.current = nil - assert_equal "2007-03-12", TimeEntry.earilest_date_for_project.to_s + @public_project = Project.generate!(:is_public => true) + @issue = Issue.generate_for_project!(@public_project) + TimeEntry.generate!(:spent_on => '2010-01-01', + :issue => @issue, + :project => @public_project) end + + context "without a project" do + should "return the lowest spent_on value that is visible to the current user" do + assert_equal "2007-03-12", TimeEntry.earilest_date_for_project.to_s + end + end + + context "with a project" do + should "return the lowest spent_on value that is visible to the current user for that project and it's subprojects only" do + assert_equal "2010-01-01", TimeEntry.earilest_date_for_project(@public_project).to_s + end + end + end context "#latest_date_for_project" do - should "return the highest spent_on value that is visible to the current user" do + setup do User.current = nil - assert_equal "2007-04-22", TimeEntry.latest_date_for_project.to_s + @public_project = Project.generate!(:is_public => true) + @issue = Issue.generate_for_project!(@public_project) + TimeEntry.generate!(:spent_on => '2010-01-01', + :issue => @issue, + :project => @public_project) end - end - + + context "without a project" do + should "return the highest spent_on value that is visible to the current user" do + assert_equal "2010-01-01", TimeEntry.latest_date_for_project.to_s + end + end + + context "with a project" do + should "return the highest spent_on value that is visible to the current user for that project and it's subprojects only" do + project = Project.find(1) + assert_equal "2007-04-22", TimeEntry.latest_date_for_project(project).to_s + end + end + end end From be6e0927f3ce95e751bd3c6f4a61de6979898012 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Wed, 15 Sep 2010 16:50:25 +0000 Subject: [PATCH 52/72] Refactor: Split VersionsController#edit into #edit and #update git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4088 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/versions_controller.rb | 5 ++++- app/views/versions/edit.rhtml | 2 +- config/routes.rb | 3 +++ lib/redmine.rb | 2 +- test/functional/versions_controller_test.rb | 4 ++-- test/integration/routing_test.rb | 6 ++++++ 6 files changed, 17 insertions(+), 5 deletions(-) diff --git a/app/controllers/versions_controller.rb b/app/controllers/versions_controller.rb index dd01da95b..96c240c50 100644 --- a/app/controllers/versions_controller.rb +++ b/app/controllers/versions_controller.rb @@ -87,8 +87,11 @@ class VersionsController < ApplicationController end end end - + def edit + end + + def update if request.post? && params[:version] attributes = params[:version].dup attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing']) diff --git a/app/views/versions/edit.rhtml b/app/views/versions/edit.rhtml index 1556ebba1..8c437eb5e 100644 --- a/app/views/versions/edit.rhtml +++ b/app/views/versions/edit.rhtml @@ -1,6 +1,6 @@

    <%=l(:label_version)%>

    -<% labelled_tabular_form_for :version, @version, :url => { :action => 'edit' } do |f| %> +<% labelled_tabular_form_for :version, @version, :url => { :action => 'update', :id => @version } do |f| %> <%= render :partial => 'form', :locals => { :f => f } %> <%= submit_tag l(:button_save) %> <% end %> diff --git a/config/routes.rb b/config/routes.rb index c85f23eb2..9f9a4e3f0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -205,7 +205,10 @@ ActionController::Routing::Routes.draw do |map| map.with_options :controller => 'versions' do |versions| versions.connect 'projects/:project_id/versions/new', :action => 'new' versions.connect 'projects/:project_id/roadmap', :action => 'index' + versions.connect 'versions/:action/:id', :conditions => {:method => :get} + versions.with_options :conditions => {:method => :post} do |version_actions| + version_actions.connect 'versions/update/:id', :action => 'update' version_actions.connect 'projects/:project_id/versions/close_completed', :action => 'close_completed' end end diff --git a/lib/redmine.rb b/lib/redmine.rb index f8f564220..2b27c206b 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -50,7 +50,7 @@ Redmine::AccessControl.map do |map| map.permission :edit_project, {:projects => [:settings, :edit, :update]}, :require => :member map.permission :select_project_modules, {:projects => :modules}, :require => :member map.permission :manage_members, {:projects => :settings, :members => [:new, :edit, :destroy, :autocomplete_for_member]}, :require => :member - map.permission :manage_versions, {:projects => :settings, :versions => [:new, :edit, :close_completed, :destroy]}, :require => :member + map.permission :manage_versions, {:projects => :settings, :versions => [:new, :edit, :update, :close_completed, :destroy]}, :require => :member map.permission :add_subprojects, {:projects => [:new, :create]}, :require => :member map.project_module :issue_tracking do |map| diff --git a/test/functional/versions_controller_test.rb b/test/functional/versions_controller_test.rb index f738794ae..9e8cdf90d 100644 --- a/test/functional/versions_controller_test.rb +++ b/test/functional/versions_controller_test.rb @@ -113,9 +113,9 @@ class VersionsControllerTest < ActionController::TestCase assert_not_nil Version.find_by_status('closed') end - def test_post_edit + def test_post_update @request.session[:user_id] = 2 - post :edit, :id => 2, + post :update, :id => 2, :version => { :name => 'New version name', :effective_date => Date.today.strftime("%Y-%m-%d")} assert_redirected_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => 'ecookbook' diff --git a/test/integration/routing_test.rb b/test/integration/routing_test.rb index 38c1b2a0e..ba4948008 100644 --- a/test/integration/routing_test.rb +++ b/test/integration/routing_test.rb @@ -251,10 +251,16 @@ class RoutingTest < ActionController::IntegrationTest should_route :post, "/users/567/memberships/12/destroy", :controller => 'users', :action => 'destroy_membership', :id => '567', :membership_id => '12' end + # TODO: should they all be scoped under /projects/:project_id ? context "versions" do should_route :get, "/projects/foo/versions/new", :controller => 'versions', :action => 'new', :project_id => 'foo' + should_route :get, "/versions/show/1", :controller => 'versions', :action => 'show', :id => '1' + should_route :get, "/versions/edit/1", :controller => 'versions', :action => 'edit', :id => '1' should_route :post, "/projects/foo/versions/new", :controller => 'versions', :action => 'new', :project_id => 'foo' + should_route :post, "/versions/update/1", :controller => 'versions', :action => 'update', :id => '1' + + should_route :delete, "/versions/destroy/1", :controller => 'versions', :action => 'destroy', :id => '1' end context "wiki (singular, project's pages)" do From bde8ab84f27426e425ba2d6a7e733571fcd5242e Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Wed, 15 Sep 2010 22:43:30 +0000 Subject: [PATCH 53/72] Use the full path to the partials When trying to use the issue form in a plugin, it would try to use the relative path to the partials which were incorrect. Example: would render 'my_plugin_views/attributes' git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4089 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/views/issues/_attributes.rhtml | 2 +- app/views/issues/_form.rhtml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/issues/_attributes.rhtml b/app/views/issues/_attributes.rhtml index 455eb77b2..f9f3f94ea 100644 --- a/app/views/issues/_attributes.rhtml +++ b/app/views/issues/_attributes.rhtml @@ -40,6 +40,6 @@
    -<%= render :partial => 'form_custom_fields' %> +<%= render :partial => 'issues/form_custom_fields' %> <% end %> diff --git a/app/views/issues/_form.rhtml b/app/views/issues/_form.rhtml index 2bee7d36c..0b1a07499 100644 --- a/app/views/issues/_form.rhtml +++ b/app/views/issues/_form.rhtml @@ -20,7 +20,7 @@
    - <%= render :partial => 'attributes' %> + <%= render :partial => 'issues/attributes' %>
    <% if @issue.new_record? %> From 969bf2107b2ebcdb480cbb56f523ecb72643146d Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Thu, 16 Sep 2010 18:27:33 +0000 Subject: [PATCH 54/72] Refactor: split VersionsController#new into #new and #create git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4090 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/versions_controller.rb | 19 +++++++++++++++---- app/views/issues/_attributes.rhtml | 2 +- app/views/versions/new.html.erb | 4 ++-- config/routes.rb | 1 + lib/redmine.rb | 2 +- test/functional/versions_controller_test.rb | 8 ++++---- test/integration/routing_test.rb | 2 +- 7 files changed, 25 insertions(+), 13 deletions(-) diff --git a/app/controllers/versions_controller.rb b/app/controllers/versions_controller.rb index 96c240c50..331935d9b 100644 --- a/app/controllers/versions_controller.rb +++ b/app/controllers/versions_controller.rb @@ -18,9 +18,9 @@ class VersionsController < ApplicationController menu_item :roadmap model_object Version - before_filter :find_model_object, :except => [:index, :new, :close_completed] - before_filter :find_project_from_association, :except => [:index, :new, :close_completed] - before_filter :find_project, :only => [:index, :new, :close_completed] + before_filter :find_model_object, :except => [:index, :new, :create, :close_completed] + before_filter :find_project_from_association, :except => [:index, :new, :create, :close_completed] + before_filter :find_project, :only => [:index, :new, :create, :close_completed] before_filter :authorize helper :custom_fields @@ -63,6 +63,17 @@ class VersionsController < ApplicationController attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing']) @version.attributes = attributes end + end + + def create + # TODO: refactor with code above in #new + @version = @project.versions.build + if params[:version] + attributes = params[:version].dup + attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing']) + @version.attributes = attributes + end + if request.post? if @version.save respond_to do |format| @@ -79,7 +90,7 @@ class VersionsController < ApplicationController end else respond_to do |format| - format.html + format.html { render :action => 'new' } format.js do render(:update) {|page| page.alert(@version.errors.full_messages.join('\n')) } end diff --git a/app/views/issues/_attributes.rhtml b/app/views/issues/_attributes.rhtml index f9f3f94ea..e10858b09 100644 --- a/app/views/issues/_attributes.rhtml +++ b/app/views/issues/_attributes.rhtml @@ -23,7 +23,7 @@ <%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'), l(:label_version_new), 'version[name]', - {:controller => 'versions', :action => 'new', :project_id => @project}, + {:controller => 'versions', :action => 'create', :project_id => @project}, :title => l(:label_version_new), :tabindex => 200) if authorize_for('versions', 'new') %>

    diff --git a/app/views/versions/new.html.erb b/app/views/versions/new.html.erb index a7ef03e76..9fbd94178 100644 --- a/app/views/versions/new.html.erb +++ b/app/views/versions/new.html.erb @@ -1,6 +1,6 @@

    <%=l(:label_version_new)%>

    -<% labelled_tabular_form_for :version, @version, :url => { :action => 'new' } do |f| %> +<% labelled_tabular_form_for :version, @version, :url => { :action => 'create', :project_id => @project } do |f| %> <%= render :partial => 'versions/form', :locals => { :f => f } %> <%= submit_tag l(:button_create) %> -<% end %> \ No newline at end of file +<% end %> diff --git a/config/routes.rb b/config/routes.rb index 9f9a4e3f0..3197df01a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -208,6 +208,7 @@ ActionController::Routing::Routes.draw do |map| versions.connect 'versions/:action/:id', :conditions => {:method => :get} versions.with_options :conditions => {:method => :post} do |version_actions| + version_actions.connect 'projects/:project_id/versions', :action => 'create' version_actions.connect 'versions/update/:id', :action => 'update' version_actions.connect 'projects/:project_id/versions/close_completed', :action => 'close_completed' end diff --git a/lib/redmine.rb b/lib/redmine.rb index 2b27c206b..8acae48a8 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -50,7 +50,7 @@ Redmine::AccessControl.map do |map| map.permission :edit_project, {:projects => [:settings, :edit, :update]}, :require => :member map.permission :select_project_modules, {:projects => :modules}, :require => :member map.permission :manage_members, {:projects => :settings, :members => [:new, :edit, :destroy, :autocomplete_for_member]}, :require => :member - map.permission :manage_versions, {:projects => :settings, :versions => [:new, :edit, :update, :close_completed, :destroy]}, :require => :member + map.permission :manage_versions, {:projects => :settings, :versions => [:new, :create, :edit, :update, :close_completed, :destroy]}, :require => :member map.permission :add_subprojects, {:projects => [:new, :create]}, :require => :member map.project_module :issue_tracking do |map| diff --git a/test/functional/versions_controller_test.rb b/test/functional/versions_controller_test.rb index 9e8cdf90d..1b33ba816 100644 --- a/test/functional/versions_controller_test.rb +++ b/test/functional/versions_controller_test.rb @@ -75,10 +75,10 @@ class VersionsControllerTest < ActionController::TestCase assert_tag :tag => 'h2', :content => /1.0/ end - def test_new + def test_create @request.session[:user_id] = 2 # manager assert_difference 'Version.count' do - post :new, :project_id => '1', :version => {:name => 'test_add_version'} + post :create, :project_id => '1', :version => {:name => 'test_add_version'} end assert_redirected_to '/projects/ecookbook/settings/versions' version = Version.find_by_name('test_add_version') @@ -86,10 +86,10 @@ class VersionsControllerTest < ActionController::TestCase assert_equal 1, version.project_id end - def test_new_from_issue_form + def test_create_from_issue_form @request.session[:user_id] = 2 # manager assert_difference 'Version.count' do - xhr :post, :new, :project_id => '1', :version => {:name => 'test_add_version_from_issue_form'} + xhr :post, :create, :project_id => '1', :version => {:name => 'test_add_version_from_issue_form'} end assert_response :success assert_select_rjs :replace, 'issue_fixed_version_id' diff --git a/test/integration/routing_test.rb b/test/integration/routing_test.rb index ba4948008..794a7780e 100644 --- a/test/integration/routing_test.rb +++ b/test/integration/routing_test.rb @@ -257,7 +257,7 @@ class RoutingTest < ActionController::IntegrationTest should_route :get, "/versions/show/1", :controller => 'versions', :action => 'show', :id => '1' should_route :get, "/versions/edit/1", :controller => 'versions', :action => 'edit', :id => '1' - should_route :post, "/projects/foo/versions/new", :controller => 'versions', :action => 'new', :project_id => 'foo' + should_route :post, "/projects/foo/versions", :controller => 'versions', :action => 'create', :project_id => 'foo' should_route :post, "/versions/update/1", :controller => 'versions', :action => 'update', :id => '1' should_route :delete, "/versions/destroy/1", :controller => 'versions', :action => 'destroy', :id => '1' From c4d44af54c967d77b1f908de816453aed05a72e0 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Barth Date: Thu, 16 Sep 2010 19:28:04 +0000 Subject: [PATCH 55/72] Fixed non standard SQL syntax. #6413 Contributed by Juan G git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4091 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/models/issue.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/issue.rb b/app/models/issue.rb index 80db48108..2b7362589 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -846,7 +846,7 @@ class Issue < ActiveRecord::Base j.id as #{select_field}, count(i.id) as total from - #{Issue.table_name} i, #{IssueStatus.table_name} s, #{joins} as j + #{Issue.table_name} i, #{IssueStatus.table_name} s, #{joins} j where i.status_id=s.id and #{where} From 0d967c0572762026210db65632dea8649e308ad0 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Barth Date: Thu, 16 Sep 2010 21:26:30 +0000 Subject: [PATCH 56/72] Fix links to activity pages broken with r4047. #6392 Contributed by Felix Schafer git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4092 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/helpers/application_helper.rb | 2 +- app/views/wiki/special_page_index.rhtml | 4 ++-- special_page_index.rhtml | 26 +++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 special_page_index.rhtml diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 34b17c760..eecbc5b91 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -326,7 +326,7 @@ module ApplicationHelper def time_tag(time) text = distance_of_time_in_words(Time.now, time) if @project - link_to(text, {:controller => 'projects', :action => 'activity', :id => @project, :from => time.to_date}, :title => format_time(time)) + link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => time.to_date}, :title => format_time(time)) else content_tag('acronym', text, :title => format_time(time)) end diff --git a/app/views/wiki/special_page_index.rhtml b/app/views/wiki/special_page_index.rhtml index b3ad10019..e2d1e44ad 100644 --- a/app/views/wiki/special_page_index.rhtml +++ b/app/views/wiki/special_page_index.rhtml @@ -16,11 +16,11 @@ <% unless @pages.empty? %> <% other_formats_links do |f| %> - <%= f.link_to 'Atom', :url => {:controller => 'projects', :action => 'activity', :id => @project, :show_wiki_edits => 1, :key => User.current.rss_key} %> + <%= f.link_to 'Atom', :url => {:controller => 'activities', :action => 'index', :id => @project, :show_wiki_edits => 1, :key => User.current.rss_key} %> <%= f.link_to('HTML', :url => {:action => 'special', :page => 'export'}) if User.current.allowed_to?(:export_wiki_pages, @project) %> <% end %> <% end %> <% content_for :header_tags do %> -<%= auto_discovery_link_tag(:atom, :controller => 'projects', :action => 'activity', :id => @project, :show_wiki_edits => 1, :format => 'atom', :key => User.current.rss_key) %> +<%= auto_discovery_link_tag(:atom, :controller => 'activities', :action => 'index', :id => @project, :show_wiki_edits => 1, :format => 'atom', :key => User.current.rss_key) %> <% end %> diff --git a/special_page_index.rhtml b/special_page_index.rhtml new file mode 100644 index 000000000..e2d1e44ad --- /dev/null +++ b/special_page_index.rhtml @@ -0,0 +1,26 @@ +
    +<%= watcher_tag(@wiki, User.current) %> +
    + +

    <%= l(:label_index_by_title) %>

    + +<% if @pages.empty? %> +

    <%= l(:label_no_data) %>

    +<% end %> + +<%= render_page_hierarchy(@pages_by_parent_id) %> + +<% content_for :sidebar do %> + <%= render :partial => 'sidebar' %> +<% end %> + +<% unless @pages.empty? %> +<% other_formats_links do |f| %> + <%= f.link_to 'Atom', :url => {:controller => 'activities', :action => 'index', :id => @project, :show_wiki_edits => 1, :key => User.current.rss_key} %> + <%= f.link_to('HTML', :url => {:action => 'special', :page => 'export'}) if User.current.allowed_to?(:export_wiki_pages, @project) %> +<% end %> +<% end %> + +<% content_for :header_tags do %> +<%= auto_discovery_link_tag(:atom, :controller => 'activities', :action => 'index', :id => @project, :show_wiki_edits => 1, :format => 'atom', :key => User.current.rss_key) %> +<% end %> From 35dba0f412b7aa29a839869fdb9e020585dd89c5 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Barth Date: Thu, 16 Sep 2010 21:33:49 +0000 Subject: [PATCH 57/72] Fixed: pressing enter in filters should result to an 'apply', not a 'save'. #2285 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4093 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/views/issues/index.rhtml | 1 + app/views/queries/_filters.rhtml | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/app/views/issues/index.rhtml b/app/views/issues/index.rhtml index ee6514d0b..ddd5d9080 100644 --- a/app/views/issues/index.rhtml +++ b/app/views/issues/index.rhtml @@ -39,6 +39,7 @@ { :url => { :set_filter => 1 }, :before => 'selectAllOptions("selected_columns");', :update => "content", + :complete => "apply_filters_observer()", :with => "Form.serialize('query_form')" }, :class => 'icon icon-checked' %> diff --git a/app/views/queries/_filters.rhtml b/app/views/queries/_filters.rhtml index 58ea1524c..20640eb8d 100644 --- a/app/views/queries/_filters.rhtml +++ b/app/views/queries/_filters.rhtml @@ -53,6 +53,18 @@ function toggle_multi_select(field) { select.multiple = true; } } + +function apply_filters_observer() { + $$("#query_form input[type=text]").invoke("observe", "keypress", function(e){ + if(e.keyCode == Event.KEY_RETURN) { + <%= remote_function(:url => { :set_filter => 1}, + :update => "content", + :with => "Form.serialize('query_form')", + :complete => "e.stop(); apply_filters_observer()") %> + } + }); +} +Event.observe(document,"dom:loaded", apply_filters_observer); //]]> From ebb445c36468529cc7873136469a6162660ad253 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Fri, 17 Sep 2010 04:06:40 +0000 Subject: [PATCH 58/72] Use the relative_url_root when generating asset_paths. #3935 Contributed by Daniel S from Redmine. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4094 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- .../engines/lib/engines/rails_extensions/asset_helpers.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vendor/plugins/engines/lib/engines/rails_extensions/asset_helpers.rb b/vendor/plugins/engines/lib/engines/rails_extensions/asset_helpers.rb index 9fd1742c4..a4a9266f2 100644 --- a/vendor/plugins/engines/lib/engines/rails_extensions/asset_helpers.rb +++ b/vendor/plugins/engines/lib/engines/rails_extensions/asset_helpers.rb @@ -109,11 +109,11 @@ module Engines::RailsExtensions::AssetHelpers # Returns the publicly-addressable relative URI for the given asset, type and plugin def self.plugin_asset_path(plugin_name, type, asset) raise "No plugin called '#{plugin_name}' - please use the full name of a loaded plugin." if Engines.plugins[plugin_name].nil? - "/#{Engines.plugins[plugin_name].public_asset_directory}/#{type}/#{asset}" + "#{ActionController::Base.relative_url_root}/#{Engines.plugins[plugin_name].public_asset_directory}/#{type}/#{asset}" end end module ::ActionView::Helpers::AssetTagHelper #:nodoc: include Engines::RailsExtensions::AssetHelpers -end \ No newline at end of file +end From b8ea55661631be2e7aa535fee7dc43fd3c4ca75d Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Fri, 17 Sep 2010 04:22:46 +0000 Subject: [PATCH 59/72] Fix the new subproject link on project overview. #6388 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contributed by Felix Schäfer. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4095 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/views/projects/show.rhtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/show.rhtml b/app/views/projects/show.rhtml index 0ad9a11da..cf3814b77 100644 --- a/app/views/projects/show.rhtml +++ b/app/views/projects/show.rhtml @@ -1,6 +1,6 @@
    <% if User.current.allowed_to?(:add_subprojects, @project) %> - <%= link_to l(:label_subproject_new), {:controller => 'projects', :action => 'add', :parent_id => @project}, :class => 'icon icon-add' %> + <%= link_to l(:label_subproject_new), {:controller => 'projects', :action => 'new', :parent_id => @project}, :class => 'icon icon-add' %> <% end %>
    From eb1f58f9624a22d4b5a22f1510c858fa6d5cf7cb Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Fri, 17 Sep 2010 04:31:17 +0000 Subject: [PATCH 60/72] Added some documentation about the Rails Logger. #6135 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contributed by Felix Schäfer git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4096 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- config/environments/production.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/config/environments/production.rb b/config/environments/production.rb index cfd2aa0f2..ac11f5219 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -4,9 +4,14 @@ # Code is not reloaded between requests config.cache_classes = true +##### +# Customize the default logger (http://ruby-doc.org/core/classes/Logger.html) +# # Use a different logger for distributed setups # config.logger = SyslogLogger.new - +# +# Rotate logs bigger than 1MB, keeps no more than 7 rotated logs around. +# config.logger = Logger.new(config.log_path, 7, 1048576) # Full error reports are disabled and caching is turned on config.action_controller.consider_all_requests_local = false From bd193a026df09ba231b241c24b43762810af7871 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Fri, 17 Sep 2010 15:55:08 +0000 Subject: [PATCH 61/72] Refactor: convert VersionsController to a REST resource. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4097 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/versions_controller.rb | 4 ++-- app/views/projects/settings/_versions.rhtml | 4 ++-- app/views/versions/edit.rhtml | 2 +- app/views/versions/new.html.erb | 2 +- config/routes.rb | 17 +++++------------ test/functional/versions_controller_test.rb | 6 +++--- 6 files changed, 14 insertions(+), 21 deletions(-) diff --git a/app/controllers/versions_controller.rb b/app/controllers/versions_controller.rb index 331935d9b..48612c7b8 100644 --- a/app/controllers/versions_controller.rb +++ b/app/controllers/versions_controller.rb @@ -103,7 +103,7 @@ class VersionsController < ApplicationController end def update - if request.post? && params[:version] + if request.put? && params[:version] attributes = params[:version].dup attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing']) if @version.update_attributes(attributes) @@ -114,7 +114,7 @@ class VersionsController < ApplicationController end def close_completed - if request.post? + if request.put? @project.close_completed_versions end redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project diff --git a/app/views/projects/settings/_versions.rhtml b/app/views/projects/settings/_versions.rhtml index dc81f6265..d41929c2d 100644 --- a/app/views/projects/settings/_versions.rhtml +++ b/app/views/projects/settings/_versions.rhtml @@ -21,7 +21,7 @@
    @@ -34,7 +34,7 @@
    <% if @project.versions.any? %> - <%= link_to l(:label_close_versions), {:controller => 'versions', :action => 'close_completed', :project_id => @project}, :method => :post %> + <%= link_to l(:label_close_versions), close_completed_project_versions_path(@project), :method => :put %> <% end %>
    diff --git a/app/views/versions/edit.rhtml b/app/views/versions/edit.rhtml index 8c437eb5e..8724fe62a 100644 --- a/app/views/versions/edit.rhtml +++ b/app/views/versions/edit.rhtml @@ -1,6 +1,6 @@

    <%=l(:label_version)%>

    -<% labelled_tabular_form_for :version, @version, :url => { :action => 'update', :id => @version } do |f| %> +<% labelled_tabular_form_for :version, @version, :url => project_version_path(@project, @version), :html => {:method => :put} do |f| %> <%= render :partial => 'form', :locals => { :f => f } %> <%= submit_tag l(:button_save) %> <% end %> diff --git a/app/views/versions/new.html.erb b/app/views/versions/new.html.erb index 9fbd94178..d60468159 100644 --- a/app/views/versions/new.html.erb +++ b/app/views/versions/new.html.erb @@ -1,6 +1,6 @@

    <%=l(:label_version_new)%>

    -<% labelled_tabular_form_for :version, @version, :url => { :action => 'create', :project_id => @project } do |f| %> +<% labelled_tabular_form_for :version, @version, :url => project_versions_path(@project) do |f| %> <%= render :partial => 'versions/form', :locals => { :f => f } %> <%= submit_tag l(:button_create) %> <% end %> diff --git a/config/routes.rb b/config/routes.rb index 3197df01a..00a2c3608 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -173,6 +173,9 @@ ActionController::Routing::Routes.draw do |map| end end + # For nice "roadmap" in the url for the index action + map.connect 'projects/:project_id/roadmap', :controller => 'versions', :action => 'index' + map.resources :projects, :member => { :copy => [:get, :post], :settings => :get, @@ -182,6 +185,7 @@ ActionController::Routing::Routes.draw do |map| } do |project| project.resource :project_enumerations, :as => 'enumerations', :only => [:update, :destroy] project.resources :files, :only => [:index, :new, :create] + project.resources :versions, :collection => {:close_completed => :put} end # Destroy uses a get request to prompt the user before the actual DELETE request @@ -201,19 +205,8 @@ ActionController::Routing::Routes.draw do |map| activity.connect 'activity', :id => nil activity.connect 'activity.:format', :id => nil end + - map.with_options :controller => 'versions' do |versions| - versions.connect 'projects/:project_id/versions/new', :action => 'new' - versions.connect 'projects/:project_id/roadmap', :action => 'index' - versions.connect 'versions/:action/:id', :conditions => {:method => :get} - - versions.with_options :conditions => {:method => :post} do |version_actions| - version_actions.connect 'projects/:project_id/versions', :action => 'create' - version_actions.connect 'versions/update/:id', :action => 'update' - version_actions.connect 'projects/:project_id/versions/close_completed', :action => 'close_completed' - end - end - map.with_options :controller => 'issue_categories' do |categories| categories.connect 'projects/:project_id/issue_categories/new', :action => 'new' end diff --git a/test/functional/versions_controller_test.rb b/test/functional/versions_controller_test.rb index 1b33ba816..e4ac5c068 100644 --- a/test/functional/versions_controller_test.rb +++ b/test/functional/versions_controller_test.rb @@ -108,14 +108,14 @@ class VersionsControllerTest < ActionController::TestCase def test_close_completed Version.update_all("status = 'open'") @request.session[:user_id] = 2 - post :close_completed, :project_id => 'ecookbook' + put :close_completed, :project_id => 'ecookbook' assert_redirected_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => 'ecookbook' assert_not_nil Version.find_by_status('closed') end def test_post_update @request.session[:user_id] = 2 - post :update, :id => 2, + put :update, :id => 2, :version => { :name => 'New version name', :effective_date => Date.today.strftime("%Y-%m-%d")} assert_redirected_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => 'ecookbook' @@ -126,7 +126,7 @@ class VersionsControllerTest < ActionController::TestCase def test_destroy @request.session[:user_id] = 2 - post :destroy, :id => 3 + delete :destroy, :id => 3 assert_redirected_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => 'ecookbook' assert_nil Version.find_by_id(3) end From 5fdfe02b3a3e8cc396f38c4af75c586ae584a4d2 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Fri, 17 Sep 2010 16:11:43 +0000 Subject: [PATCH 62/72] Refactor: add VersionsController#status_by to the resource. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4098 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/views/versions/_issue_counts.rhtml | 2 +- config/routes.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/versions/_issue_counts.rhtml b/app/views/versions/_issue_counts.rhtml index 4bab5c659..38f3edbcb 100644 --- a/app/views/versions/_issue_counts.rhtml +++ b/app/views/versions/_issue_counts.rhtml @@ -5,7 +5,7 @@ select_tag('status_by', status_by_options_for_select(criteria), :id => 'status_by_select', - :onchange => remote_function(:url => { :action => :status_by, :id => version }, + :onchange => remote_function(:url => status_by_project_version_path(version.project, version), :with => "Form.serialize('status_by_form')"))) %> <% if counts.empty? %> diff --git a/config/routes.rb b/config/routes.rb index 00a2c3608..062bb586e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -185,7 +185,7 @@ ActionController::Routing::Routes.draw do |map| } do |project| project.resource :project_enumerations, :as => 'enumerations', :only => [:update, :destroy] project.resources :files, :only => [:index, :new, :create] - project.resources :versions, :collection => {:close_completed => :put} + project.resources :versions, :collection => {:close_completed => :put}, :member => {:status_by => :post} end # Destroy uses a get request to prompt the user before the actual DELETE request From ffdaead5b72966c7e4f74cca4d5951cc3cefc4c4 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Barth Date: Fri, 17 Sep 2010 23:13:26 +0000 Subject: [PATCH 63/72] Removed file mistakenly added in r4092. #6392 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4099 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- special_page_index.rhtml | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 special_page_index.rhtml diff --git a/special_page_index.rhtml b/special_page_index.rhtml deleted file mode 100644 index e2d1e44ad..000000000 --- a/special_page_index.rhtml +++ /dev/null @@ -1,26 +0,0 @@ -
    -<%= watcher_tag(@wiki, User.current) %> -
    - -

    <%= l(:label_index_by_title) %>

    - -<% if @pages.empty? %> -

    <%= l(:label_no_data) %>

    -<% end %> - -<%= render_page_hierarchy(@pages_by_parent_id) %> - -<% content_for :sidebar do %> - <%= render :partial => 'sidebar' %> -<% end %> - -<% unless @pages.empty? %> -<% other_formats_links do |f| %> - <%= f.link_to 'Atom', :url => {:controller => 'activities', :action => 'index', :id => @project, :show_wiki_edits => 1, :key => User.current.rss_key} %> - <%= f.link_to('HTML', :url => {:action => 'special', :page => 'export'}) if User.current.allowed_to?(:export_wiki_pages, @project) %> -<% end %> -<% end %> - -<% content_for :header_tags do %> -<%= auto_discovery_link_tag(:atom, :controller => 'activities', :action => 'index', :id => @project, :show_wiki_edits => 1, :format => 'atom', :key => User.current.rss_key) %> -<% end %> From 099761d8fbba3d33cd34c9ef24bcdbe09d84232d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Barth Date: Sat, 18 Sep 2010 16:50:08 +0000 Subject: [PATCH 64/72] Fixes switching between inline and side-by-side in diff view with path. #6242 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contributed by Felix Schäfer git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4100 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/views/repositories/diff.rhtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/repositories/diff.rhtml b/app/views/repositories/diff.rhtml index 36e86403f..24f92a540 100644 --- a/app/views/repositories/diff.rhtml +++ b/app/views/repositories/diff.rhtml @@ -1,7 +1,7 @@

    <%= l(:label_revision) %> <%= format_revision(@rev_to) + ':' if @rev_to %><%= format_revision(@rev) %> <%=h @path %>

    -<% form_tag({:path => @path}, :method => 'get') do %> +<% form_tag({:path => to_path_param(@path)}, :method => 'get') do %> <%= hidden_field_tag('rev', params[:rev]) if params[:rev] %> <%= hidden_field_tag('rev_to', params[:rev_to]) if params[:rev_to] %>

    From 81a28e9ffa0d4a2279a6a2b19b3fd60f12ccdd31 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Barth Date: Sat, 18 Sep 2010 17:30:22 +0000 Subject: [PATCH 65/72] Adds a link to edit the associated wiki page on the version view. #4416 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contributed by Felix Schäfer git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4101 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/views/versions/show.rhtml | 1 + config/locales/bg.yml | 1 + config/locales/bs.yml | 1 + config/locales/ca.yml | 1 + config/locales/cs.yml | 1 + config/locales/da.yml | 1 + config/locales/de.yml | 1 + config/locales/el.yml | 1 + config/locales/en-GB.yml | 1 + config/locales/en.yml | 1 + config/locales/es.yml | 1 + config/locales/eu.yml | 1 + config/locales/fi.yml | 1 + config/locales/fr.yml | 1 + config/locales/gl.yml | 1 + config/locales/he.yml | 1 + config/locales/hr.yml | 1 + config/locales/hu.yml | 1 + config/locales/id.yml | 1 + config/locales/it.yml | 1 + config/locales/ja.yml | 1 + config/locales/ko.yml | 1 + config/locales/lt.yml | 1 + config/locales/lv.yml | 1 + config/locales/mk.yml | 1 + config/locales/mn.yml | 1 + config/locales/nl.yml | 1 + config/locales/no.yml | 1 + config/locales/pl.yml | 1 + config/locales/pt-BR.yml | 1 + config/locales/pt.yml | 1 + config/locales/ro.yml | 1 + config/locales/ru.yml | 1 + config/locales/sk.yml | 1 + config/locales/sl.yml | 1 + config/locales/sr-YU.yml | 1 + config/locales/sr.yml | 1 + config/locales/sv.yml | 1 + config/locales/th.yml | 1 + config/locales/tr.yml | 1 + config/locales/uk.yml | 1 + config/locales/vi.yml | 1 + config/locales/zh-TW.yml | 1 + config/locales/zh.yml | 1 + 44 files changed, 44 insertions(+) diff --git a/app/views/versions/show.rhtml b/app/views/versions/show.rhtml index 79de5984b..6b2b09d0f 100644 --- a/app/views/versions/show.rhtml +++ b/app/views/versions/show.rhtml @@ -1,5 +1,6 @@

    <%= link_to_if_authorized l(:button_edit), {:controller => 'versions', :action => 'edit', :id => @version}, :class => 'icon icon-edit' %> +<%= link_to_if_authorized(l(:button_edit_associated_wikipage, :page_title => @version.wiki_page_title), {:controller => 'wiki', :action => 'edit', :page => Wiki.titleize(@version.wiki_page_title)}, :class => 'icon icon-edit') unless @version.wiki_page_title.blank? || @project.wiki.nil? %> <%= call_hook(:view_versions_show_contextual, { :version => @version, :project => @project }) %>
    diff --git a/config/locales/bg.yml b/config/locales/bg.yml index 0a8c427ef..ceee5c3cf 100644 --- a/config/locales/bg.yml +++ b/config/locales/bg.yml @@ -909,3 +909,4 @@ bg: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/bs.yml b/config/locales/bs.yml index 741f46232..b855be9e9 100644 --- a/config/locales/bs.yml +++ b/config/locales/bs.yml @@ -929,3 +929,4 @@ bs: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 50a389156..6ba0a8bea 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -918,3 +918,4 @@ ca: field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/cs.yml b/config/locales/cs.yml index 96fb608b6..71591fa46 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -915,3 +915,4 @@ cs: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/da.yml b/config/locales/da.yml index 38d9d6646..b6270f0d1 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -931,3 +931,4 @@ da: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/de.yml b/config/locales/de.yml index 5ec7073b3..3a5ee094e 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -803,6 +803,7 @@ de: button_create_and_continue: Anlegen + nächstes Ticket button_test: Testen button_edit: Bearbeiten + button_edit_associated_wikipage: "Zugehörige Wikiseite bearbeiten: {{page_title}}" button_add: Hinzufügen button_change: Wechseln button_apply: Anwenden diff --git a/config/locales/el.yml b/config/locales/el.yml index 22bc6a459..d0608e851 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -915,3 +915,4 @@ el: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 8c0b28cf3..c0b520dfd 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -919,3 +919,4 @@ en-GB: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/en.yml b/config/locales/en.yml index cc4e31185..81a26c82c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -786,6 +786,7 @@ en: button_create_and_continue: Create and continue button_test: Test button_edit: Edit + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" button_add: Add button_change: Change button_apply: Apply diff --git a/config/locales/es.yml b/config/locales/es.yml index 3f848f1d1..1828558bf 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -955,3 +955,4 @@ es: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/eu.yml b/config/locales/eu.yml index 24ef38ea7..64881fe6f 100644 --- a/config/locales/eu.yml +++ b/config/locales/eu.yml @@ -919,3 +919,4 @@ eu: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/fi.yml b/config/locales/fi.yml index 985ab8b2a..8b56c94da 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -940,3 +940,4 @@ fi: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 7f96b6112..0a1ee46ea 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -933,3 +933,4 @@ fr: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Modifier la page de Wiki associée: {{page_title}}" diff --git a/config/locales/gl.yml b/config/locales/gl.yml index b717d8d60..b5345de69 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -931,3 +931,4 @@ gl: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/he.yml b/config/locales/he.yml index 86ebe7d3a..8c55b2351 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -920,3 +920,4 @@ he: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/hr.yml b/config/locales/hr.yml index f4a809a9a..77cda672a 100644 --- a/config/locales/hr.yml +++ b/config/locales/hr.yml @@ -922,3 +922,4 @@ hr: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/hu.yml b/config/locales/hu.yml index fe1abdf36..84e714a6b 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -938,3 +938,4 @@ project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/id.yml b/config/locales/id.yml index 7c327833f..ca6444b0c 100644 --- a/config/locales/id.yml +++ b/config/locales/id.yml @@ -923,3 +923,4 @@ id: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/it.yml b/config/locales/it.yml index 8d5aa4ef4..6bd4096a9 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -919,3 +919,4 @@ it: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 09eb36690..fa3346f17 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -940,3 +940,4 @@ ja: enumeration_doc_categories: 文書カテゴリ enumeration_activities: 作業分類 (時間トラッキング) enumeration_system_activity: システム作業分類 + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/ko.yml b/config/locales/ko.yml index eea667d62..771f91cef 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -971,3 +971,4 @@ ko: project_module_calendar: 달력 field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/lt.yml b/config/locales/lt.yml index b12a1107b..27c5466fd 100644 --- a/config/locales/lt.yml +++ b/config/locales/lt.yml @@ -979,3 +979,4 @@ lt: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/lv.yml b/config/locales/lv.yml index 271140cce..5a628e5c5 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -910,3 +910,4 @@ lv: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/mk.yml b/config/locales/mk.yml index c98761865..ac2a043d4 100644 --- a/config/locales/mk.yml +++ b/config/locales/mk.yml @@ -915,3 +915,4 @@ mk: field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/mn.yml b/config/locales/mn.yml index b92538a47..908e8a844 100644 --- a/config/locales/mn.yml +++ b/config/locales/mn.yml @@ -916,3 +916,4 @@ mn: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/nl.yml b/config/locales/nl.yml index e13e67ddf..b5933d222 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -897,3 +897,4 @@ nl: project_module_calendar: Kalender field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/no.yml b/config/locales/no.yml index cc95dae7d..24be1a06e 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -906,3 +906,4 @@ project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 6621ad39e..aade4af0e 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -936,3 +936,4 @@ pl: project_module_calendar: Kalendarz field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index 547074083..7fa9ae2f5 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -939,3 +939,4 @@ pt-BR: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/pt.yml b/config/locales/pt.yml index 28822061a..9a7547ab4 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -923,3 +923,4 @@ pt: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/ro.yml b/config/locales/ro.yml index 85fea1399..aac64cc2b 100644 --- a/config/locales/ro.yml +++ b/config/locales/ro.yml @@ -908,3 +908,4 @@ ro: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 806b5c0ac..68a8fa6ef 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -1032,3 +1032,4 @@ ru: notice_unable_delete_time_entry: Невозможно удалить запись журнала. label_overall_spent_time: Всего затрачено времени + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/sk.yml b/config/locales/sk.yml index 3fd6c41b2..87c50e27a 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -910,3 +910,4 @@ sk: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/sl.yml b/config/locales/sl.yml index 68d8bb089..3b579a824 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -911,3 +911,4 @@ sl: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/sr-YU.yml b/config/locales/sr-YU.yml index c2ac531c9..b48e90223 100644 --- a/config/locales/sr-YU.yml +++ b/config/locales/sr-YU.yml @@ -915,3 +915,4 @@ sr-YU: project_module_calendar: Kalendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/sr.yml b/config/locales/sr.yml index 5b5dfd461..8282a37bf 100644 --- a/config/locales/sr.yml +++ b/config/locales/sr.yml @@ -916,3 +916,4 @@ sr: field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 8055c572d..28ce75e21 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -960,3 +960,4 @@ sv: enumeration_system_activity: Systemaktivitet field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/th.yml b/config/locales/th.yml index 3e855995f..859cf0f08 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -912,3 +912,4 @@ th: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/tr.yml b/config/locales/tr.yml index 43871ffe9..9244d983d 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -938,3 +938,4 @@ tr: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/uk.yml b/config/locales/uk.yml index 89901d1ea..eade7c4a2 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -911,3 +911,4 @@ uk: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/vi.yml b/config/locales/vi.yml index 28351ed26..94fbad869 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -970,3 +970,4 @@ vi: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 8dab78787..26f23b7fa 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -1000,3 +1000,4 @@ enumeration_system_activity: 系統活動 field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/zh.yml b/config/locales/zh.yml index b882d277a..3b788860d 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -933,3 +933,4 @@ zh: project_module_calendar: Calendar field_member_of_group: Member of Group field_assigned_to_role: Member of Role + button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" From 944d1a68bd23b722e84243fda7eda0e4a0a23459 Mon Sep 17 00:00:00 2001 From: Azamat Hackimov Date: Sun, 19 Sep 2010 12:00:23 +0000 Subject: [PATCH 66/72] Translations update * ru * zh-TW (#6365) git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4102 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- config/locales/ru.yml | 2 +- config/locales/zh-TW.yml | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 68a8fa6ef..827ed52ee 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -230,6 +230,7 @@ ru: button_delete: Удалить button_download: Загрузить button_edit: Редактировать + button_edit_associated_wikipage: "Редактировать связанную wiki-страницу: {{page_title}}" button_list: Список button_lock: Заблокировать button_login: Вход @@ -1032,4 +1033,3 @@ ru: notice_unable_delete_time_entry: Невозможно удалить запись журнала. label_overall_spent_time: Всего затрачено времени - button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 26f23b7fa..3cfa4248a 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -366,6 +366,7 @@ field_redirect_existing_links: 重新導向現有連結 field_estimated_hours: 預估工時 field_column_names: 欄位 + field_time_entries: 耗用工時 field_time_zone: 時區 field_searchable: 可用做搜尋條件 field_default_value: 預設值 @@ -378,8 +379,9 @@ field_group_by: 結果分組方式 field_sharing: 共用 field_parent_issue: 父工作項目 - field_time_entries: 耗用工時 - + field_member_of_group: 所屬群組 + field_assigned_to_role: 所屬角色 + setting_app_title: 標題 setting_app_subtitle: 副標題 setting_welcome_text: 歡迎詞 @@ -870,6 +872,7 @@ button_create_and_continue: 繼續建立 button_test: 測試 button_edit: 編輯 + button_edit_associated_wikipage: "編輯相關 Wiki 頁面: {{page_title}}" button_add: 新增 button_change: 修改 button_apply: 套用 @@ -998,6 +1001,4 @@ enumeration_doc_categories: 文件分類 enumeration_activities: 活動 (時間追蹤) enumeration_system_activity: 系統活動 - field_member_of_group: Member of Group - field_assigned_to_role: Member of Role - button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + From 09331ec432bc0b3b308ecd4f44e39dcccf670eb7 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Mon, 20 Sep 2010 02:55:14 +0000 Subject: [PATCH 67/72] Turn on ssl Gravatars for all SSL requests. #5929 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contributed by Felix Schäfer git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4103 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/helpers/application_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index eecbc5b91..6ba40eb45 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -829,7 +829,7 @@ module ApplicationHelper # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe ') def avatar(user, options = { }) if Setting.gravatar_enabled? - options.merge!({:ssl => Setting.protocol == 'https', :default => Setting.gravatar_default}) + options.merge!({:ssl => (defined?(request) && request.ssl?), :default => Setting.gravatar_default}) email = nil if user.respond_to?(:mail) email = user.mail From 1809eefe4cd10b47c72bac3bed0c336ac0db206b Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Mon, 20 Sep 2010 02:55:20 +0000 Subject: [PATCH 68/72] Use the correct local variables in the PDF export. #6382 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contributed by Felix Schäfer git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4104 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- lib/redmine/export/pdf.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/redmine/export/pdf.rb b/lib/redmine/export/pdf.rb index 27905b2bb..c4a2c3d90 100644 --- a/lib/redmine/export/pdf.rb +++ b/lib/redmine/export/pdf.rb @@ -154,7 +154,7 @@ module Redmine if query.grouped? && (group = query.group_by_column.value(issue)) != previous_group pdf.SetFontStyle('B',9) pdf.Cell(277, row_height, - (group.blank? ? 'None' : group.to_s) + " (#{@issue_count_by_group[group]})", + (group.blank? ? 'None' : group.to_s) + " (#{query.issue_count_by_group[group]})", 1, 1, 'L') pdf.SetFontStyle('',8) previous_group = group @@ -255,7 +255,7 @@ module Redmine pdf.SetFontStyle('B',9) pdf.Cell(35,5, l(:field_description) + ":") pdf.SetFontStyle('',9) - pdf.MultiCell(155,5, @issue.description,"BR") + pdf.MultiCell(155,5, issue.description,"BR") pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY) pdf.Line(pdf.GetX, pdf.GetY, 170, pdf.GetY) From f62a1827f33f9d3d2fa5e990a9f6c9475d4bdc71 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Mon, 20 Sep 2010 02:55:26 +0000 Subject: [PATCH 69/72] Updated issue delete confirmation when it has child issues. #6191 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4105 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/models/issue.rb | 5 +++++ app/views/issues/_action_menu.rhtml | 2 +- config/locales/bg.yml | 1 + config/locales/bs.yml | 1 + config/locales/ca.yml | 1 + config/locales/cs.yml | 1 + config/locales/da.yml | 1 + config/locales/de.yml | 1 + config/locales/el.yml | 1 + config/locales/en-GB.yml | 1 + config/locales/en.yml | 1 + config/locales/es.yml | 1 + config/locales/eu.yml | 1 + config/locales/fi.yml | 1 + config/locales/fr.yml | 1 + config/locales/gl.yml | 1 + config/locales/he.yml | 1 + config/locales/hr.yml | 1 + config/locales/hu.yml | 1 + config/locales/id.yml | 1 + config/locales/it.yml | 1 + config/locales/ja.yml | 1 + config/locales/ko.yml | 1 + config/locales/lt.yml | 1 + config/locales/lv.yml | 1 + config/locales/mk.yml | 1 + config/locales/mn.yml | 1 + config/locales/nl.yml | 1 + config/locales/no.yml | 1 + config/locales/pl.yml | 1 + config/locales/pt-BR.yml | 1 + config/locales/pt.yml | 1 + config/locales/ro.yml | 1 + config/locales/ru.yml | 1 + config/locales/sk.yml | 1 + config/locales/sl.yml | 1 + config/locales/sr-YU.yml | 1 + config/locales/sr.yml | 1 + config/locales/sv.yml | 1 + config/locales/th.yml | 1 + config/locales/tr.yml | 1 + config/locales/uk.yml | 1 + config/locales/vi.yml | 1 + config/locales/zh-TW.yml | 1 + config/locales/zh.yml | 1 + 45 files changed, 49 insertions(+), 1 deletion(-) diff --git a/app/models/issue.rb b/app/models/issue.rb index 2b7362589..c49952306 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -382,6 +382,11 @@ class Issue < ActiveRecord::Base done_date = start_date + ((due_date - start_date+1)* done_ratio/100).floor return done_date <= Date.today end + + # Does this issue have children? + def children? + !leaf? + end # Users the issue can be assigned to def assignable_users diff --git a/app/views/issues/_action_menu.rhtml b/app/views/issues/_action_menu.rhtml index c5d17511a..30c63ec66 100644 --- a/app/views/issues/_action_menu.rhtml +++ b/app/views/issues/_action_menu.rhtml @@ -6,5 +6,5 @@ <%= link_to_if_authorized l(:button_duplicate), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue }, :class => 'icon icon-duplicate' %> <%= link_to_if_authorized l(:button_copy), new_issue_move_path(:id => @issue, :copy_options => {:copy => 't'}), :class => 'icon icon-copy' %> <%= link_to_if_authorized l(:button_move), new_issue_move_path(:id => @issue), :class => 'icon icon-move' %> -<%= link_to_if_authorized l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %> +<%= link_to_if_authorized l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue}, :confirm => (@issue.leaf? ? l(:text_are_you_sure) : l(:text_are_you_sure_with_children)), :method => :post, :class => 'icon icon-del' %> diff --git a/config/locales/bg.yml b/config/locales/bg.yml index ceee5c3cf..e99da7062 100644 --- a/config/locales/bg.yml +++ b/config/locales/bg.yml @@ -910,3 +910,4 @@ bg: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/bs.yml b/config/locales/bs.yml index b855be9e9..bef17900e 100644 --- a/config/locales/bs.yml +++ b/config/locales/bs.yml @@ -930,3 +930,4 @@ bs: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 6ba0a8bea..38092a2a7 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -919,3 +919,4 @@ ca: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/cs.yml b/config/locales/cs.yml index 71591fa46..d3cdae5ef 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -916,3 +916,4 @@ cs: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/da.yml b/config/locales/da.yml index b6270f0d1..25d288336 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -932,3 +932,4 @@ da: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/de.yml b/config/locales/de.yml index 3a5ee094e..f9847c3f5 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -935,3 +935,4 @@ de: field_member_of_group: Member of Group field_assigned_to_role: Member of Role + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/el.yml b/config/locales/el.yml index d0608e851..2b6e3493d 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -916,3 +916,4 @@ el: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index c0b520dfd..4f5aec111 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -920,3 +920,4 @@ en-GB: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/en.yml b/config/locales/en.yml index 81a26c82c..2930d8079 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -838,6 +838,7 @@ en: text_subprojects_destroy_warning: "Its subproject(s): {{value}} will be also deleted." text_workflow_edit: Select a role and a tracker to edit the workflow text_are_you_sure: Are you sure ? + text_are_you_sure_with_children: "Delete issue and all child issues?" text_journal_changed: "{{label}} changed from {{old}} to {{new}}" text_journal_set_to: "{{label}} set to {{value}}" text_journal_deleted: "{{label}} deleted ({{old}})" diff --git a/config/locales/es.yml b/config/locales/es.yml index 1828558bf..18d56ae3e 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -956,3 +956,4 @@ es: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/eu.yml b/config/locales/eu.yml index 64881fe6f..983fe1482 100644 --- a/config/locales/eu.yml +++ b/config/locales/eu.yml @@ -920,3 +920,4 @@ eu: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/fi.yml b/config/locales/fi.yml index 8b56c94da..ea14a5c0d 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -941,3 +941,4 @@ fi: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 0a1ee46ea..0654ee736 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -934,3 +934,4 @@ fr: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Modifier la page de Wiki associée: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/gl.yml b/config/locales/gl.yml index b5345de69..5360095f6 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -932,3 +932,4 @@ gl: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/he.yml b/config/locales/he.yml index 8c55b2351..0129eff88 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -921,3 +921,4 @@ he: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/hr.yml b/config/locales/hr.yml index 77cda672a..9219ac660 100644 --- a/config/locales/hr.yml +++ b/config/locales/hr.yml @@ -923,3 +923,4 @@ hr: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/hu.yml b/config/locales/hu.yml index 84e714a6b..e099cb9bc 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -939,3 +939,4 @@ field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/id.yml b/config/locales/id.yml index ca6444b0c..185d4f21d 100644 --- a/config/locales/id.yml +++ b/config/locales/id.yml @@ -924,3 +924,4 @@ id: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/it.yml b/config/locales/it.yml index 6bd4096a9..be0da6a48 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -920,3 +920,4 @@ it: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/ja.yml b/config/locales/ja.yml index fa3346f17..2ea6b009f 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -941,3 +941,4 @@ ja: enumeration_activities: 作業分類 (時間トラッキング) enumeration_system_activity: システム作業分類 button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 771f91cef..e4dc36fb6 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -972,3 +972,4 @@ ko: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/lt.yml b/config/locales/lt.yml index 27c5466fd..40e58b980 100644 --- a/config/locales/lt.yml +++ b/config/locales/lt.yml @@ -980,3 +980,4 @@ lt: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/lv.yml b/config/locales/lv.yml index 5a628e5c5..6162f60ef 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -911,3 +911,4 @@ lv: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/mk.yml b/config/locales/mk.yml index ac2a043d4..fb2f78d29 100644 --- a/config/locales/mk.yml +++ b/config/locales/mk.yml @@ -916,3 +916,4 @@ mk: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/mn.yml b/config/locales/mn.yml index 908e8a844..5358657eb 100644 --- a/config/locales/mn.yml +++ b/config/locales/mn.yml @@ -917,3 +917,4 @@ mn: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/nl.yml b/config/locales/nl.yml index b5933d222..071b3919e 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -898,3 +898,4 @@ nl: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/no.yml b/config/locales/no.yml index 24be1a06e..4b76683fc 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -907,3 +907,4 @@ field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/pl.yml b/config/locales/pl.yml index aade4af0e..b56fd71eb 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -937,3 +937,4 @@ pl: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index 7fa9ae2f5..ee1aefac5 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -940,3 +940,4 @@ pt-BR: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/pt.yml b/config/locales/pt.yml index 9a7547ab4..cb336e25a 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -924,3 +924,4 @@ pt: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/ro.yml b/config/locales/ro.yml index aac64cc2b..2e8f7a1ca 100644 --- a/config/locales/ro.yml +++ b/config/locales/ro.yml @@ -909,3 +909,4 @@ ro: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 827ed52ee..08e1acb95 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -1033,3 +1033,4 @@ ru: notice_unable_delete_time_entry: Невозможно удалить запись журнала. label_overall_spent_time: Всего затрачено времени + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/sk.yml b/config/locales/sk.yml index 87c50e27a..37c5ea6b1 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -911,3 +911,4 @@ sk: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/sl.yml b/config/locales/sl.yml index 3b579a824..6732ef98d 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -912,3 +912,4 @@ sl: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/sr-YU.yml b/config/locales/sr-YU.yml index b48e90223..d73fd0340 100644 --- a/config/locales/sr-YU.yml +++ b/config/locales/sr-YU.yml @@ -916,3 +916,4 @@ sr-YU: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/sr.yml b/config/locales/sr.yml index 8282a37bf..1b6a8d334 100644 --- a/config/locales/sr.yml +++ b/config/locales/sr.yml @@ -917,3 +917,4 @@ sr: field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 28ce75e21..439f9c7cd 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -961,3 +961,4 @@ sv: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/th.yml b/config/locales/th.yml index 859cf0f08..bc936f709 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -913,3 +913,4 @@ th: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/tr.yml b/config/locales/tr.yml index 9244d983d..4b0e6022c 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -939,3 +939,4 @@ tr: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/uk.yml b/config/locales/uk.yml index eade7c4a2..146454da4 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -912,3 +912,4 @@ uk: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/vi.yml b/config/locales/vi.yml index 94fbad869..8cf978d09 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -971,3 +971,4 @@ vi: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 3cfa4248a..38efcfa9d 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -1002,3 +1002,4 @@ enumeration_activities: 活動 (時間追蹤) enumeration_system_activity: 系統活動 + text_are_you_sure_with_children: Delete issue and all child issues? diff --git a/config/locales/zh.yml b/config/locales/zh.yml index 3b788860d..a7611bf1d 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -934,3 +934,4 @@ zh: field_member_of_group: Member of Group field_assigned_to_role: Member of Role button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}" + text_are_you_sure_with_children: Delete issue and all child issues? From e58513c9767a2eafb3a101b3e466e41a85918edd Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Mon, 20 Sep 2010 02:55:32 +0000 Subject: [PATCH 70/72] Fix failing test. See http://ci.finn.de/builds/1-8-7_redmine-trunk_postgres/4100 Contributed by Holger Just git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4106 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- test/unit/query_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/query_test.rb b/test/unit/query_test.rb index f737b2f0c..db6173bae 100644 --- a/test/unit/query_test.rb +++ b/test/unit/query_test.rb @@ -400,7 +400,7 @@ class QueryTest < ActiveSupport::TestCase [group1.name, group1.id], [group2.name, group2.id] ] - assert_equal expected_group_list, @query.available_filters["member_of_group"][:values] + assert_equal expected_group_list.sort, @query.available_filters["member_of_group"][:values].sort end end From 1a4f5e87f2839fa2260deefd4d9a5c992ef860fe Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Mon, 20 Sep 2010 02:55:37 +0000 Subject: [PATCH 71/72] Add scm.css to news pages to allow proper syntax highlighting. #6270 Contributed by Holger Just. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4107 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/views/news/edit.rhtml | 4 ++++ app/views/news/index.rhtml | 1 + 2 files changed, 5 insertions(+) diff --git a/app/views/news/edit.rhtml b/app/views/news/edit.rhtml index 4be566e0b..04d64df1f 100644 --- a/app/views/news/edit.rhtml +++ b/app/views/news/edit.rhtml @@ -12,3 +12,7 @@ }, :accesskey => accesskey(:preview) %> <% end %>
    + +<% content_for :header_tags do %> + <%= stylesheet_link_tag 'scm' %> +<% end %> diff --git a/app/views/news/index.rhtml b/app/views/news/index.rhtml index 8b7cc66e1..41e8bb355 100644 --- a/app/views/news/index.rhtml +++ b/app/views/news/index.rhtml @@ -45,6 +45,7 @@ <% content_for :header_tags do %> <%= auto_discovery_link_tag(:atom, params.merge({:format => 'atom', :page => nil, :key => User.current.rss_key})) %> + <%= stylesheet_link_tag 'scm' %> <% end %> <% html_title(l(:label_news_plural)) -%> From 3bc29e29e091b56bcffeb07ffb9c37c328525f99 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Mon, 20 Sep 2010 15:13:48 +0000 Subject: [PATCH 72/72] Refactor: split NewsController#new into #new and #create methods. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4163 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/news_controller.rb | 12 +++++++++--- app/views/news/index.rhtml | 2 +- app/views/news/new.rhtml | 2 +- config/routes.rb | 2 +- lib/redmine.rb | 2 +- test/functional/news_controller_test.rb | 8 ++++---- test/integration/routing_test.rb | 2 +- 7 files changed, 18 insertions(+), 12 deletions(-) diff --git a/app/controllers/news_controller.rb b/app/controllers/news_controller.rb index 4d44158a4..f4e4b6559 100644 --- a/app/controllers/news_controller.rb +++ b/app/controllers/news_controller.rb @@ -18,9 +18,9 @@ class NewsController < ApplicationController default_search_scope :news model_object News - before_filter :find_model_object, :except => [:new, :index, :preview] - before_filter :find_project_from_association, :except => [:new, :index, :preview] - before_filter :find_project, :only => [:new, :preview] + before_filter :find_model_object, :except => [:new, :create, :index, :preview] + before_filter :find_project_from_association, :except => [:new, :create, :index, :preview] + before_filter :find_project, :only => [:new, :create, :preview] before_filter :authorize, :except => [:index, :preview] before_filter :find_optional_project, :only => :index accept_key_auth :index @@ -46,11 +46,17 @@ class NewsController < ApplicationController def new @news = News.new(:project => @project, :author => User.current) + end + + def create + @news = News.new(:project => @project, :author => User.current) if request.post? @news.attributes = params[:news] if @news.save flash[:notice] = l(:notice_successful_create) redirect_to :controller => 'news', :action => 'index', :project_id => @project + else + render :action => 'new' end end end diff --git a/app/views/news/index.rhtml b/app/views/news/index.rhtml index 41e8bb355..2b53b7f72 100644 --- a/app/views/news/index.rhtml +++ b/app/views/news/index.rhtml @@ -7,7 +7,7 @@
    @@ -67,26 +69,10 @@ t_height = g_height + headers_height
    -<% -# -# Tasks subjects -# -top = headers_height + 8 -@gantt.events.each do |i| -left = 4 + (i.is_a?(Issue) ? i.level * 16 : 0) - %> -
    - <% if i.is_a? Issue %> - <%= h("#{i.project} -") unless @project && @project == i.project %> - <%= link_to_issue i %> - <% else %> - - <%= link_to_version i %> - - <% end %> -
    - <% top = top + 20 -end %> +<% top = headers_height + 8 %> + +<%= @gantt.subjects(:headers_height => headers_height, :top => top, :g_width => g_width) %> +
    @@ -164,53 +150,9 @@ if show_days end end %> -<% -# -# Tasks -# -top = headers_height + 10 -@gantt.events.each do |i| - if i.is_a? Issue - i_start_date = (i.start_date >= @gantt.date_from ? i.start_date : @gantt.date_from ) - i_end_date = (i.due_before <= @gantt.date_to ? i.due_before : @gantt.date_to ) - - i_done_date = i.start_date + ((i.due_before - i.start_date+1)*i.done_ratio/100).floor - i_done_date = (i_done_date <= @gantt.date_from ? @gantt.date_from : i_done_date ) - i_done_date = (i_done_date >= @gantt.date_to ? @gantt.date_to : i_done_date ) - - i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today - - i_left = ((i_start_date - @gantt.date_from)*zoom).floor - i_width = ((i_end_date - i_start_date + 1)*zoom).floor - 2 # total width of the issue (- 2 for left and right borders) - d_width = ((i_done_date - i_start_date)*zoom).floor - 2 # done width - l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor - 2 : 0 # delay width - css = "task " + (i.leaf? ? 'leaf' : 'parent') - %> -
     
    - <% if l_width > 0 %> -
     
    - <% end %> - <% if d_width > 0 %> -
     
    - <% end %> -
    - <%= i.status.name %> - <%= (i.done_ratio).to_i %>% -
    -
    - - <%= render_issue_tooltip i %> -
    -<% else - i_left = ((i.start_date - @gantt.date_from)*zoom).floor - %> -
     
    -
    - <%= format_version_name i %> -
    -<% end %> - <% top = top + 20 -end %> +<% top = headers_height + 10 %> + +<%= @gantt.lines(:top => top, :zoom => zoom, :g_width => g_width ) %> <% # diff --git a/lib/redmine/export/pdf.rb b/lib/redmine/export/pdf.rb index 9b1f2b8a2..27905b2bb 100644 --- a/lib/redmine/export/pdf.rb +++ b/lib/redmine/export/pdf.rb @@ -184,7 +184,7 @@ module Redmine end pdf.Output end - + # Returns a PDF string of a single issue def issue_to_pdf(issue) pdf = IFPDF.new(current_language) @@ -208,7 +208,7 @@ module Redmine pdf.SetFontStyle('',9) pdf.Cell(60,5, issue.priority.to_s,"RT") pdf.Ln - + pdf.SetFontStyle('B',9) pdf.Cell(35,5, l(:field_author) + ":","L") pdf.SetFontStyle('',9) @@ -238,14 +238,14 @@ module Redmine pdf.SetFontStyle('',9) pdf.Cell(60,5, format_date(issue.due_date),"RB") pdf.Ln - + for custom_value in issue.custom_field_values pdf.SetFontStyle('B',9) pdf.Cell(35,5, custom_value.custom_field.name + ":","L") pdf.SetFontStyle('',9) pdf.MultiCell(155,5, (show_value custom_value),"R") end - + pdf.SetFontStyle('B',9) pdf.Cell(35,5, l(:field_subject) + ":","LTB") pdf.SetFontStyle('',9) @@ -311,187 +311,7 @@ module Redmine end pdf.Output end - - # Returns a PDF string of a gantt chart - def gantt_to_pdf(gantt, project) - pdf = IFPDF.new(current_language) - pdf.SetTitle("#{l(:label_gantt)} #{project}") - pdf.AliasNbPages - pdf.footer_date = format_date(Date.today) - pdf.AddPage("L") - pdf.SetFontStyle('B',12) - pdf.SetX(15) - pdf.Cell(70, 20, project.to_s) - pdf.Ln - pdf.SetFontStyle('B',9) - - subject_width = 100 - header_heigth = 5 - - headers_heigth = header_heigth - show_weeks = false - show_days = false - - if gantt.months < 7 - show_weeks = true - headers_heigth = 2*header_heigth - if gantt.months < 3 - show_days = true - headers_heigth = 3*header_heigth - end - end - - g_width = 280 - subject_width - zoom = (g_width) / (gantt.date_to - gantt.date_from + 1) - g_height = 120 - t_height = g_height + headers_heigth - - y_start = pdf.GetY - - # Months headers - month_f = gantt.date_from - left = subject_width - height = header_heigth - gantt.months.times do - width = ((month_f >> 1) - month_f) * zoom - pdf.SetY(y_start) - pdf.SetX(left) - pdf.Cell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C") - left = left + width - month_f = month_f >> 1 - end - - # Weeks headers - if show_weeks - left = subject_width - height = header_heigth - if gantt.date_from.cwday == 1 - # gantt.date_from is monday - week_f = gantt.date_from - else - # find next monday after gantt.date_from - week_f = gantt.date_from + (7 - gantt.date_from.cwday + 1) - width = (7 - gantt.date_from.cwday + 1) * zoom-1 - pdf.SetY(y_start + header_heigth) - pdf.SetX(left) - pdf.Cell(width + 1, height, "", "LTR") - left = left + width+1 - end - while week_f <= gantt.date_to - width = (week_f + 6 <= gantt.date_to) ? 7 * zoom : (gantt.date_to - week_f + 1) * zoom - pdf.SetY(y_start + header_heigth) - pdf.SetX(left) - pdf.Cell(width, height, (width >= 5 ? week_f.cweek.to_s : ""), "LTR", 0, "C") - left = left + width - week_f = week_f+7 - end - end - - # Days headers - if show_days - left = subject_width - height = header_heigth - wday = gantt.date_from.cwday - pdf.SetFontStyle('B',7) - (gantt.date_to - gantt.date_from + 1).to_i.times do - width = zoom - pdf.SetY(y_start + 2 * header_heigth) - pdf.SetX(left) - pdf.Cell(width, height, day_name(wday).first, "LTR", 0, "C") - left = left + width - wday = wday + 1 - wday = 1 if wday > 7 - end - end - - pdf.SetY(y_start) - pdf.SetX(15) - pdf.Cell(subject_width+g_width-15, headers_heigth, "", 1) - - # Tasks - top = headers_heigth + y_start - pdf.SetFontStyle('B',7) - gantt.events.each do |i| - pdf.SetY(top) - pdf.SetX(15) - - text = "" - if i.is_a? Issue - text = "#{i.tracker} #{i.id}: #{i.subject}" - else - text = i.name - end - text = "#{i.project} - #{text}" unless project && project == i.project - pdf.Cell(subject_width-15, 5, text, "LR") - - pdf.SetY(top + 0.2) - pdf.SetX(subject_width) - pdf.SetFillColor(255, 255, 255) - pdf.Cell(g_width, 4.6, "", "LR", 0, "", 1) - pdf.SetY(top+1.5) - - if i.is_a? Issue - i_start_date = (i.start_date >= gantt.date_from ? i.start_date : gantt.date_from ) - i_end_date = (i.due_before <= gantt.date_to ? i.due_before : gantt.date_to ) - - i_done_date = i.start_date + ((i.due_before - i.start_date+1)*i.done_ratio/100).floor - i_done_date = (i_done_date <= gantt.date_from ? gantt.date_from : i_done_date ) - i_done_date = (i_done_date >= gantt.date_to ? gantt.date_to : i_done_date ) - - i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today - - i_left = ((i_start_date - gantt.date_from)*zoom) - i_width = ((i_end_date - i_start_date + 1)*zoom) - d_width = ((i_done_date - i_start_date)*zoom) - l_width = ((i_late_date - i_start_date+1)*zoom) if i_late_date - l_width ||= 0 - - pdf.SetX(subject_width + i_left) - pdf.SetFillColor(200,200,200) - pdf.Cell(i_width, 2, "", 0, 0, "", 1) - - if l_width > 0 - pdf.SetY(top+1.5) - pdf.SetX(subject_width + i_left) - pdf.SetFillColor(255,100,100) - pdf.Cell(l_width, 2, "", 0, 0, "", 1) - end - if d_width > 0 - pdf.SetY(top+1.5) - pdf.SetX(subject_width + i_left) - pdf.SetFillColor(100,100,255) - pdf.Cell(d_width, 2, "", 0, 0, "", 1) - end - - pdf.SetY(top+1.5) - pdf.SetX(subject_width + i_left + i_width) - pdf.Cell(30, 2, "#{i.status} #{i.done_ratio}%") - else - i_left = ((i.start_date - gantt.date_from)*zoom) - - pdf.SetX(subject_width + i_left) - pdf.SetFillColor(50,200,50) - pdf.Cell(2, 2, "", 0, 0, "", 1) - - pdf.SetY(top+1.5) - pdf.SetX(subject_width + i_left + 3) - pdf.Cell(30, 2, "#{i.name}") - end - - top = top + 5 - pdf.SetDrawColor(200, 200, 200) - pdf.Line(15, top, subject_width+g_width, top) - if pdf.GetY() > 180 - pdf.AddPage("L") - top = 20 - pdf.Line(15, top, subject_width+g_width, top) - end - pdf.SetDrawColor(0, 0, 0) - end - - pdf.Line(15, top, subject_width+g_width, top) - pdf.Output - end + end end end diff --git a/lib/redmine/helpers/gantt.rb b/lib/redmine/helpers/gantt.rb index 96ba4db7c..33a4e1c2c 100644 --- a/lib/redmine/helpers/gantt.rb +++ b/lib/redmine/helpers/gantt.rb @@ -19,11 +19,28 @@ module Redmine module Helpers # Simple class to handle gantt chart data class Gantt - attr_reader :year_from, :month_from, :date_from, :date_to, :zoom, :months, :events - + include ERB::Util + include Redmine::I18n + + # :nodoc: + # Some utility methods for the PDF export + class PDF + MaxCharactorsForSubject = 45 + TotalWidth = 280 + LeftPaneWidth = 100 + + def self.right_pane_width + TotalWidth - LeftPaneWidth + end + end + + attr_reader :year_from, :month_from, :date_from, :date_to, :zoom, :months + attr_accessor :query + attr_accessor :project + attr_accessor :view + def initialize(options={}) options = options.dup - @events = [] if options[:year] && options[:year].to_i >0 @year_from = options[:year].to_i @@ -52,31 +69,6 @@ module Redmine @date_to = (@date_from >> @months) - 1 end - - def events=(e) - @events = e - # Adds all ancestors - root_ids = e.select {|i| i.is_a?(Issue) && i.parent_id? }.collect(&:root_id).uniq - if root_ids.any? - # Retrieves all nodes - parents = Issue.find_all_by_root_id(root_ids, :conditions => ["rgt - lft > 1"]) - # Only add ancestors - @events += parents.select {|p| @events.detect {|i| i.is_a?(Issue) && p.is_ancestor_of?(i)}} - end - @events.uniq! - # Sort issues by hierarchy and start dates - @events.sort! {|x,y| - if x.is_a?(Issue) && y.is_a?(Issue) - gantt_issue_compare(x, y, @events) - else - gantt_start_compare(x, y) - end - } - # Removes issues that have no start or end date - @events.reject! {|i| i.is_a?(Issue) && (i.start_date.nil? || i.due_before.nil?) } - @events - end - def params { :zoom => zoom, :year => year_from, :month => month_from, :months => months } end @@ -88,10 +80,652 @@ module Redmine def params_next { :year => (date_from >> months).year, :month => (date_from >> months).month, :zoom => zoom, :months => months } end - + + ### Extracted from the HTML view/helpers + # Returns the number of rows that will be rendered on the Gantt chart + def number_of_rows + if @project + return number_of_rows_on_project(@project) + else + Project.roots.inject(0) do |total, project| + total += number_of_rows_on_project(project) + end + end + end + + # Returns the number of rows that will be used to list a project on + # the Gantt chart. This will recurse for each subproject. + def number_of_rows_on_project(project) + # Remove the project requirement for Versions because it will + # restrict issues to only be on the current project. This + # ends up missing issues which are assigned to shared versions. + @query.project = nil if @query.project + + # One Root project + count = 1 + # Issues without a Version + count += project.issues.for_gantt.without_version.with_query(@query).count + + # Versions + count += project.versions.count + + # Issues on the Versions + project.versions.each do |version| + count += version.fixed_issues.for_gantt.with_query(@query).count + end + + # Subprojects + project.children.each do |subproject| + count += number_of_rows_on_project(subproject) + end + + count + end + + # Renders the subjects of the Gantt chart, the left side. + def subjects(options={}) + options = {:indent => 4, :render => :subject, :format => :html}.merge(options) + + output = '' + if @project + output << render_project(@project, options) + else + Project.roots.each do |project| + output << render_project(project, options) + end + end + + output + end + + # Renders the lines of the Gantt chart, the right side + def lines(options={}) + options = {:indent => 4, :render => :line, :format => :html}.merge(options) + output = '' + + if @project + output << render_project(@project, options) + else + Project.roots.each do |project| + output << render_project(project, options) + end + end + + output + end + + def render_project(project, options={}) + options[:top] = 0 unless options.key? :top + options[:indent_increment] = 20 unless options.key? :indent_increment + options[:top_increment] = 20 unless options.key? :top_increment + + output = '' + # Project Header + project_header = if options[:render] == :subject + subject_for_project(project, options) + else + # :line + line_for_project(project, options) + end + output << project_header if options[:format] == :html + + options[:top] += options[:top_increment] + options[:indent] += options[:indent_increment] + + # Second, Issues without a version + issues = project.issues.for_gantt.without_version.with_query(@query) + if issues + issue_rendering = render_issues(issues, options) + output << issue_rendering if options[:format] == :html + end + + # Third, Versions + project.versions.sort.each do |version| + version_rendering = render_version(version, options) + output << version_rendering if options[:format] == :html + end + + # Fourth, subprojects + project.children.each do |project| + subproject_rendering = render_project(project, options) + output << subproject_rendering if options[:format] == :html + end + + # Remove indent to hit the next sibling + options[:indent] -= options[:indent_increment] + + output + end + + def render_issues(issues, options={}) + output = '' + issues.each do |i| + issue_rendering = if options[:render] == :subject + subject_for_issue(i, options) + else + # :line + line_for_issue(i, options) + end + output << issue_rendering if options[:format] == :html + options[:top] += options[:top_increment] + end + output + end + + def render_version(version, options={}) + output = '' + # Version header + version_rendering = if options[:render] == :subject + subject_for_version(version, options) + else + # :line + line_for_version(version, options) + end + + output << version_rendering if options[:format] == :html + + options[:top] += options[:top_increment] + + # Remove the project requirement for Versions because it will + # restrict issues to only be on the current project. This + # ends up missing issues which are assigned to shared versions. + @query.project = nil if @query.project + + issues = version.fixed_issues.for_gantt.with_query(@query) + if issues + # Indent issues + options[:indent] += options[:indent_increment] + output << render_issues(issues, options) + options[:indent] -= options[:indent_increment] + end + + output + end + + def subject_for_project(project, options) + case options[:format] + when :html + output = '' + + output << "
    " + if project.is_a? Project + output << "" + output << view.link_to_project(project) + output << '' + else + ActiveRecord::Base.logger.debug "Gantt#subject_for_project was not given a project" + '' + end + output << "
    " + + output + when :image + + options[:image].fill('black') + options[:image].stroke('transparent') + options[:image].stroke_width(1) + options[:image].text(options[:indent], options[:top] + 2, project.name) + when :pdf + options[:pdf].SetY(options[:top]) + options[:pdf].SetX(15) + + char_limit = PDF::MaxCharactorsForSubject - options[:indent] + options[:pdf].Cell(options[:subject_width]-15, 5, (" " * options[:indent]) +"#{project.name}".sub(/^(.{#{char_limit}}[^\s]*\s).*$/, '\1 (...)'), "LR") + + options[:pdf].SetY(options[:top]) + options[:pdf].SetX(options[:subject_width]) + options[:pdf].Cell(options[:g_width], 5, "", "LR") + end + end + + def line_for_project(project, options) + # Skip versions that don't have a start_date + if project.is_a?(Project) && project.start_date + options[:zoom] ||= 1 + options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom] + + + case options[:format] + when :html + output = '' + i_left = ((project.start_date - self.date_from)*options[:zoom]).floor + + start_date = project.start_date + start_date ||= self.date_from + start_left = ((start_date - self.date_from)*options[:zoom]).floor + + i_end_date = ((project.due_date <= self.date_to) ? project.due_date : self.date_to ) + i_done_date = start_date + ((project.due_date - start_date+1)* project.completed_percent(:include_subprojects => true)/100).floor + i_done_date = (i_done_date <= self.date_from ? self.date_from : i_done_date ) + i_done_date = (i_done_date >= self.date_to ? self.date_to : i_done_date ) + + i_late_date = [i_end_date, Date.today].min if start_date < Date.today + i_end = ((i_end_date - self.date_from) * options[:zoom]).floor + + i_width = (i_end - i_left + 1).floor - 2 # total width of the issue (- 2 for left and right borders) + d_width = ((i_done_date - start_date)*options[:zoom]).floor - 2 # done width + l_width = i_late_date ? ((i_late_date - start_date+1)*options[:zoom]).floor - 2 : 0 # delay width + + # Bar graphic + + # Make sure that negative i_left and i_width don't + # overflow the subject + if i_end > 0 && i_left <= options[:g_width] + output << "
     
    " + end + + if l_width > 0 && i_left <= options[:g_width] + output << "
     
    " + end + if d_width > 0 && i_left <= options[:g_width] + output<< "
     
    " + end + + + # Starting diamond + if start_left <= options[:g_width] && start_left > 0 + output << "
     
    " + output << "
    " + output << "
    " + end + + # Ending diamond + # Don't show items too far ahead + if i_end <= options[:g_width] && i_end > 0 + output << "
     
    " + end + + # DIsplay the Project name and % + if i_end <= options[:g_width] + # Display the status even if it's floated off to the left + status_px = i_end + 12 # 12px for the diamond + status_px = 0 if status_px <= 0 + + output << "
    " + output << "#{h project } #{h project.completed_percent(:include_subprojects => true).to_i.to_s}%" + output << "
    " + end + + output + when :image + options[:image].stroke('transparent') + i_left = options[:subject_width] + ((project.due_date - self.date_from)*options[:zoom]).floor + + # Make sure negative i_left doesn't overflow the subject + if i_left > options[:subject_width] + options[:image].fill('blue') + options[:image].rectangle(i_left, options[:top], i_left + 6, options[:top] - 6) + options[:image].fill('black') + options[:image].text(i_left + 11, options[:top] + 1, project.name) + end + when :pdf + options[:pdf].SetY(options[:top]+1.5) + i_left = ((project.due_date - @date_from)*options[:zoom]) + + # Make sure negative i_left doesn't overflow the subject + if i_left > 0 + options[:pdf].SetX(options[:subject_width] + i_left) + options[:pdf].SetFillColor(50,50,200) + options[:pdf].Cell(2, 2, "", 0, 0, "", 1) + + options[:pdf].SetY(options[:top]+1.5) + options[:pdf].SetX(options[:subject_width] + i_left + 3) + options[:pdf].Cell(30, 2, "#{project.name}") + end + end + else + ActiveRecord::Base.logger.debug "Gantt#line_for_project was not given a project with a start_date" + '' + end + end + + def subject_for_version(version, options) + case options[:format] + when :html + output = '' + output << "
    " + if version.is_a? Version + output << "" + output << view.link_to_version(version) + output << '' + else + ActiveRecord::Base.logger.debug "Gantt#subject_for_version was not given a version" + '' + end + output << "
    " + + output + when :image + options[:image].fill('black') + options[:image].stroke('transparent') + options[:image].stroke_width(1) + options[:image].text(options[:indent], options[:top] + 2, version.name) + when :pdf + options[:pdf].SetY(options[:top]) + options[:pdf].SetX(15) + + char_limit = PDF::MaxCharactorsForSubject - options[:indent] + options[:pdf].Cell(options[:subject_width]-15, 5, (" " * options[:indent]) +"#{version.name}".sub(/^(.{#{char_limit}}[^\s]*\s).*$/, '\1 (...)'), "LR") + + options[:pdf].SetY(options[:top]) + options[:pdf].SetX(options[:subject_width]) + options[:pdf].Cell(options[:g_width], 5, "", "LR") + end + end + + def line_for_version(version, options) + # Skip versions that don't have a start_date + if version.is_a?(Version) && version.start_date + options[:zoom] ||= 1 + options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom] + + case options[:format] + when :html + output = '' + i_left = ((version.start_date - self.date_from)*options[:zoom]).floor + # TODO: or version.fixed_issues.collect(&:start_date).min + start_date = version.fixed_issues.minimum('start_date') if version.fixed_issues.present? + start_date ||= self.date_from + start_left = ((start_date - self.date_from)*options[:zoom]).floor + + i_end_date = ((version.due_date <= self.date_to) ? version.due_date : self.date_to ) + i_done_date = start_date + ((version.due_date - start_date+1)* version.completed_pourcent/100).floor + i_done_date = (i_done_date <= self.date_from ? self.date_from : i_done_date ) + i_done_date = (i_done_date >= self.date_to ? self.date_to : i_done_date ) + + i_late_date = [i_end_date, Date.today].min if start_date < Date.today + + i_width = (i_left - start_left + 1).floor - 2 # total width of the issue (- 2 for left and right borders) + d_width = ((i_done_date - start_date)*options[:zoom]).floor - 2 # done width + l_width = i_late_date ? ((i_late_date - start_date+1)*options[:zoom]).floor - 2 : 0 # delay width + + i_end = ((i_end_date - self.date_from) * options[:zoom]).floor # Ending pixel + + # Bar graphic + + # Make sure that negative i_left and i_width don't + # overflow the subject + if i_width > 0 && i_left <= options[:g_width] + output << "
     
    " + end + if l_width > 0 && i_left <= options[:g_width] + output << "
     
    " + end + if d_width > 0 && i_left <= options[:g_width] + output<< "
     
    " + end + + + # Starting diamond + if start_left <= options[:g_width] && start_left > 0 + output << "
     
    " + output << "
    " + output << "
    " + end + + # Ending diamond + # Don't show items too far ahead + if i_left <= options[:g_width] && i_end > 0 + output << "
     
    " + end + + # Display the Version name and % + if i_end <= options[:g_width] + # Display the status even if it's floated off to the left + status_px = i_end + 12 # 12px for the diamond + status_px = 0 if status_px <= 0 + + output << "
    " + output << h("#{version.project} -") unless @project && @project == version.project + output << "#{h version } #{h version.completed_pourcent.to_i.to_s}%" + output << "
    " + end + + output + when :image + options[:image].stroke('transparent') + i_left = options[:subject_width] + ((version.start_date - @date_from)*options[:zoom]).floor + + # Make sure negative i_left doesn't overflow the subject + if i_left > options[:subject_width] + options[:image].fill('green') + options[:image].rectangle(i_left, options[:top], i_left + 6, options[:top] - 6) + options[:image].fill('black') + options[:image].text(i_left + 11, options[:top] + 1, version.name) + end + when :pdf + options[:pdf].SetY(options[:top]+1.5) + i_left = ((version.start_date - @date_from)*options[:zoom]) + + # Make sure negative i_left doesn't overflow the subject + if i_left > 0 + options[:pdf].SetX(options[:subject_width] + i_left) + options[:pdf].SetFillColor(50,200,50) + options[:pdf].Cell(2, 2, "", 0, 0, "", 1) + + options[:pdf].SetY(options[:top]+1.5) + options[:pdf].SetX(options[:subject_width] + i_left + 3) + options[:pdf].Cell(30, 2, "#{version.name}") + end + end + else + ActiveRecord::Base.logger.debug "Gantt#line_for_version was not given a version with a start_date" + '' + end + end + + def subject_for_issue(issue, options) + case options[:format] + when :html + output = '' + output << "
    " + output << "
    " + if issue.is_a? Issue + css_classes = [] + css_classes << 'issue-overdue' if issue.overdue? + css_classes << 'issue-behind-schedule' if issue.behind_schedule? + css_classes << 'icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to + + if issue.assigned_to.present? + assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name + output << view.avatar(issue.assigned_to, :class => 'gravatar icon-gravatar', :size => 10, :title => assigned_string) + end + output << "" + output << view.link_to_issue(issue) + output << ":" + output << h(issue.subject) + output << '' + else + ActiveRecord::Base.logger.debug "Gantt#subject_for_issue was not given an issue" + '' + end + output << "
    " + + # Tooltip + if issue.is_a? Issue + output << "" + output << view.render_issue_tooltip(issue) + output << "" + end + + output << "
    " + output + when :image + options[:image].fill('black') + options[:image].stroke('transparent') + options[:image].stroke_width(1) + options[:image].text(options[:indent], options[:top] + 2, issue.subject) + when :pdf + options[:pdf].SetY(options[:top]) + options[:pdf].SetX(15) + + char_limit = PDF::MaxCharactorsForSubject - options[:indent] + options[:pdf].Cell(options[:subject_width]-15, 5, (" " * options[:indent]) +"#{issue.tracker} #{issue.id}: #{issue.subject}".sub(/^(.{#{char_limit}}[^\s]*\s).*$/, '\1 (...)'), "LR") + + options[:pdf].SetY(options[:top]) + options[:pdf].SetX(options[:subject_width]) + options[:pdf].Cell(options[:g_width], 5, "", "LR") + end + end + + def line_for_issue(issue, options) + # Skip issues that don't have a due_before (due_date or version's due_date) + if issue.is_a?(Issue) && issue.due_before + case options[:format] + when :html + output = '' + # Handle nil start_dates, rare but can happen. + i_start_date = if issue.start_date && issue.start_date >= self.date_from + issue.start_date + else + self.date_from + end + + i_end_date = ((issue.due_before && issue.due_before <= self.date_to) ? issue.due_before : self.date_to ) + i_done_date = i_start_date + ((issue.due_before - i_start_date+1)*issue.done_ratio/100).floor + i_done_date = (i_done_date <= self.date_from ? self.date_from : i_done_date ) + i_done_date = (i_done_date >= self.date_to ? self.date_to : i_done_date ) + + i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today + + i_left = ((i_start_date - self.date_from)*options[:zoom]).floor + i_width = ((i_end_date - i_start_date + 1)*options[:zoom]).floor - 2 # total width of the issue (- 2 for left and right borders) + d_width = ((i_done_date - i_start_date)*options[:zoom]).floor - 2 # done width + l_width = i_late_date ? ((i_late_date - i_start_date+1)*options[:zoom]).floor - 2 : 0 # delay width + css = "task " + (issue.leaf? ? 'leaf' : 'parent') + + # Make sure that negative i_left and i_width don't + # overflow the subject + if i_width > 0 + output << "
     
    " + end + if l_width > 0 + output << "
     
    " + end + if d_width > 0 + output<< "
     
    " + end + + # Display the status even if it's floated off to the left + status_px = i_left + i_width + 5 + status_px = 5 if status_px <= 0 + + output << "
    " + output << issue.status.name + output << ' ' + output << (issue.done_ratio).to_i.to_s + output << "%" + output << "
    " + + output << "
    " + output << '' + output << view.render_issue_tooltip(issue) + output << "
    " + output + + when :image + # Handle nil start_dates, rare but can happen. + i_start_date = if issue.start_date && issue.start_date >= @date_from + issue.start_date + else + @date_from + end + + i_end_date = (issue.due_before <= date_to ? issue.due_before : date_to ) + i_done_date = i_start_date + ((issue.due_before - i_start_date+1)*issue.done_ratio/100).floor + i_done_date = (i_done_date <= @date_from ? @date_from : i_done_date ) + i_done_date = (i_done_date >= date_to ? date_to : i_done_date ) + i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today + + i_left = options[:subject_width] + ((i_start_date - @date_from)*options[:zoom]).floor + i_width = ((i_end_date - i_start_date + 1)*options[:zoom]).floor # total width of the issue + d_width = ((i_done_date - i_start_date)*options[:zoom]).floor # done width + l_width = i_late_date ? ((i_late_date - i_start_date+1)*options[:zoom]).floor : 0 # delay width + + + # Make sure that negative i_left and i_width don't + # overflow the subject + if i_width > 0 + options[:image].fill('grey') + options[:image].rectangle(i_left, options[:top], i_left + i_width, options[:top] - 6) + options[:image].fill('red') + options[:image].rectangle(i_left, options[:top], i_left + l_width, options[:top] - 6) if l_width > 0 + options[:image].fill('blue') + options[:image].rectangle(i_left, options[:top], i_left + d_width, options[:top] - 6) if d_width > 0 + end + + # Show the status and % done next to the subject if it overflows + options[:image].fill('black') + if i_width > 0 + options[:image].text(i_left + i_width + 5,options[:top] + 1, "#{issue.status.name} #{issue.done_ratio}%") + else + options[:image].text(options[:subject_width] + 5,options[:top] + 1, "#{issue.status.name} #{issue.done_ratio}%") + end + + when :pdf + options[:pdf].SetY(options[:top]+1.5) + # Handle nil start_dates, rare but can happen. + i_start_date = if issue.start_date && issue.start_date >= @date_from + issue.start_date + else + @date_from + end + + i_end_date = (issue.due_before <= @date_to ? issue.due_before : @date_to ) + + i_done_date = i_start_date + ((issue.due_before - i_start_date+1)*issue.done_ratio/100).floor + i_done_date = (i_done_date <= @date_from ? @date_from : i_done_date ) + i_done_date = (i_done_date >= @date_to ? @date_to : i_done_date ) + + i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today + + i_left = ((i_start_date - @date_from)*options[:zoom]) + i_width = ((i_end_date - i_start_date + 1)*options[:zoom]) + d_width = ((i_done_date - i_start_date)*options[:zoom]) + l_width = ((i_late_date - i_start_date+1)*options[:zoom]) if i_late_date + l_width ||= 0 + + # Make sure that negative i_left and i_width don't + # overflow the subject + if i_width > 0 + options[:pdf].SetX(options[:subject_width] + i_left) + options[:pdf].SetFillColor(200,200,200) + options[:pdf].Cell(i_width, 2, "", 0, 0, "", 1) + end + + if l_width > 0 + options[:pdf].SetY(options[:top]+1.5) + options[:pdf].SetX(options[:subject_width] + i_left) + options[:pdf].SetFillColor(255,100,100) + options[:pdf].Cell(l_width, 2, "", 0, 0, "", 1) + end + if d_width > 0 + options[:pdf].SetY(options[:top]+1.5) + options[:pdf].SetX(options[:subject_width] + i_left) + options[:pdf].SetFillColor(100,100,255) + options[:pdf].Cell(d_width, 2, "", 0, 0, "", 1) + end + + options[:pdf].SetY(options[:top]+1.5) + + # Make sure that negative i_left and i_width don't + # overflow the subject + if (i_left + i_width) >= 0 + options[:pdf].SetX(options[:subject_width] + i_left + i_width) + else + options[:pdf].SetX(options[:subject_width]) + end + options[:pdf].Cell(30, 2, "#{issue.status} #{issue.done_ratio}%") + end + else + ActiveRecord::Base.logger.debug "GanttHelper#line_for_issue was not given an issue with a due_before" + '' + end + end + # Generates a gantt image # Only defined if RMagick is avalaible - def to_image(project, format='PNG') + def to_image(format='PNG') date_to = (@date_from >> @months)-1 show_weeks = @zoom > 1 show_days = @zoom > 2 @@ -101,7 +735,7 @@ module Redmine # width of one day in pixels zoom = @zoom*2 g_width = (@date_to - @date_from + 1)*zoom - g_height = 20 * events.length + 20 + g_height = 20 * number_of_rows + 30 headers_heigth = (show_weeks ? 2*header_heigth : header_heigth) height = g_height + headers_heigth @@ -110,21 +744,7 @@ module Redmine gc = Magick::Draw.new # Subjects - top = headers_heigth + 20 - gc.fill('black') - gc.stroke('transparent') - gc.stroke_width(1) - events.each do |i| - text = "" - if i.is_a? Issue - text = "#{i.tracker} #{i.id}: #{i.subject}" - else - text = i.name - end - text = "#{i.project} - #{text}" unless project && project == i.project - gc.text(4, top + 2, text) - top = top + 20 - end + subjects(:image => gc, :top => (headers_heigth + 20), :indent => 4, :format => :image) # Months headers month_f = @date_from @@ -202,38 +822,8 @@ module Redmine # content top = headers_heigth + 20 - gc.stroke('transparent') - events.each do |i| - if i.is_a?(Issue) - i_start_date = (i.start_date >= @date_from ? i.start_date : @date_from ) - i_end_date = (i.due_before <= date_to ? i.due_before : date_to ) - i_done_date = i.start_date + ((i.due_before - i.start_date+1)*i.done_ratio/100).floor - i_done_date = (i_done_date <= @date_from ? @date_from : i_done_date ) - i_done_date = (i_done_date >= date_to ? date_to : i_done_date ) - i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today - - i_left = subject_width + ((i_start_date - @date_from)*zoom).floor - i_width = ((i_end_date - i_start_date + 1)*zoom).floor # total width of the issue - d_width = ((i_done_date - i_start_date)*zoom).floor # done width - l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor : 0 # delay width - - gc.fill('grey') - gc.rectangle(i_left, top, i_left + i_width, top - 6) - gc.fill('red') - gc.rectangle(i_left, top, i_left + l_width, top - 6) if l_width > 0 - gc.fill('blue') - gc.rectangle(i_left, top, i_left + d_width, top - 6) if d_width > 0 - gc.fill('black') - gc.text(i_left + i_width + 5,top + 1, "#{i.status.name} #{i.done_ratio}%") - else - i_left = subject_width + ((i.start_date - @date_from)*zoom).floor - gc.fill('green') - gc.rectangle(i_left, top, i_left + 6, top - 6) - gc.fill('black') - gc.text(i_left + 11, top + 1, i.name) - end - top = top + 20 - end + + lines(:image => gc, :top => top, :zoom => zoom, :subject_width => subject_width, :format => :image) # today red line if Date.today >= @date_from and Date.today <= date_to @@ -246,36 +836,137 @@ module Redmine imgl.format = format imgl.to_blob end if Object.const_defined?(:Magick) + + def to_pdf + pdf = ::Redmine::Export::PDF::IFPDF.new(current_language) + pdf.SetTitle("#{l(:label_gantt)} #{project}") + pdf.AliasNbPages + pdf.footer_date = format_date(Date.today) + pdf.AddPage("L") + pdf.SetFontStyle('B',12) + pdf.SetX(15) + pdf.Cell(PDF::LeftPaneWidth, 20, project.to_s) + pdf.Ln + pdf.SetFontStyle('B',9) + + subject_width = PDF::LeftPaneWidth + header_heigth = 5 + + headers_heigth = header_heigth + show_weeks = false + show_days = false + + if self.months < 7 + show_weeks = true + headers_heigth = 2*header_heigth + if self.months < 3 + show_days = true + headers_heigth = 3*header_heigth + end + end + + g_width = PDF.right_pane_width + zoom = (g_width) / (self.date_to - self.date_from + 1) + g_height = 120 + t_height = g_height + headers_heigth + + y_start = pdf.GetY + + # Months headers + month_f = self.date_from + left = subject_width + height = header_heigth + self.months.times do + width = ((month_f >> 1) - month_f) * zoom + pdf.SetY(y_start) + pdf.SetX(left) + pdf.Cell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C") + left = left + width + month_f = month_f >> 1 + end + + # Weeks headers + if show_weeks + left = subject_width + height = header_heigth + if self.date_from.cwday == 1 + # self.date_from is monday + week_f = self.date_from + else + # find next monday after self.date_from + week_f = self.date_from + (7 - self.date_from.cwday + 1) + width = (7 - self.date_from.cwday + 1) * zoom-1 + pdf.SetY(y_start + header_heigth) + pdf.SetX(left) + pdf.Cell(width + 1, height, "", "LTR") + left = left + width+1 + end + while week_f <= self.date_to + width = (week_f + 6 <= self.date_to) ? 7 * zoom : (self.date_to - week_f + 1) * zoom + pdf.SetY(y_start + header_heigth) + pdf.SetX(left) + pdf.Cell(width, height, (width >= 5 ? week_f.cweek.to_s : ""), "LTR", 0, "C") + left = left + width + week_f = week_f+7 + end + end + + # Days headers + if show_days + left = subject_width + height = header_heigth + wday = self.date_from.cwday + pdf.SetFontStyle('B',7) + (self.date_to - self.date_from + 1).to_i.times do + width = zoom + pdf.SetY(y_start + 2 * header_heigth) + pdf.SetX(left) + pdf.Cell(width, height, day_name(wday).first, "LTR", 0, "C") + left = left + width + wday = wday + 1 + wday = 1 if wday > 7 + end + end + + pdf.SetY(y_start) + pdf.SetX(15) + pdf.Cell(subject_width+g_width-15, headers_heigth, "", 1) + + # Tasks + top = headers_heigth + y_start + pdf_subjects_and_lines(pdf, { + :top => top, + :zoom => zoom, + :subject_width => subject_width, + :g_width => g_width + }) + + + pdf.Line(15, top, subject_width+g_width, top) + pdf.Output + + + end private - - def gantt_issue_compare(x, y, issues) - if x.parent_id == y.parent_id - gantt_start_compare(x, y) - elsif x.is_ancestor_of?(y) - -1 - elsif y.is_ancestor_of?(x) - 1 + + # Renders both the subjects and lines of the Gantt chart for the + # PDF format + def pdf_subjects_and_lines(pdf, options = {}) + subject_options = {:indent => 0, :indent_increment => 5, :top_increment => 3, :render => :subject, :format => :pdf, :pdf => pdf}.merge(options) + line_options = {:indent => 0, :indent_increment => 5, :top_increment => 3, :render => :line, :format => :pdf, :pdf => pdf}.merge(options) + + if @project + render_project(@project, subject_options) + render_project(@project, line_options) else - ax = issues.select {|i| i.is_a?(Issue) && i.is_ancestor_of?(x) && !i.is_ancestor_of?(y) }.sort_by(&:lft).first - ay = issues.select {|i| i.is_a?(Issue) && i.is_ancestor_of?(y) && !i.is_ancestor_of?(x) }.sort_by(&:lft).first - if ax.nil? && ay.nil? - gantt_start_compare(x, y) - else - gantt_issue_compare(ax || x, ay || y, issues) + Project.roots.each do |project| + render_project(project, subject_options) + render_project(project, line_options) end end end - - def gantt_start_compare(x, y) - if x.start_date.nil? - -1 - elsif y.start_date.nil? - 1 - else - x.start_date <=> y.start_date - end - end + end end end diff --git a/public/images/milestone.png b/public/images/milestone.png deleted file mode 100644 index a89791cf5ec644abead1698aac4a09d03e615033..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 122 zcmeAS@N?(olHy`uVBq!ia0vp^93afW3?x5a^xFxf*aCb)T>k?Z#~AJ~C!Gc|7)yfu zf*Bm1-ADs+lssJ=LpZJ{Cjdc83Ins78%Os`sfO)hC6VXcX~FDLatA77@O1TaS?83{1OOFyBV7Oh literal 0 HcmV?d00001 diff --git a/public/images/milestone_late.png b/public/images/milestone_late.png new file mode 100644 index 0000000000000000000000000000000000000000..cf922e95450b05a61cd6d9060552e532739f8c64 GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1SGw4HSYi^#^NA%Cx&(BWL^R}Y)RhkE)4%c zaKYZ?lYt_f1s;*b3=G^tAk28_ZrvZCAbW|YuPggaE@>e>8Iza1tUw_JPZ!4!j_b*P w!U7L|DCA?V=aM?v5;~>PzLj(J3y0T0OOqU%yAIS&0IFs1boFyt=akR{0Nu(fk44ofy`glX(f`um$*pxHdF2 z{Qv*|`L*6PAn7D;cNc~ZR#^`qhqJ&VvY3H^TL^?1FWs&C0~BO0@$_|NzsV&nBx5+o sjg=87#OLYa7{YNqIpV0msfHFtMHlA3vpGIZ0?IIWy85}Sb4q9e0Cfc@%>V!Z literal 0 HcmV?d00001 diff --git a/public/images/project_marker.png b/public/images/project_marker.png new file mode 100644 index 0000000000000000000000000000000000000000..4124787d0e1f40ac2a5fee71c985128e3126b393 GIT binary patch literal 204 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqjKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p=efH0%e8j~47LG}_)Usv{<+)U!yD&=aMCIE$eJY5_^BrezX z8}c16;9x#4`&mxxpMA>7>WG(3AyYy=vRL(`1{(1bP0l+XkK@4!E( literal 0 HcmV?d00001 diff --git a/public/images/task_done.png b/public/images/task_done.png index 954ebedcec558388158c8570a0db7fd02a9e0dcd..5fdcb415c0b002cb8853aea98619fb644e473097 100644 GIT binary patch delta 119 zcmcc4*2y?QvVe(!fx$ah^A3<=EDmyaVpw-h<|UBBS>O>_%)r3)0fZTy)|kuy3bL1Y z`ns~;d~=oIJrnw}9TX^@u6{1-oD!M 1 assert_response :success assert_template 'show.html.erb' assert_not_nil assigns(:gantt) - events = assigns(:gantt).events - assert_not_nil events # Issue with start and due dates i = Issue.find(1) assert_not_nil i.due_date - assert events.include?(Issue.find(1)) - # Issue with without due date but targeted to a version with date + assert_select "div a.issue", /##{i.id}/ + # Issue with on a targeted version should not be in the events but loaded in the html i = Issue.find(2) - assert_nil i.due_date - assert events.include?(i) + assert_select "div a.issue", /##{i.id}/ end should "work cross project" do @@ -26,8 +26,8 @@ class GanttsControllerTest < ActionController::TestCase assert_response :success assert_template 'show.html.erb' assert_not_nil assigns(:gantt) - events = assigns(:gantt).events - assert_not_nil events + assert_not_nil assigns(:gantt).query + assert_nil assigns(:gantt).project end should "export to pdf" do diff --git a/test/object_daddy_helpers.rb b/test/object_daddy_helpers.rb index 4a2b85a9e..7b144b18f 100644 --- a/test/object_daddy_helpers.rb +++ b/test/object_daddy_helpers.rb @@ -25,8 +25,9 @@ module ObjectDaddyHelpers def Issue.generate_for_project!(project, attributes={}) issue = Issue.spawn(attributes) do |issue| issue.project = project + issue.tracker = project.trackers.first unless project.trackers.empty? + yield issue if block_given? end - issue.tracker = project.trackers.first unless project.trackers.empty? issue.save! issue end diff --git a/test/unit/helpers/application_helper_test.rb b/test/unit/helpers/application_helper_test.rb index 1936a981b..2906ec6e4 100644 --- a/test/unit/helpers/application_helper_test.rb +++ b/test/unit/helpers/application_helper_test.rb @@ -601,7 +601,7 @@ EXPECTED # turn off avatars Setting.gravatar_enabled = '0' - assert_nil avatar(User.find_by_mail('jsmith@somenet.foo')) + assert_equal '', avatar(User.find_by_mail('jsmith@somenet.foo')) end def test_link_to_user diff --git a/test/unit/issue_test.rb b/test/unit/issue_test.rb index e0eb479d9..dd10e0120 100644 --- a/test/unit/issue_test.rb +++ b/test/unit/issue_test.rb @@ -510,6 +510,28 @@ class IssueTest < ActiveSupport::TestCase assert !Issue.new(:due_date => nil).overdue? assert !Issue.new(:due_date => 1.day.ago.to_date, :status => IssueStatus.find(:first, :conditions => {:is_closed => true})).overdue? end + + context "#behind_schedule?" do + should "be false if the issue has no start_date" do + assert !Issue.new(:start_date => nil, :due_date => 1.day.from_now.to_date, :done_ratio => 0).behind_schedule? + end + + should "be false if the issue has no end_date" do + assert !Issue.new(:start_date => 1.day.from_now.to_date, :due_date => nil, :done_ratio => 0).behind_schedule? + end + + should "be false if the issue has more done than it's calendar time" do + assert !Issue.new(:start_date => 50.days.ago.to_date, :due_date => 50.days.from_now.to_date, :done_ratio => 90).behind_schedule? + end + + should "be true if the issue hasn't been started at all" do + assert Issue.new(:start_date => 1.day.ago.to_date, :due_date => 1.day.from_now.to_date, :done_ratio => 0).behind_schedule? + end + + should "be true if the issue has used more calendar time than it's done ratio" do + assert Issue.new(:start_date => 100.days.ago.to_date, :due_date => Date.today, :done_ratio => 90).behind_schedule? + end + end def test_assignable_users assert_kind_of User, Issue.find(1).assignable_users.first diff --git a/test/unit/lib/redmine/helpers/gantt_test.rb b/test/unit/lib/redmine/helpers/gantt_test.rb new file mode 100644 index 000000000..6b97b083f --- /dev/null +++ b/test/unit/lib/redmine/helpers/gantt_test.rb @@ -0,0 +1,703 @@ +# redMine - project management software +# Copyright (C) 2006-2008 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' + +class Redmine::Helpers::GanttTest < ActiveSupport::TestCase + # Utility methods and classes so assert_select can be used. + class GanttViewTest < ActionView::Base + include ActionView::Helpers::UrlHelper + include ActionView::Helpers::TextHelper + include ActionController::UrlWriter + include ApplicationHelper + include ProjectsHelper + include IssuesHelper + + def self.default_url_options + {:only_path => true } + end + + end + + include ActionController::Assertions::SelectorAssertions + + def setup + @response = ActionController::TestResponse.new + # Fixtures + ProjectCustomField.delete_all + Project.destroy_all + + User.current = User.find(1) + end + + def build_view + @view = GanttViewTest.new + end + + def html_document + HTML::Document.new(@response.body) + end + + # Creates a Gantt chart for a 4 week span + def create_gantt(project=Project.generate!) + @project = project + @gantt = Redmine::Helpers::Gantt.new + @gantt.project = @project + @gantt.query = Query.generate_default!(:project => @project) + @gantt.view = build_view + @gantt.instance_variable_set('@date_from', 2.weeks.ago.to_date) + @gantt.instance_variable_set('@date_to', 2.weeks.from_now.to_date) + end + + context "#number_of_rows" do + + context "with one project" do + should "return the number of rows just for that project" + end + + context "with no project" do + should "return the total number of rows for all the projects, resursively" + end + + end + + context "#number_of_rows_on_project" do + setup do + create_gantt + end + + should "clear the @query.project so cross-project issues and versions can be counted" do + assert @gantt.query.project + @gantt.number_of_rows_on_project(@project) + assert_nil @gantt.query.project + end + + should "count 1 for the project itself" do + assert_equal 1, @gantt.number_of_rows_on_project(@project) + end + + should "count the number of issues without a version" do + @project.issues << Issue.generate_for_project!(@project, :fixed_version => nil) + assert_equal 2, @gantt.number_of_rows_on_project(@project) + end + + should "count the number of versions" do + @project.versions << Version.generate! + @project.versions << Version.generate! + assert_equal 3, @gantt.number_of_rows_on_project(@project) + end + + should "count the number of issues on versions, including cross-project" do + version = Version.generate! + @project.versions << version + @project.issues << Issue.generate_for_project!(@project, :fixed_version => version) + + assert_equal 3, @gantt.number_of_rows_on_project(@project) + end + + should "recursive and count the number of rows on each subproject" do + @project.versions << Version.generate! # +1 + + @subproject = Project.generate!(:enabled_module_names => ['issue_tracking']) # +1 + @subproject.set_parent!(@project) + @subproject.issues << Issue.generate_for_project!(@subproject) # +1 + @subproject.issues << Issue.generate_for_project!(@subproject) # +1 + + @subsubproject = Project.generate!(:enabled_module_names => ['issue_tracking']) # +1 + @subsubproject.set_parent!(@subproject) + @subsubproject.issues << Issue.generate_for_project!(@subsubproject) # +1 + + assert_equal 7, @gantt.number_of_rows_on_project(@project) # +1 for self + end + end + + # TODO: more of an integration test + context "#subjects" do + setup do + create_gantt + @project.enabled_module_names = [:issue_tracking] + @tracker = Tracker.generate! + @project.trackers << @tracker + @version = Version.generate!(:effective_date => 1.week.from_now.to_date, :sharing => 'none') + @project.versions << @version + + @issue = Issue.generate!(:fixed_version => @version, + :subject => "gantt#line_for_project", + :tracker => @tracker, + :project => @project, + :done_ratio => 30, + :start_date => Date.yesterday, + :due_date => 1.week.from_now.to_date) + @project.issues << @issue + + @response.body = @gantt.subjects + end + + context "project" do + should "be rendered" do + assert_select "div.project-name a", /#{@project.name}/ + end + + should "have an indent of 4" do + assert_select "div.project-name[style*=left:4px]" + end + end + + context "version" do + should "be rendered" do + assert_select "div.version-name a", /#{@version.name}/ + end + + should "be indented 24 (one level)" do + assert_select "div.version-name[style*=left:24px]" + end + end + + context "issue" do + should "be rendered" do + assert_select "div.issue-subject", /#{@issue.subject}/ + end + + should "be indented 44 (two levels)" do + assert_select "div.issue-subject[style*=left:44px]" + end + end + end + + context "#lines" do + setup do + create_gantt + @project.enabled_module_names = [:issue_tracking] + @tracker = Tracker.generate! + @project.trackers << @tracker + @version = Version.generate!(:effective_date => 1.week.from_now.to_date) + @project.versions << @version + @issue = Issue.generate!(:fixed_version => @version, + :subject => "gantt#line_for_project", + :tracker => @tracker, + :project => @project, + :done_ratio => 30, + :start_date => Date.yesterday, + :due_date => 1.week.from_now.to_date) + @project.issues << @issue + + @response.body = @gantt.lines + end + + context "project" do + should "be rendered" do + assert_select "div.project_todo" + assert_select "div.project-line.starting" + assert_select "div.project-line.ending" + assert_select "div.label.project-name", /#{@project.name}/ + end + end + + context "version" do + should "be rendered" do + assert_select "div.milestone_todo" + assert_select "div.milestone.starting" + assert_select "div.milestone.ending" + assert_select "div.label.version-name", /#{@version.name}/ + end + end + + context "issue" do + should "be rendered" do + assert_select "div.task_todo" + assert_select "div.label.issue-name", /#{@issue.done_ratio}/ + assert_select "div.tooltip", /#{@issue.subject}/ + end + end + end + + context "#render_project" do + should "be tested" + end + + context "#render_issues" do + should "be tested" + end + + context "#render_version" do + should "be tested" + end + + context "#subject_for_project" do + setup do + create_gantt + end + + context ":html format" do + should "add an absolute positioned div" do + @response.body = @gantt.subject_for_project(@project, {:format => :html}) + assert_select "div[style*=absolute]" + end + + should "use the indent option to move the div to the right" do + @response.body = @gantt.subject_for_project(@project, {:format => :html, :indent => 40}) + assert_select "div[style*=left:40]" + end + + should "include the project name" do + @response.body = @gantt.subject_for_project(@project, {:format => :html}) + assert_select 'div', :text => /#{@project.name}/ + end + + should "include a link to the project" do + @response.body = @gantt.subject_for_project(@project, {:format => :html}) + assert_select 'a[href=?]', "/projects/#{@project.identifier}", :text => /#{@project.name}/ + end + + should "style overdue projects" do + @project.enabled_module_names = [:issue_tracking] + @project.versions << Version.generate!(:effective_date => Date.yesterday) + + assert @project.overdue?, "Need an overdue project for this test" + @response.body = @gantt.subject_for_project(@project, {:format => :html}) + + assert_select 'div span.project-overdue' + end + + + end + + should "test the PNG format" + should "test the PDF format" + end + + context "#line_for_project" do + setup do + create_gantt + @project.enabled_module_names = [:issue_tracking] + @tracker = Tracker.generate! + @project.trackers << @tracker + @version = Version.generate!(:effective_date => Date.yesterday) + @project.versions << @version + + @project.issues << Issue.generate!(:fixed_version => @version, + :subject => "gantt#line_for_project", + :tracker => @tracker, + :project => @project, + :done_ratio => 30, + :start_date => Date.yesterday, + :due_date => 1.week.from_now.to_date) + end + + context ":html format" do + context "todo line" do + should "start from the starting point on the left" do + @response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4}) + assert_select "div.project_todo[style*=left:52px]" + end + + should "be the total width of the project" do + @response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4}) + assert_select "div.project_todo[style*=width:31px]" + end + + end + + context "late line" do + should "start from the starting point on the left" do + @response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4}) + assert_select "div.project_late[style*=left:52px]" + end + + should "be the total delayed width of the project" do + @response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4}) + assert_select "div.project_late[style*=width:6px]" + end + end + + context "done line" do + should "start from the starting point on the left" do + @response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4}) + assert_select "div.project_done[style*=left:52px]" + end + + should "Be the total done width of the project" do + @response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4}) + assert_select "div.project_done[style*=left:52px]" + end + end + + context "starting marker" do + should "not appear if the starting point is off the gantt chart" do + # Shift the date range of the chart + @gantt.instance_variable_set('@date_from', Date.today) + + @response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4}) + assert_select "div.project-line.starting", false + end + + should "appear at the starting point" do + @response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4}) + assert_select "div.project-line.starting[style*=left:52px]" + end + end + + context "ending marker" do + should "not appear if the starting point is off the gantt chart" do + # Shift the date range of the chart + @gantt.instance_variable_set('@date_to', 2.weeks.ago.to_date) + + @response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4}) + assert_select "div.project-line.ending", false + + end + + should "appear at the end of the date range" do + @response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4}) + assert_select "div.project-line.ending[style*=left:84px]" + end + end + + context "status content" do + should "appear at the far left, even if it's far in the past" do + @gantt.instance_variable_set('@date_to', 2.weeks.ago.to_date) + + @response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4}) + assert_select "div.project-name", /#{@project.name}/ + end + + should "show the project name" do + @response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4}) + assert_select "div.project-name", /#{@project.name}/ + end + + should "show the percent complete" do + @response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4}) + assert_select "div.project-name", /0%/ + end + end + end + + should "test the PNG format" + should "test the PDF format" + end + + context "#subject_for_version" do + setup do + create_gantt + @project.enabled_module_names = [:issue_tracking] + @tracker = Tracker.generate! + @project.trackers << @tracker + @version = Version.generate!(:effective_date => Date.yesterday) + @project.versions << @version + + @project.issues << Issue.generate!(:fixed_version => @version, + :subject => "gantt#subject_for_version", + :tracker => @tracker, + :project => @project, + :start_date => Date.today) + + end + + context ":html format" do + should "add an absolute positioned div" do + @response.body = @gantt.subject_for_version(@version, {:format => :html}) + assert_select "div[style*=absolute]" + end + + should "use the indent option to move the div to the right" do + @response.body = @gantt.subject_for_version(@version, {:format => :html, :indent => 40}) + assert_select "div[style*=left:40]" + end + + should "include the version name" do + @response.body = @gantt.subject_for_version(@version, {:format => :html}) + assert_select 'div', :text => /#{@version.name}/ + end + + should "include a link to the version" do + @response.body = @gantt.subject_for_version(@version, {:format => :html}) + assert_select 'a[href=?]', Regexp.escape("/versions/show/#{@version.to_param}"), :text => /#{@version.name}/ + end + + should "style late versions" do + assert @version.overdue?, "Need an overdue version for this test" + @response.body = @gantt.subject_for_version(@version, {:format => :html}) + + assert_select 'div span.version-behind-schedule' + end + + should "style behind schedule versions" do + assert @version.behind_schedule?, "Need a behind schedule version for this test" + @response.body = @gantt.subject_for_version(@version, {:format => :html}) + + assert_select 'div span.version-behind-schedule' + end + end + should "test the PNG format" + should "test the PDF format" + end + + context "#line_for_version" do + setup do + create_gantt + @project.enabled_module_names = [:issue_tracking] + @tracker = Tracker.generate! + @project.trackers << @tracker + @version = Version.generate!(:effective_date => 1.week.from_now.to_date) + @project.versions << @version + + @project.issues << Issue.generate!(:fixed_version => @version, + :subject => "gantt#line_for_project", + :tracker => @tracker, + :project => @project, + :done_ratio => 30, + :start_date => Date.yesterday, + :due_date => 1.week.from_now.to_date) + end + + context ":html format" do + context "todo line" do + should "start from the starting point on the left" do + @response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4}) + assert_select "div.milestone_todo[style*=left:52px]" + end + + should "be the total width of the version" do + @response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4}) + assert_select "div.milestone_todo[style*=width:31px]" + end + + end + + context "late line" do + should "start from the starting point on the left" do + @response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4}) + assert_select "div.milestone_late[style*=left:52px]" + end + + should "be the total delayed width of the version" do + @response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4}) + assert_select "div.milestone_late[style*=width:6px]" + end + end + + context "done line" do + should "start from the starting point on the left" do + @response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4}) + assert_select "div.milestone_done[style*=left:52px]" + end + + should "Be the total done width of the version" do + @response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4}) + assert_select "div.milestone_done[style*=left:52px]" + end + end + + context "starting marker" do + should "not appear if the starting point is off the gantt chart" do + # Shift the date range of the chart + @gantt.instance_variable_set('@date_from', Date.today) + + @response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4}) + assert_select "div.milestone.starting", false + end + + should "appear at the starting point" do + @response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4}) + assert_select "div.milestone.starting[style*=left:52px]" + end + end + + context "ending marker" do + should "not appear if the starting point is off the gantt chart" do + # Shift the date range of the chart + @gantt.instance_variable_set('@date_to', 2.weeks.ago.to_date) + + @response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4}) + assert_select "div.milestone.ending", false + + end + + should "appear at the end of the date range" do + @response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4}) + assert_select "div.milestone.ending[style*=left:84px]" + end + end + + context "status content" do + should "appear at the far left, even if it's far in the past" do + @gantt.instance_variable_set('@date_to', 2.weeks.ago.to_date) + + @response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4}) + assert_select "div.version-name", /#{@version.name}/ + end + + should "show the version name" do + @response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4}) + assert_select "div.version-name", /#{@version.name}/ + end + + should "show the percent complete" do + @response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4}) + assert_select "div.version-name", /30%/ + end + end + end + + should "test the PNG format" + should "test the PDF format" + end + + context "#subject_for_issue" do + setup do + create_gantt + @project.enabled_module_names = [:issue_tracking] + @tracker = Tracker.generate! + @project.trackers << @tracker + + @issue = Issue.generate!(:subject => "gantt#subject_for_issue", + :tracker => @tracker, + :project => @project, + :start_date => 3.days.ago.to_date, + :due_date => Date.yesterday) + @project.issues << @issue + + end + + context ":html format" do + should "add an absolute positioned div" do + @response.body = @gantt.subject_for_issue(@issue, {:format => :html}) + assert_select "div[style*=absolute]" + end + + should "use the indent option to move the div to the right" do + @response.body = @gantt.subject_for_issue(@issue, {:format => :html, :indent => 40}) + assert_select "div[style*=left:40]" + end + + should "include the issue subject" do + @response.body = @gantt.subject_for_issue(@issue, {:format => :html}) + assert_select 'div', :text => /#{@issue.subject}/ + end + + should "include a link to the issue" do + @response.body = @gantt.subject_for_issue(@issue, {:format => :html}) + assert_select 'a[href=?]', Regexp.escape("/issues/#{@issue.to_param}"), :text => /#{@tracker.name} ##{@issue.id}/ + end + + should "style overdue issues" do + assert @issue.overdue?, "Need an overdue issue for this test" + @response.body = @gantt.subject_for_issue(@issue, {:format => :html}) + + assert_select 'div span.issue-overdue' + end + + end + should "test the PNG format" + should "test the PDF format" + end + + context "#line_for_issue" do + setup do + create_gantt + @project.enabled_module_names = [:issue_tracking] + @tracker = Tracker.generate! + @project.trackers << @tracker + @version = Version.generate!(:effective_date => 1.week.from_now.to_date) + @project.versions << @version + @issue = Issue.generate!(:fixed_version => @version, + :subject => "gantt#line_for_project", + :tracker => @tracker, + :project => @project, + :done_ratio => 30, + :start_date => Date.yesterday, + :due_date => 1.week.from_now.to_date) + @project.issues << @issue + end + + context ":html format" do + context "todo line" do + should "start from the starting point on the left" do + @response.body = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4}) + assert_select "div.task_todo[style*=left:52px]" + end + + should "be the total width of the issue" do + @response.body = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4}) + assert_select "div.task_todo[style*=width:34px]" + end + + end + + context "late line" do + should "start from the starting point on the left" do + @response.body = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4}) + assert_select "div.task_late[style*=left:52px]" + end + + should "be the total delayed width of the issue" do + @response.body = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4}) + assert_select "div.task_late[style*=width:6px]" + end + end + + context "done line" do + should "start from the starting point on the left" do + @response.body = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4}) + assert_select "div.task_done[style*=left:52px]" + end + + should "Be the total done width of the issue" do + @response.body = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4}) + assert_select "div.task_done[style*=left:52px]" + end + end + + context "status content" do + should "appear at the far left, even if it's far in the past" do + @gantt.instance_variable_set('@date_to', 2.weeks.ago.to_date) + + @response.body = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4}) + assert_select "div.issue-name" + end + + should "show the issue status" do + @response.body = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4}) + assert_select "div.issue-name", /#{@issue.status.name}/ + end + + should "show the percent complete" do + @response.body = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4}) + assert_select "div.issue-name", /30%/ + end + end + end + + should "have an issue tooltip" do + @response.body = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4}) + assert_select "div.tooltip", /#{@issue.subject}/ + end + + should "test the PNG format" + should "test the PDF format" + end + + context "#to_image" do + should "be tested" + end + + context "#to_pdf" do + should "be tested" + end + +end diff --git a/test/unit/project_test.rb b/test/unit/project_test.rb index 7870dc2a5..b0b5a4837 100644 --- a/test/unit/project_test.rb +++ b/test/unit/project_test.rb @@ -842,4 +842,122 @@ class ProjectTest < ActiveSupport::TestCase end + context "#start_date" do + setup do + ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests + @project = Project.generate!(:identifier => 'test0') + @project.trackers << Tracker.generate! + end + + should "be nil if there are no issues on the project" do + assert_nil @project.start_date + end + + should "be nil if issue tracking is disabled" do + Issue.generate_for_project!(@project, :start_date => Date.today) + @project.enabled_modules.find_all_by_name('issue_tracking').each {|m| m.destroy} + @project.reload + + assert_nil @project.start_date + end + + should "be the earliest start date of it's issues" do + early = 7.days.ago.to_date + Issue.generate_for_project!(@project, :start_date => Date.today) + Issue.generate_for_project!(@project, :start_date => early) + + assert_equal early, @project.start_date + end + + end + + context "#due_date" do + setup do + ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests + @project = Project.generate!(:identifier => 'test0') + @project.trackers << Tracker.generate! + end + + should "be nil if there are no issues on the project" do + assert_nil @project.due_date + end + + should "be nil if issue tracking is disabled" do + Issue.generate_for_project!(@project, :due_date => Date.today) + @project.enabled_modules.find_all_by_name('issue_tracking').each {|m| m.destroy} + @project.reload + + assert_nil @project.due_date + end + + should "be the latest due date of it's issues" do + future = 7.days.from_now.to_date + Issue.generate_for_project!(@project, :due_date => future) + Issue.generate_for_project!(@project, :due_date => Date.today) + + assert_equal future, @project.due_date + end + + should "be the latest due date of it's versions" do + future = 7.days.from_now.to_date + @project.versions << Version.generate!(:effective_date => future) + @project.versions << Version.generate!(:effective_date => Date.today) + + + assert_equal future, @project.due_date + + end + + should "pick the latest date from it's issues and versions" do + future = 7.days.from_now.to_date + far_future = 14.days.from_now.to_date + Issue.generate_for_project!(@project, :due_date => far_future) + @project.versions << Version.generate!(:effective_date => future) + + assert_equal far_future, @project.due_date + end + + end + + context "Project#completed_percent" do + setup do + ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests + @project = Project.generate!(:identifier => 'test0') + @project.trackers << Tracker.generate! + end + + context "no versions" do + should "be 100" do + assert_equal 100, @project.completed_percent + end + end + + context "with versions" do + should "return 0 if the versions have no issues" do + Version.generate!(:project => @project) + Version.generate!(:project => @project) + + assert_equal 0, @project.completed_percent + end + + should "return 100 if the version has only closed issues" do + v1 = Version.generate!(:project => @project) + Issue.generate_for_project!(@project, :status => IssueStatus.find_by_name('Closed'), :fixed_version => v1) + v2 = Version.generate!(:project => @project) + Issue.generate_for_project!(@project, :status => IssueStatus.find_by_name('Closed'), :fixed_version => v2) + + assert_equal 100, @project.completed_percent + end + + should "return the averaged completed percent of the versions (not weighted)" do + v1 = Version.generate!(:project => @project) + Issue.generate_for_project!(@project, :status => IssueStatus.find_by_name('New'), :estimated_hours => 10, :done_ratio => 50, :fixed_version => v1) + v2 = Version.generate!(:project => @project) + Issue.generate_for_project!(@project, :status => IssueStatus.find_by_name('New'), :estimated_hours => 10, :done_ratio => 50, :fixed_version => v2) + + assert_equal 50, @project.completed_percent + end + + end + end end diff --git a/test/unit/version_test.rb b/test/unit/version_test.rb index 1abb4a272..b30eedaea 100644 --- a/test/unit/version_test.rb +++ b/test/unit/version_test.rb @@ -104,7 +104,57 @@ class VersionTest < ActiveSupport::TestCase assert_progress_equal (25.0*0.2 + 25.0*1 + 10.0*0.3 + 40.0*0.1)/100.0*100, v.completed_pourcent assert_progress_equal 25.0/100.0*100, v.closed_pourcent end - + + context "#behind_schedule?" do + setup do + ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests + @project = Project.generate!(:identifier => 'test0') + @project.trackers << Tracker.generate! + + @version = Version.generate!(:project => @project, :effective_date => nil) + end + + should "be false if there are no issues assigned" do + @version.update_attribute(:effective_date, Date.yesterday) + assert_equal false, @version.behind_schedule? + end + + should "be false if there is no effective_date" do + assert_equal false, @version.behind_schedule? + end + + should "be false if all of the issues are ahead of schedule" do + @version.update_attribute(:effective_date, 7.days.from_now.to_date) + @version.fixed_issues = [ + Issue.generate_for_project!(@project, :start_date => 7.days.ago, :done_ratio => 60), # 14 day span, 60% done, 50% time left + Issue.generate_for_project!(@project, :start_date => 7.days.ago, :done_ratio => 60) # 14 day span, 60% done, 50% time left + ] + assert_equal 60, @version.completed_pourcent + assert_equal false, @version.behind_schedule? + end + + should "be true if any of the issues are behind schedule" do + @version.update_attribute(:effective_date, 7.days.from_now.to_date) + @version.fixed_issues = [ + Issue.generate_for_project!(@project, :start_date => 7.days.ago, :done_ratio => 60), # 14 day span, 60% done, 50% time left + Issue.generate_for_project!(@project, :start_date => 7.days.ago, :done_ratio => 20) # 14 day span, 20% done, 50% time left + ] + assert_equal 40, @version.completed_pourcent + assert_equal true, @version.behind_schedule? + end + + should "be false if all of the issues are complete" do + @version.update_attribute(:effective_date, 7.days.from_now.to_date) + @version.fixed_issues = [ + Issue.generate_for_project!(@project, :start_date => 14.days.ago, :done_ratio => 100, :status => IssueStatus.find(5)), # 7 day span + Issue.generate_for_project!(@project, :start_date => 14.days.ago, :done_ratio => 100, :status => IssueStatus.find(5)) # 7 day span + ] + assert_equal 100, @version.completed_pourcent + assert_equal false, @version.behind_schedule? + + end + end + context "#estimated_hours" do setup do @version = Version.create!(:project_id => 1, :name => '#estimated_hours') diff --git a/vendor/plugins/gravatar/Rakefile b/vendor/plugins/gravatar/Rakefile index 9e4854916..e67e5e7f9 100644 --- a/vendor/plugins/gravatar/Rakefile +++ b/vendor/plugins/gravatar/Rakefile @@ -6,7 +6,7 @@ task :default => :spec desc 'Run all application-specific specs' Spec::Rake::SpecTask.new(:spec) do |t| - t.rcov = true + # t.rcov = true end desc "Report code statistics (KLOCs, etc) from the application" diff --git a/vendor/plugins/gravatar/lib/gravatar.rb b/vendor/plugins/gravatar/lib/gravatar.rb index 6246645bc..9af1fed16 100644 --- a/vendor/plugins/gravatar/lib/gravatar.rb +++ b/vendor/plugins/gravatar/lib/gravatar.rb @@ -26,6 +26,9 @@ module GravatarHelper # decorational picture, the alt text should be empty according to the # XHTML specs. :alt => '', + + # The title text to use for the img tag for the gravatar. + :title => '', # The class to assign to the img tag for the gravatar. :class => 'gravatar', @@ -48,8 +51,8 @@ module GravatarHelper def gravatar(email, options={}) src = h(gravatar_url(email, options)) options = DEFAULT_OPTIONS.merge(options) - [:class, :alt, :size].each { |opt| options[opt] = h(options[opt]) } - "\"#{options[:alt]}\"" + [:class, :alt, :size, :title].each { |opt| options[opt] = h(options[opt]) } + "\"#{options[:alt]}\"" end # Returns the base Gravatar URL for the given email hash. If ssl evaluates to true, @@ -82,4 +85,4 @@ module GravatarHelper end -end \ No newline at end of file +end diff --git a/vendor/plugins/gravatar/spec/gravatar_spec.rb b/vendor/plugins/gravatar/spec/gravatar_spec.rb index a11d2683a..6f78d79ad 100644 --- a/vendor/plugins/gravatar/spec/gravatar_spec.rb +++ b/vendor/plugins/gravatar/spec/gravatar_spec.rb @@ -4,34 +4,40 @@ require 'active_support' # to get "returning" require File.dirname(__FILE__) + '/../lib/gravatar' include GravatarHelper, GravatarHelper::PublicMethods, ERB::Util -context "gravatar_url with a custom default URL" do - setup do +describe "gravatar_url with a custom default URL" do + before(:each) do @original_options = DEFAULT_OPTIONS.dup DEFAULT_OPTIONS[:default] = "no_avatar.png" @url = gravatar_url("somewhere") end - specify "should include the \"default\" argument in the result" do + it "should include the \"default\" argument in the result" do @url.should match(/&default=no_avatar.png/) end - teardown do + after(:each) do DEFAULT_OPTIONS.merge!(@original_options) end end -context "gravatar_url with default settings" do - setup do +describe "gravatar_url with default settings" do + before(:each) do @url = gravatar_url("somewhere") end - specify "should have a nil default URL" do + it "should have a nil default URL" do DEFAULT_OPTIONS[:default].should be_nil end - specify "should not include the \"default\" argument in the result" do + it "should not include the \"default\" argument in the result" do @url.should_not match(/&default=/) end -end \ No newline at end of file +end + +describe "gravatar with a custom title option" do + it "should include the title in the result" do + gravatar('example@example.com', :title => "This is a title attribute").should match(/This is a title attribute/) + end +end From abf3ee4999050bd24ba75a53805bc7e7ac80ed0d Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Fri, 10 Sep 2010 03:09:11 +0000 Subject: [PATCH 37/72] Add project names to the Versions in the Gantt export. #5904 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4073 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/models/version.rb | 4 ++++ lib/redmine/helpers/gantt.rb | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/models/version.rb b/app/models/version.rb index c3969fe87..95e6ad5f6 100644 --- a/app/models/version.rb +++ b/app/models/version.rb @@ -135,6 +135,10 @@ class Version < ActiveRecord::Base end def to_s; name end + + def to_s_with_project + "#{project} - #{name}" + end # Versions are sorted by effective_date and "Project Name - Version name" # Those with no effective_date are at the end, sorted by "Project Name - Version name" diff --git a/lib/redmine/helpers/gantt.rb b/lib/redmine/helpers/gantt.rb index 33a4e1c2c..7c694022a 100644 --- a/lib/redmine/helpers/gantt.rb +++ b/lib/redmine/helpers/gantt.rb @@ -399,13 +399,13 @@ module Redmine options[:image].fill('black') options[:image].stroke('transparent') options[:image].stroke_width(1) - options[:image].text(options[:indent], options[:top] + 2, version.name) + options[:image].text(options[:indent], options[:top] + 2, version.to_s_with_project) when :pdf options[:pdf].SetY(options[:top]) options[:pdf].SetX(15) char_limit = PDF::MaxCharactorsForSubject - options[:indent] - options[:pdf].Cell(options[:subject_width]-15, 5, (" " * options[:indent]) +"#{version.name}".sub(/^(.{#{char_limit}}[^\s]*\s).*$/, '\1 (...)'), "LR") + options[:pdf].Cell(options[:subject_width]-15, 5, (" " * options[:indent]) +"#{version.to_s_with_project}".sub(/^(.{#{char_limit}}[^\s]*\s).*$/, '\1 (...)'), "LR") options[:pdf].SetY(options[:top]) options[:pdf].SetX(options[:subject_width]) From 5e1c29523003f697b4ba88998c1c587881785820 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Fri, 10 Sep 2010 03:09:18 +0000 Subject: [PATCH 38/72] Fixed the zoom, previous, and next links on the Gantt chart. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4074 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/helpers/issues_helper.rb | 4 ++-- app/views/gantts/show.html.erb | 4 ++-- lib/redmine/helpers/gantt.rb | 10 +++++++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 284aae91a..f1ddcfcbf 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -245,7 +245,7 @@ module IssuesHelper when :in if gantt.zoom < 4 link_to_remote(l(:text_zoom_in) + image_tag('zoom_in.png', img_attributes.merge(:alt => l(:text_zoom_in))), - {:url => gantt.params.merge(:zoom => (gantt.zoom+1)), :update => 'content'}, + {:url => gantt.params.merge(:zoom => (gantt.zoom+1)), :method => :get, :update => 'content'}, {:href => url_for(gantt.params.merge(:zoom => (gantt.zoom+1)))}) else l(:text_zoom_in) + @@ -255,7 +255,7 @@ module IssuesHelper when :out if gantt.zoom > 1 link_to_remote(l(:text_zoom_out) + image_tag('zoom_out.png', img_attributes.merge(:alt => l(:text_zoom_out))), - {:url => gantt.params.merge(:zoom => (gantt.zoom-1)), :update => 'content'}, + {:url => gantt.params.merge(:zoom => (gantt.zoom-1)), :method => :get, :update => 'content'}, {:href => url_for(gantt.params.merge(:zoom => (gantt.zoom-1)))}) else l(:text_zoom_out) + diff --git a/app/views/gantts/show.html.erb b/app/views/gantts/show.html.erb index ce8c67b26..5a64054af 100644 --- a/app/views/gantts/show.html.erb +++ b/app/views/gantts/show.html.erb @@ -169,8 +169,8 @@ if Date.today >= @gantt.date_from and Date.today <= @gantt.date_to %> - - + +
    <%= link_to_remote ('« ' + l(:label_previous)), {:url => @gantt.params_previous, :update => 'content', :complete => 'window.scrollTo(0,0)'}, {:href => url_for(@gantt.params_previous)} %><%= link_to_remote (l(:label_next) + ' »'), {:url => @gantt.params_next, :update => 'content', :complete => 'window.scrollTo(0,0)'}, {:href => url_for(@gantt.params_next)} %><%= link_to_remote ('« ' + l(:label_previous)), {:url => @gantt.params_previous, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'}, {:href => url_for(@gantt.params_previous)} %><%= link_to_remote (l(:label_next) + ' »'), {:url => @gantt.params_next, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'}, {:href => url_for(@gantt.params_next)} %>
    diff --git a/lib/redmine/helpers/gantt.rb b/lib/redmine/helpers/gantt.rb index 7c694022a..cec323720 100644 --- a/lib/redmine/helpers/gantt.rb +++ b/lib/redmine/helpers/gantt.rb @@ -68,17 +68,21 @@ module Redmine @date_from = Date.civil(@year_from, @month_from, 1) @date_to = (@date_from >> @months) - 1 end + + def common_params + { :controller => 'gantts', :action => 'show', :project_id => @project } + end def params - { :zoom => zoom, :year => year_from, :month => month_from, :months => months } + common_params.merge({ :zoom => zoom, :year => year_from, :month => month_from, :months => months }) end def params_previous - { :year => (date_from << months).year, :month => (date_from << months).month, :zoom => zoom, :months => months } + common_params.merge({:year => (date_from << months).year, :month => (date_from << months).month, :zoom => zoom, :months => months }) end def params_next - { :year => (date_from >> months).year, :month => (date_from >> months).month, :zoom => zoom, :months => months } + common_params.merge({:year => (date_from >> months).year, :month => (date_from >> months).month, :zoom => zoom, :months => months }) end ### Extracted from the HTML view/helpers From 3a2efb47572d198c91a09b1bf92e717b843980ed Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Fri, 10 Sep 2010 16:00:49 +0000 Subject: [PATCH 39/72] Refactor: convert ProjectEnumerations to a resource on a project. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4075 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/application_controller.rb | 7 ++++++ .../project_enumerations_controller.rb | 6 ++--- app/views/projects/settings/_activities.rhtml | 4 ++-- config/routes.rb | 10 +++------ lib/redmine.rb | 2 +- .../project_enumerations_controller_test.rb | 22 +++++++++---------- test/integration/routing_test.rb | 4 ++-- 7 files changed, 29 insertions(+), 26 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 725bde788..32bb528a3 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -169,6 +169,13 @@ class ApplicationController < ActionController::Base render_404 end + # Find project of id params[:project_id] + def find_project_by_project_id + @project = Project.find(params[:project_id]) + rescue ActiveRecord::RecordNotFound + render_404 + end + # Find a project based on params[:project_id] # TODO: some subclasses override this, see about merging their logic def find_optional_project diff --git a/app/controllers/project_enumerations_controller.rb b/app/controllers/project_enumerations_controller.rb index d63e23641..0b15887fa 100644 --- a/app/controllers/project_enumerations_controller.rb +++ b/app/controllers/project_enumerations_controller.rb @@ -1,9 +1,9 @@ class ProjectEnumerationsController < ApplicationController - before_filter :find_project + before_filter :find_project_by_project_id before_filter :authorize - def save - if request.post? && params[:enumerations] + def update + if request.put? && params[:enumerations] Project.transaction do params[:enumerations].each do |id, activity| @project.update_or_create_time_entry_activity(id, activity) diff --git a/app/views/projects/settings/_activities.rhtml b/app/views/projects/settings/_activities.rhtml index 5909ac64c..726e6240c 100644 --- a/app/views/projects/settings/_activities.rhtml +++ b/app/views/projects/settings/_activities.rhtml @@ -1,4 +1,4 @@ -<% form_tag({:controller => 'project_enumerations', :action => 'save', :id => @project}, :class => "tabular") do %> +<% form_tag(project_project_enumerations_path(@project), :method => :put, :class => "tabular") do %> @@ -32,7 +32,7 @@
    -<%= link_to(l(:button_reset), {:controller => 'project_enumerations', :action => 'destroy', :id => @project}, +<%= link_to(l(:button_reset), project_project_enumerations_path(@project), :method => :delete, :confirm => l(:text_are_you_sure), :class => 'icon icon-del') %> diff --git a/config/routes.rb b/config/routes.rb index 9f12cd454..129c3b456 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -179,7 +179,9 @@ ActionController::Routing::Routes.draw do |map| :modules => :post, :archive => :post, :unarchive => :post - } + } do |project| + project.resource :project_enumerations, :as => 'enumerations', :only => [:update, :destroy] + end # Destroy uses a get request to prompt the user before the actual DELETE request map.project_destroy_confirm 'projects/:id/destroy', :controller => 'projects', :action => 'destroy', :conditions => {:method => :get} @@ -195,13 +197,7 @@ ActionController::Routing::Routes.draw do |map| project_mapper.with_options :conditions => {:method => :post} do |project_actions| project_actions.connect 'projects/:id/files/new', :controller => 'files', :action => 'new' - project_actions.connect 'projects/:id/activities/save', :controller => 'project_enumerations', :action => 'save' end - - project_mapper.with_options :conditions => {:method => :delete} do |project_actions| - project_actions.conditions 'projects/:id/reset_activities', :controller => 'project_enumerations', :action => 'destroy' - end - end map.with_options :controller => 'activities', :action => 'index', :conditions => {:method => :get} do |activity| diff --git a/lib/redmine.rb b/lib/redmine.rb index 534f85875..526b83e65 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -87,7 +87,7 @@ Redmine::AccessControl.map do |map| map.permission :view_time_entries, :timelog => [:details, :report] map.permission :edit_time_entries, {:timelog => [:edit, :destroy]}, :require => :member map.permission :edit_own_time_entries, {:timelog => [:edit, :destroy]}, :require => :loggedin - map.permission :manage_project_activities, {:project_enumerations => [:save, :destroy]}, :require => :member + map.permission :manage_project_activities, {:project_enumerations => [:update, :destroy]}, :require => :member end map.project_module :news do |map| diff --git a/test/functional/project_enumerations_controller_test.rb b/test/functional/project_enumerations_controller_test.rb index c03be04fb..942399a96 100644 --- a/test/functional/project_enumerations_controller_test.rb +++ b/test/functional/project_enumerations_controller_test.rb @@ -8,11 +8,11 @@ class ProjectEnumerationsControllerTest < ActionController::TestCase Setting.default_language = 'en' end - def test_save_to_override_system_activities + def test_update_to_override_system_activities @request.session[:user_id] = 2 # manager billable_field = TimeEntryActivityCustomField.find_by_name("Billable") - post :save, :id => 1, :enumerations => { + put :update, :project_id => 1, :enumerations => { "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # Design, De-activate "10"=> {"parent_id"=>"10", "custom_field_values"=>{"7"=>"0"}, "active"=>"1"}, # Development, Change custom value "14"=>{"parent_id"=>"14", "custom_field_values"=>{"7"=>"1"}, "active"=>"1"}, # Inactive Activity, Activate with custom value @@ -58,7 +58,7 @@ class ProjectEnumerationsControllerTest < ActionController::TestCase assert_equal nil, project.time_entry_activities.find_by_name("QA"), "Custom QA activity created when it wasn't modified" end - def test_save_will_update_project_specific_activities + def test_update_will_update_project_specific_activities @request.session[:user_id] = 2 # manager project_activity = TimeEntryActivity.new({ @@ -77,7 +77,7 @@ class ProjectEnumerationsControllerTest < ActionController::TestCase assert project_activity_two.save - post :save, :id => 1, :enumerations => { + put :update, :project_id => 1, :enumerations => { project_activity.id => {"custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # De-activate project_activity_two.id => {"custom_field_values"=>{"7" => "1"}, "active"=>"0"} # De-activate } @@ -100,11 +100,11 @@ class ProjectEnumerationsControllerTest < ActionController::TestCase assert !activity_two.active? end - def test_save_when_creating_new_activities_will_convert_existing_data + def test_update_when_creating_new_activities_will_convert_existing_data assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size @request.session[:user_id] = 2 # manager - post :save, :id => 1, :enumerations => { + put :update, :project_id => 1, :enumerations => { "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"} # Design, De-activate } assert_response :redirect @@ -116,7 +116,7 @@ class ProjectEnumerationsControllerTest < ActionController::TestCase assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(project_specific_activity.id, 1).size, "No Time Entries assigned to the project activity" end - def test_save_when_creating_new_activities_will_not_convert_existing_data_if_an_exception_is_raised + def test_update_when_creating_new_activities_will_not_convert_existing_data_if_an_exception_is_raised # TODO: Need to cause an exception on create but these tests # aren't setup for mocking. Just create a record now so the # second one is a dupicate @@ -128,7 +128,7 @@ class ProjectEnumerationsControllerTest < ActionController::TestCase assert_equal 1, TimeEntry.find_all_by_activity_id_and_project_id(10, 1).size @request.session[:user_id] = 2 # manager - post :save, :id => 1, :enumerations => { + put :update, :project_id => 1, :enumerations => { "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # Design "10"=> {"parent_id"=>"10", "custom_field_values"=>{"7"=>"0"}, "active"=>"1"} # Development, Change custom value } @@ -140,7 +140,7 @@ class ProjectEnumerationsControllerTest < ActionController::TestCase assert_equal 1, TimeEntry.find_all_by_activity_id_and_project_id(10, 1).size, "Time Entries are not assigned to system activities" end - def test_destroy + def test_destroy @request.session[:user_id] = 2 # manager project_activity = TimeEntryActivity.new({ :name => 'Project Specific', @@ -157,7 +157,7 @@ class ProjectEnumerationsControllerTest < ActionController::TestCase }) assert project_activity_two.save - delete :destroy, :id => 1 + delete :destroy, :project_id => 1 assert_response :redirect assert_redirected_to 'projects/ecookbook/settings/activities' @@ -177,7 +177,7 @@ class ProjectEnumerationsControllerTest < ActionController::TestCase assert TimeEntry.update_all("activity_id = '#{project_activity.id}'", ["project_id = ? AND activity_id = ?", 1, 9]) assert 3, TimeEntry.find_all_by_activity_id_and_project_id(project_activity.id, 1).size - delete :destroy, :id => 1 + delete :destroy, :project_id => 1 assert_response :redirect assert_redirected_to 'projects/ecookbook/settings/activities' diff --git a/test/integration/routing_test.rb b/test/integration/routing_test.rb index 5cd0b2d39..ac75d4d7c 100644 --- a/test/integration/routing_test.rb +++ b/test/integration/routing_test.rb @@ -182,14 +182,14 @@ class RoutingTest < ActionController::IntegrationTest should_route :post, "/projects/33/files/new", :controller => 'files', :action => 'new', :id => '33' should_route :post, "/projects/64/archive", :controller => 'projects', :action => 'archive', :id => '64' should_route :post, "/projects/64/unarchive", :controller => 'projects', :action => 'unarchive', :id => '64' - should_route :post, "/projects/64/activities/save", :controller => 'project_enumerations', :action => 'save', :id => '64' + should_route :put, "/projects/64/enumerations", :controller => 'project_enumerations', :action => 'update', :project_id => '64' should_route :put, "/projects/4223", :controller => 'projects', :action => 'update', :id => '4223' should_route :put, "/projects/1.xml", :controller => 'projects', :action => 'update', :id => '1', :format => 'xml' should_route :delete, "/projects/64", :controller => 'projects', :action => 'destroy', :id => '64' should_route :delete, "/projects/1.xml", :controller => 'projects', :action => 'destroy', :id => '1', :format => 'xml' - should_route :delete, "/projects/64/reset_activities", :controller => 'project_enumerations', :action => 'destroy', :id => '64' + should_route :delete, "/projects/64/enumerations", :controller => 'project_enumerations', :action => 'destroy', :project_id => '64' end context "repositories" do From 4c656fcffcfe43482d62065122f6029bba62090d Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Fri, 10 Sep 2010 18:46:23 +0000 Subject: [PATCH 40/72] Define Principal#name so all subclasses will have some sort of name when printed. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4076 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/models/principal.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/models/principal.rb b/app/models/principal.rb index 58c3f0497..b3e07dda5 100644 --- a/app/models/principal.rb +++ b/app/models/principal.rb @@ -33,7 +33,11 @@ class Principal < ActiveRecord::Base } before_create :set_default_empty_values - + + def name(formatter = nil) + to_s + end + def <=>(principal) if self.class.name == principal.class.name self.to_s.downcase <=> principal.to_s.downcase From 109b42f4828c0966e771809afcfd99d545af3ca0 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Fri, 10 Sep 2010 18:46:29 +0000 Subject: [PATCH 41/72] Added a "Member of Group" to the issues filter. #5869 This filter will check an issue's assigned to field for users in (or not in) specific groups. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4077 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/models/query.rb | 19 +++++++++ config/locales/en.yml | 1 + test/unit/query_test.rb | 90 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 108 insertions(+), 2 deletions(-) diff --git a/app/models/query.rb b/app/models/query.rb index b1f784528..96e99ef94 100644 --- a/app/models/query.rb +++ b/app/models/query.rb @@ -195,6 +195,9 @@ class Query < ActiveRecord::Base end @available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => user_values } unless user_values.empty? @available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values } unless user_values.empty? + + group_values = Group.all.collect {|g| [g.name, g.id] } + @available_filters["member_of_group"] = { :type => :list_optional, :order => 6, :values => group_values } unless group_values.empty? if User.current.logged? @available_filters["watcher_id"] = { :type => :list, :order => 15, :values => [["<< #{l(:label_me)} >>", "me"]] } @@ -432,6 +435,22 @@ class Query < ActiveRecord::Base db_field = 'user_id' sql << "#{Issue.table_name}.id #{ operator == '=' ? 'IN' : 'NOT IN' } (SELECT #{db_table}.watchable_id FROM #{db_table} WHERE #{db_table}.watchable_type='Issue' AND " sql << sql_for_field(field, '=', v, db_table, db_field) + ')' + elsif field == "member_of_group" # named field + if operator == '*' # Any group + groups = Group.all + members_of_groups = groups.collect(&:user_ids).flatten.compact.collect(&:to_s) + operator = '=' # Override the operator since we want to find by assigned_to + elsif operator == "!*" + groups = Group.all + members_of_groups = groups.collect(&:user_ids).flatten.compact.collect(&:to_s) + operator = '!' # Override the operator since we want to find by assigned_to + else + groups = Group.find_all_by_id(v) + members_of_groups = groups.collect(&:user_ids).flatten.compact.collect(&:to_s) + end + + sql << '(' + sql_for_field("assigned_to_id", operator, members_of_groups, Issue.table_name, "assigned_to_id", false) + ')' + else # regular field db_table = Issue.table_name diff --git a/config/locales/en.yml b/config/locales/en.yml index 555043a8f..ba7a2da23 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -293,6 +293,7 @@ en: field_group_by: Group results by field_sharing: Sharing field_parent_issue: Parent task + field_member_of_group: Member of Group setting_app_title: Application title setting_app_subtitle: Application subtitle diff --git a/test/unit/query_test.rb b/test/unit/query_test.rb index 12ab5d932..acab56ffd 100644 --- a/test/unit/query_test.rb +++ b/test/unit/query_test.rb @@ -48,6 +48,16 @@ class QueryTest < ActiveSupport::TestCase :conditions => query.statement end + def assert_find_issues_with_query_is_successful(query) + assert_nothing_raised do + find_issues_with_query(query) + end + end + + def assert_query_statement_includes(query, condition) + assert query.statement.include?(condition), "Query statement condition not found in: #{query.statement}" + end + def test_query_should_allow_shared_versions_for_a_project_query subproject_version = Version.find(4) query = Query.new(:project => Project.find(1), :name => '_') @@ -362,11 +372,87 @@ class QueryTest < ActiveSupport::TestCase end context "#available_filters" do + setup do + @query = Query.new(:name => "_") + end + should "include users of visible projects in cross-project view" do - query = Query.new(:name => "_") - users = query.available_filters["assigned_to_id"] + users = @query.available_filters["assigned_to_id"] assert_not_nil users assert users[:values].map{|u|u[1]}.include?("3") end + + context "'member_of_group' filter" do + should "be present" do + assert @query.available_filters.keys.include?("member_of_group") + end + + should "be an optional list" do + assert_equal :list_optional, @query.available_filters["member_of_group"][:type] + end + + should "have a list of the groups as values" do + Group.destroy_all # No fixtures + group1 = Group.generate!.reload + group2 = Group.generate!.reload + + expected_group_list = [ + [group1.name, group1.id], + [group2.name, group2.id] + ] + assert_equal expected_group_list, @query.available_filters["member_of_group"][:values] + end + + end + end + + context "#statement" do + context "with 'member_of_group' filter" do + setup do + Group.destroy_all # No fixtures + @user_in_group = User.generate! + @second_user_in_group = User.generate! + @user_in_group2 = User.generate! + @user_not_in_group = User.generate! + + @group = Group.generate!.reload + @group.users << @user_in_group + @group.users << @second_user_in_group + + @group2 = Group.generate!.reload + @group2.users << @user_in_group2 + + end + + should "search assigned to for users in the group" do + @query = Query.new(:name => '_') + @query.add_filter('member_of_group', '=', [@group.id.to_s]) + + assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IN ('#{@user_in_group.id}','#{@second_user_in_group.id}')" + assert_find_issues_with_query_is_successful @query + end + + should "search not assigned to any group member (none)" do + @query = Query.new(:name => '_') + @query.add_filter('member_of_group', '!*', ['']) + + # Users not in a group + assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IS NULL OR #{Issue.table_name}.assigned_to_id NOT IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@user_in_group2.id}')" + assert_find_issues_with_query_is_successful @query + + end + + should "search assigned to any group member (all)" do + @query = Query.new(:name => '_') + @query.add_filter('member_of_group', '*', ['']) + + # Only users in a group + assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@user_in_group2.id}')" + assert_find_issues_with_query_is_successful @query + + end + end + end + end From 41f8d043eb29452ff11baf5e5286cd907cda742e Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Fri, 10 Sep 2010 19:44:45 +0000 Subject: [PATCH 42/72] Added a "Member of Role" to the issues filters. #5869 This filter will check an issue's assigned to field for users who have (or don't have) a specific Role(s). git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4078 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/models/query.rb | 23 ++++++++++++ config/locales/en.yml | 1 + test/object_daddy_helpers.rb | 5 +++ test/unit/query_test.rb | 68 +++++++++++++++++++++++++++++++++++- 4 files changed, 96 insertions(+), 1 deletion(-) diff --git a/app/models/query.rb b/app/models/query.rb index 96e99ef94..4b2cc7557 100644 --- a/app/models/query.rb +++ b/app/models/query.rb @@ -198,6 +198,9 @@ class Query < ActiveRecord::Base group_values = Group.all.collect {|g| [g.name, g.id] } @available_filters["member_of_group"] = { :type => :list_optional, :order => 6, :values => group_values } unless group_values.empty? + + role_values = Role.givable.collect {|r| [r.name, r.id] } + @available_filters["assigned_to_role"] = { :type => :list_optional, :order => 7, :values => role_values } unless role_values.empty? if User.current.logged? @available_filters["watcher_id"] = { :type => :list, :order => 15, :values => [["<< #{l(:label_me)} >>", "me"]] } @@ -451,6 +454,26 @@ class Query < ActiveRecord::Base sql << '(' + sql_for_field("assigned_to_id", operator, members_of_groups, Issue.table_name, "assigned_to_id", false) + ')' + elsif field == "assigned_to_role" # named field + if operator == "*" # Any Role + roles = Role.givable + operator = '=' # Override the operator since we want to find by assigned_to + elsif operator == "!*" # No role + roles = Role.givable + operator = '!' # Override the operator since we want to find by assigned_to + else + roles = Role.givable.find_all_by_id(v) + end + roles ||= [] + + members_of_roles = roles.inject([]) {|user_ids, role| + if role && role.members + user_ids << role.members.collect(&:user_id) + end + user_ids.flatten.uniq.compact + }.sort.collect(&:to_s) + + sql << '(' + sql_for_field("assigned_to_id", operator, members_of_roles, Issue.table_name, "assigned_to_id", false) + ')' else # regular field db_table = Issue.table_name diff --git a/config/locales/en.yml b/config/locales/en.yml index ba7a2da23..cc4e31185 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -294,6 +294,7 @@ en: field_sharing: Sharing field_parent_issue: Parent task field_member_of_group: Member of Group + field_assigned_to_role: Member of Role setting_app_title: Application title setting_app_subtitle: Application subtitle diff --git a/test/object_daddy_helpers.rb b/test/object_daddy_helpers.rb index 7b144b18f..c94ada229 100644 --- a/test/object_daddy_helpers.rb +++ b/test/object_daddy_helpers.rb @@ -13,6 +13,11 @@ module ObjectDaddyHelpers User.spawn(attributes) end + def User.add_to_project(user, project, roles) + roles = [roles] unless roles.is_a?(Array) + Member.generate!(:principal => user, :project => project, :roles => roles) + end + # Generate the default Query def Query.generate_default!(attributes={}) query = Query.spawn(attributes) diff --git a/test/unit/query_test.rb b/test/unit/query_test.rb index acab56ffd..f737b2f0c 100644 --- a/test/unit/query_test.rb +++ b/test/unit/query_test.rb @@ -404,7 +404,29 @@ class QueryTest < ActiveSupport::TestCase end end - + + context "'assigned_to_role' filter" do + should "be present" do + assert @query.available_filters.keys.include?("assigned_to_role") + end + + should "be an optional list" do + assert_equal :list_optional, @query.available_filters["assigned_to_role"][:type] + end + + should "have a list of the Roles as values" do + assert @query.available_filters["assigned_to_role"][:values].include?(['Manager',1]) + assert @query.available_filters["assigned_to_role"][:values].include?(['Developer',2]) + assert @query.available_filters["assigned_to_role"][:values].include?(['Reporter',3]) + end + + should "not include the built in Roles as values" do + assert ! @query.available_filters["assigned_to_role"][:values].include?(['Non member',4]) + assert ! @query.available_filters["assigned_to_role"][:values].include?(['Anonymous',5]) + end + + end + end context "#statement" do @@ -453,6 +475,50 @@ class QueryTest < ActiveSupport::TestCase end end + + context "with 'assigned_to_role' filter" do + setup do + # No fixtures + MemberRole.delete_all + Member.delete_all + Role.delete_all + + @manager_role = Role.generate!(:name => 'Manager') + @developer_role = Role.generate!(:name => 'Developer') + + @project = Project.generate! + @manager = User.generate! + @developer = User.generate! + @boss = User.generate! + User.add_to_project(@manager, @project, @manager_role) + User.add_to_project(@developer, @project, @developer_role) + User.add_to_project(@boss, @project, [@manager_role, @developer_role]) + end + + should "search assigned to for users with the Role" do + @query = Query.new(:name => '_') + @query.add_filter('assigned_to_role', '=', [@manager_role.id.to_s]) + + assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IN ('#{@manager.id}','#{@boss.id}')" + assert_find_issues_with_query_is_successful @query + end + + should "search assigned to for users not assigned to any Role (none)" do + @query = Query.new(:name => '_') + @query.add_filter('assigned_to_role', '!*', ['']) + + assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IS NULL OR #{Issue.table_name}.assigned_to_id NOT IN ('#{@manager.id}','#{@developer.id}','#{@boss.id}')" + assert_find_issues_with_query_is_successful @query + end + + should "search assigned to for users assigned to any Role (all)" do + @query = Query.new(:name => '_') + @query.add_filter('assigned_to_role', '*', ['']) + + assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IN ('#{@manager.id}','#{@developer.id}','#{@boss.id}')" + assert_find_issues_with_query_is_successful @query + end + end end end From d36700eeee57dcd5da0078bf891b8186d30eb382 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Fri, 10 Sep 2010 19:53:57 +0000 Subject: [PATCH 43/72] Refactor: replace chained finders with an inject. Should handle edge cases better. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4079 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/models/query.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/models/query.rb b/app/models/query.rb index 4b2cc7557..59131afcd 100644 --- a/app/models/query.rb +++ b/app/models/query.rb @@ -441,16 +441,21 @@ class Query < ActiveRecord::Base elsif field == "member_of_group" # named field if operator == '*' # Any group groups = Group.all - members_of_groups = groups.collect(&:user_ids).flatten.compact.collect(&:to_s) operator = '=' # Override the operator since we want to find by assigned_to elsif operator == "!*" groups = Group.all - members_of_groups = groups.collect(&:user_ids).flatten.compact.collect(&:to_s) operator = '!' # Override the operator since we want to find by assigned_to else groups = Group.find_all_by_id(v) - members_of_groups = groups.collect(&:user_ids).flatten.compact.collect(&:to_s) end + groups ||= [] + + members_of_groups = groups.inject([]) {|user_ids, group| + if group && group.user_ids.present? + user_ids << group.user_ids + end + user_ids.flatten.uniq.compact + }.sort.collect(&:to_s) sql << '(' + sql_for_field("assigned_to_id", operator, members_of_groups, Issue.table_name, "assigned_to_id", false) + ')' From 878bb555227628d983997d96320a99ebb53adc7b Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Fri, 10 Sep 2010 23:07:10 +0000 Subject: [PATCH 44/72] Refactor: move method to Project#css_classes git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4080 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/helpers/admin_helper.rb | 8 -------- app/models/project.rb | 8 ++++++++ app/views/admin/projects.rhtml | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/helpers/admin_helper.rb b/app/helpers/admin_helper.rb index b49a5674c..8f81f66ba 100644 --- a/app/helpers/admin_helper.rb +++ b/app/helpers/admin_helper.rb @@ -20,12 +20,4 @@ module AdminHelper options_for_select([[l(:label_all), ''], [l(:status_active), 1]], selected) end - - def css_project_classes(project) - s = 'project' - s << ' root' if project.root? - s << ' child' if project.child? - s << (project.leaf? ? ' leaf' : ' parent') - s - end end diff --git a/app/models/project.rb b/app/models/project.rb index 5ef7915de..40898a34e 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -413,6 +413,14 @@ class Project < ActiveRecord::Base description.gsub(/^(.{#{length}}[^\n\r]*).*$/m, '\1...').strip if description end + def css_classes + s = 'project' + s << ' root' if root? + s << ' child' if child? + s << (leaf? ? ' leaf' : ' parent') + s + end + # The earliest start date of a project, based on it's issues and versions def start_date if module_enabled?(:issue_tracking) diff --git a/app/views/admin/projects.rhtml b/app/views/admin/projects.rhtml index 6cf933d11..47a2d0583 100644 --- a/app/views/admin/projects.rhtml +++ b/app/views/admin/projects.rhtml @@ -26,7 +26,7 @@
    <%= link_to_project(project, :action => 'settings') %> <%= textilizable project.short_description, :project => project %> <%= checked_image project.is_public? %> <% if version.project == @project %> <%= link_to_if_authorized l(:button_edit), {:controller => 'versions', :action => 'edit', :id => version }, :class => 'icon icon-edit' %> - <%= link_to_if_authorized l(:button_delete), {:controller => 'versions', :action => 'destroy', :id => version}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %> + <%= link_to_if_authorized l(:button_delete), {:controller => 'versions', :action => 'destroy', :id => version}, :confirm => l(:text_are_you_sure), :method => :delete, :class => 'icon icon-del' %> <% end %>