##// END OF EJS Templates
Merged rails-4.1 branch (#14534)....
Jean-Philippe Lang -
r13100:2d1866d966d9
parent child
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,3
1 #! /bin/sh
2
3 JRUBY_OPTS=-J-Xmx1024m bundle exec rake test:${TEST_SUITE}
@@ -0,0 +1,6
1 #!/usr/bin/env ruby
2
3 ENV["RAILS_ENV"] ||= "production"
4 require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
5 puts
6 puts Redmine::Info.environment
@@ -0,0 +1,3
1 #!/usr/bin/env ruby.exe
2 ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3 load Gem.bin_path('bundler', 'bundle')
@@ -0,0 +1,4
1 #!/usr/bin/env ruby.exe
2 APP_PATH = File.expand_path('../../config/application', __FILE__)
3 require_relative '../config/boot'
4 require 'rails/commands'
@@ -0,0 +1,4
1 #!/usr/bin/env ruby.exe
2 require_relative '../config/boot'
3 require 'rake'
4 Rake.application.run
@@ -3,8 +3,6
3 # You can also run tests on your environment.
3 # You can also run tests on your environment.
4 language: ruby
4 language: ruby
5 rvm:
5 rvm:
6 - 1.8.7
7 - 1.9.2
8 - 1.9.3
6 - 1.9.3
9 - 2.0
7 - 2.0
10 - 2.1
8 - 2.1
@@ -34,6 +32,7 script:
34 - "bundle install"
32 - "bundle install"
35 - "RUN_ON_NOT_OFFICIAL='' RUBY_VER=1.9 BRANCH=trunk bundle exec rake config/database.yml"
33 - "RUN_ON_NOT_OFFICIAL='' RUBY_VER=1.9 BRANCH=trunk bundle exec rake config/database.yml"
36 - "bundle install"
34 - "bundle install"
37 - "JRUBY_OPTS=-J-Xmx1024m bundle exec rake ci"
35 - "bundle exec rake ci:setup"
36 - "sh .travis.run-test.sh"
38 notifications:
37 notifications:
39 email: false
38 email: false
@@ -1,12 +1,17
1 source 'https://rubygems.org'
1 source 'https://rubygems.org'
2
2
3 gem "rails", "3.2.19"
3 gem "rails", "4.1.6"
4 gem "jquery-rails", "~> 3.1.1"
4 gem "jquery-rails", "~> 3.1.1"
5 gem "coderay", "~> 1.1.0"
5 gem "coderay", "~> 1.1.0"
6 gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby]
7 gem "builder", ">= 3.0.4"
6 gem "builder", ">= 3.0.4"
8 gem "request_store", "1.0.5"
7 gem "request_store", "1.0.5"
9 gem "mime-types"
8 gem "mime-types"
9 gem "awesome_nested_set", "3.0.0"
10 gem "protected_attributes"
11 gem "actionpack-action_caching"
12
13 # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
14 gem 'tzinfo-data', platforms: [:mingw, :mswin, :jruby]
10 gem "rbpdf", "~> 1.18.1"
15 gem "rbpdf", "~> 1.18.1"
11
16
12 # Optional gem for LDAP authentication
17 # Optional gem for LDAP authentication
@@ -23,16 +28,12 end
23 platforms :mri, :mingw do
28 platforms :mri, :mingw do
24 # Optional gem for exporting the gantt to a PNG file, not supported with jruby
29 # Optional gem for exporting the gantt to a PNG file, not supported with jruby
25 group :rmagick do
30 group :rmagick do
26 # RMagick 2 supports ruby 1.9
27 # RMagick 1 would be fine for ruby 1.8 but Bundler does not support
28 # different requirements for the same gem on different platforms
29 gem "rmagick", ">= 2.0.0"
31 gem "rmagick", ">= 2.0.0"
30 end
32 end
31
33
32 # Optional Markdown support, not for JRuby
34 # Optional Markdown support, not for JRuby
33 group :markdown do
35 group :markdown do
34 # TODO: upgrade to redcarpet 3.x when ruby1.8 support is dropped
36 gem "redcarpet", "~> 3.1.2"
35 gem "redcarpet", "~> 2.3.0"
36 end
37 end
37 end
38 end
38
39
@@ -57,7 +58,6 if File.exist?(database_file)
57 gem "mysql2", "~> 0.3.11", :platforms => [:mri, :mingw]
58 gem "mysql2", "~> 0.3.11", :platforms => [:mri, :mingw]
58 gem "activerecord-jdbcmysql-adapter", :platforms => :jruby
59 gem "activerecord-jdbcmysql-adapter", :platforms => :jruby
59 when 'mysql'
60 when 'mysql'
60 gem "mysql", "~> 2.8.1", :platforms => [:mri, :mingw]
61 gem "activerecord-jdbcmysql-adapter", :platforms => :jruby
61 gem "activerecord-jdbcmysql-adapter", :platforms => :jruby
62 when /postgresql/
62 when /postgresql/
63 gem "pg", ">= 0.11.0", :platforms => [:mri, :mingw]
63 gem "pg", ">= 0.11.0", :platforms => [:mri, :mingw]
@@ -85,24 +85,20 group :development do
85 end
85 end
86
86
87 group :test do
87 group :test do
88 gem "shoulda", "~> 3.3.2"
88 gem "minitest"
89 gem "shoulda-matchers", "1.4.1"
89 gem "shoulda-context"
90 gem "mocha", "~> 1.0.0", :require => 'mocha/api'
90 gem "mocha", "~> 1.0.0", :require => 'mocha/api'
91 if RUBY_VERSION >= '1.9.3'
91 # For running UI tests
92 gem "capybara", "~> 2.1.0"
92 gem "capybara", "~> 2.1.0"
93 gem "selenium-webdriver"
93 gem "selenium-webdriver"
94 end
95 end
94 end
96
95
97 local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
96 local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
98 if File.exists?(local_gemfile)
97 if File.exists?(local_gemfile)
99 puts "Loading Gemfile.local ..." if $DEBUG # `ruby -d` or `bundle -v`
98 eval_gemfile local_gemfile
100 instance_eval File.read(local_gemfile)
101 end
99 end
102
100
103 # Load plugins' Gemfiles
101 # Load plugins' Gemfiles
104 Dir.glob File.expand_path("../plugins/*/{Gemfile,PluginGemfile}", __FILE__) do |file|
102 Dir.glob File.expand_path("../plugins/*/{Gemfile,PluginGemfile}", __FILE__) do |file|
105 puts "Loading #{file} ..." if $DEBUG # `ruby -d` or `bundle -v`
103 eval_gemfile file
106 #TODO: switch to "eval_gemfile file" when bundler >= 1.2.0 will be required (rails 4)
107 instance_eval File.read(file), file
108 end
104 end
@@ -34,7 +34,7 class AdminController < ApplicationController
34
34
35 scope = Project.status(@status).order('lft')
35 scope = Project.status(@status).order('lft')
36 scope = scope.like(params[:name]) if params[:name].present?
36 scope = scope.like(params[:name]) if params[:name].present?
37 @projects = scope.all
37 @projects = scope.to_a
38
38
39 render :action => "projects", :layout => false if request.xhr?
39 render :action => "projects", :layout => false if request.xhr?
40 end
40 end
@@ -496,7 +496,7 class ApplicationController < ActionController::Base
496 end
496 end
497
497
498 def render_feed(items, options={})
498 def render_feed(items, options={})
499 @items = items || []
499 @items = (items || []).to_a
500 @items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
500 @items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
501 @items = @items.slice(0, Setting.feeds_limit.to_i)
501 @items = @items.slice(0, Setting.feeds_limit.to_i)
502 @title = options[:title] || Setting.app_title
502 @title = options[:title] || Setting.app_title
@@ -26,7 +26,7 class AutoCompletesController < ApplicationController
26 if q.match(/\A#?(\d+)\z/)
26 if q.match(/\A#?(\d+)\z/)
27 @issues << scope.find_by_id($1.to_i)
27 @issues << scope.find_by_id($1.to_i)
28 end
28 end
29 @issues += scope.where("LOWER(#{Issue.table_name}.subject) LIKE LOWER(?)", "%#{q}%").order("#{Issue.table_name}.id DESC").limit(10).all
29 @issues += scope.where("LOWER(#{Issue.table_name}.subject) LIKE LOWER(?)", "%#{q}%").order("#{Issue.table_name}.id DESC").limit(10).to_a
30 @issues.compact!
30 @issues.compact!
31 end
31 end
32 render :layout => false
32 render :layout => false
@@ -25,7 +25,7 class BoardsController < ApplicationController
25 helper :watchers
25 helper :watchers
26
26
27 def index
27 def index
28 @boards = @project.boards.includes(:project, :last_message => :author).all
28 @boards = @project.boards.preload(:project, :last_message => :author).to_a
29 # show the board if there is only one
29 # show the board if there is only one
30 if @boards.size == 1
30 if @boards.size == 1
31 @board = @boards.first
31 @board = @boards.first
@@ -45,12 +45,13 class BoardsController < ApplicationController
45 @topic_pages = Paginator.new @topic_count, per_page_option, params['page']
45 @topic_pages = Paginator.new @topic_count, per_page_option, params['page']
46 @topics = @board.topics.
46 @topics = @board.topics.
47 reorder("#{Message.table_name}.sticky DESC").
47 reorder("#{Message.table_name}.sticky DESC").
48 includes(:last_reply).
48 joins("LEFT OUTER JOIN #{Message.table_name} last_replies_messages ON last_replies_messages.id = #{Message.table_name}.last_reply_id").
49 references(:last_reply).
49 limit(@topic_pages.per_page).
50 limit(@topic_pages.per_page).
50 offset(@topic_pages.offset).
51 offset(@topic_pages.offset).
51 order(sort_clause).
52 order(sort_clause).
52 preload(:author, {:last_reply => :author}).
53 preload(:author, {:last_reply => :author}).
53 all
54 to_a
54 @message = Message.new(:board => @board)
55 @message = Message.new(:board => @board)
55 render :action => 'show', :layout => !request.xhr?
56 render :action => 'show', :layout => !request.xhr?
56 }
57 }
@@ -59,7 +60,7 class BoardsController < ApplicationController
59 reorder('created_on DESC').
60 reorder('created_on DESC').
60 includes(:author, :board).
61 includes(:author, :board).
61 limit(Setting.feeds_limit.to_i).
62 limit(Setting.feeds_limit.to_i).
62 all
63 to_a
63 render_feed(@messages, :title => "#{@project}: #{@board}")
64 render_feed(@messages, :title => "#{@project}: #{@board}")
64 }
65 }
65 end
66 end
@@ -27,7 +27,7 class DocumentsController < ApplicationController
27
27
28 def index
28 def index
29 @sort_by = %w(category date title author).include?(params[:sort_by]) ? params[:sort_by] : 'category'
29 @sort_by = %w(category date title author).include?(params[:sort_by]) ? params[:sort_by] : 'category'
30 documents = @project.documents.includes(:attachments, :category).all
30 documents = @project.documents.includes(:attachments, :category).to_a
31 case @sort_by
31 case @sort_by
32 when 'date'
32 when 'date'
33 @grouped = documents.group_by {|d| d.updated_on.to_date }
33 @grouped = documents.group_by {|d| d.updated_on.to_date }
@@ -43,7 +43,7 class DocumentsController < ApplicationController
43 end
43 end
44
44
45 def show
45 def show
46 @attachments = @document.attachments.all
46 @attachments = @document.attachments.to_a
47 end
47 end
48
48
49 def new
49 def new
@@ -69,7 +69,7 class DocumentsController < ApplicationController
69
69
70 def update
70 def update
71 @document.safe_attributes = params[:document]
71 @document.safe_attributes = params[:document]
72 if request.put? and @document.save
72 if @document.save
73 flash[:notice] = l(:notice_successful_update)
73 flash[:notice] = l(:notice_successful_update)
74 redirect_to document_path(@document)
74 redirect_to document_path(@document)
75 else
75 else
@@ -32,7 +32,7 class EnumerationsController < ApplicationController
32 format.api {
32 format.api {
33 @klass = Enumeration.get_subclass(params[:type])
33 @klass = Enumeration.get_subclass(params[:type])
34 if @klass
34 if @klass
35 @enumerations = @klass.shared.sorted.all
35 @enumerations = @klass.shared.sorted.to_a
36 else
36 else
37 render_404
37 render_404
38 end
38 end
@@ -56,7 +56,7 class EnumerationsController < ApplicationController
56 end
56 end
57
57
58 def update
58 def update
59 if request.put? && @enumeration.update_attributes(params[:enumeration])
59 if @enumeration.update_attributes(params[:enumeration])
60 flash[:notice] = l(:notice_successful_update)
60 flash[:notice] = l(:notice_successful_update)
61 redirect_to enumerations_path
61 redirect_to enumerations_path
62 else
62 else
@@ -75,7 +75,7 class EnumerationsController < ApplicationController
75 redirect_to enumerations_path
75 redirect_to enumerations_path
76 return
76 return
77 end
77 end
78 @enumerations = @enumeration.class.system.all - [@enumeration]
78 @enumerations = @enumeration.class.system.to_a - [@enumeration]
79 end
79 end
80
80
81 private
81 private
@@ -31,8 +31,10 class FilesController < ApplicationController
31 'size' => "#{Attachment.table_name}.filesize",
31 'size' => "#{Attachment.table_name}.filesize",
32 'downloads' => "#{Attachment.table_name}.downloads"
32 'downloads' => "#{Attachment.table_name}.downloads"
33
33
34 @containers = [ Project.includes(:attachments).reorder(sort_clause).find(@project.id)]
34 @containers = [Project.includes(:attachments).
35 @containers += @project.versions.includes(:attachments).reorder(sort_clause).all.sort.reverse
35 references(:attachments).reorder(sort_clause).find(@project.id)]
36 @containers += @project.versions.includes(:attachments).
37 references(:attachments).reorder(sort_clause).to_a.sort.reverse
36 render :layout => !request.xhr?
38 render :layout => !request.xhr?
37 end
39 end
38
40
@@ -27,13 +27,13 class GroupsController < ApplicationController
27 def index
27 def index
28 respond_to do |format|
28 respond_to do |format|
29 format.html {
29 format.html {
30 @groups = Group.sorted.all
30 @groups = Group.sorted.to_a
31 @user_count_by_group_id = user_count_by_group_id
31 @user_count_by_group_id = user_count_by_group_id
32 }
32 }
33 format.api {
33 format.api {
34 scope = Group.sorted
34 scope = Group.sorted
35 scope = scope.givable unless params[:builtin] == '1'
35 scope = scope.givable unless params[:builtin] == '1'
36 @groups = scope.all
36 @groups = scope.to_a
37 }
37 }
38 end
38 end
39 end
39 end
@@ -95,7 +95,7 class GroupsController < ApplicationController
95 end
95 end
96
96
97 def add_users
97 def add_users
98 @users = User.where(:id => (params[:user_id] || params[:user_ids])).all
98 @users = User.where(:id => (params[:user_id] || params[:user_ids])).to_a
99 @group.users << @users if request.post?
99 @group.users << @users if request.post?
100 respond_to do |format|
100 respond_to do |format|
101 format.html { redirect_to edit_group_path(@group, :tab => 'users') }
101 format.html { redirect_to edit_group_path(@group, :tab => 'users') }
@@ -27,7 +27,7 class IssueCategoriesController < ApplicationController
27 def index
27 def index
28 respond_to do |format|
28 respond_to do |format|
29 format.html { redirect_to_settings_in_projects }
29 format.html { redirect_to_settings_in_projects }
30 format.api { @categories = @project.issue_categories.all }
30 format.api { @categories = @project.issue_categories.to_a }
31 end
31 end
32 end
32 end
33
33
@@ -29,7 +29,7 class IssueStatusesController < ApplicationController
29 render :action => "index", :layout => false if request.xhr?
29 render :action => "index", :layout => false if request.xhr?
30 }
30 }
31 format.api {
31 format.api {
32 @issue_statuses = IssueStatus.order('position').all
32 @issue_statuses = IssueStatus.order('position').to_a
33 }
33 }
34 end
34 end
35 end
35 end
@@ -104,15 +104,16 class IssuesController < ApplicationController
104 end
104 end
105
105
106 def show
106 def show
107 @journals = @issue.journals.includes(:user, :details).reorder("#{Journal.table_name}.id ASC").all
107 @journals = @issue.journals.includes(:user, :details).
108 references(:user, :details).
109 reorder("#{Journal.table_name}.id ASC").to_a
108 @journals.each_with_index {|j,i| j.indice = i+1}
110 @journals.each_with_index {|j,i| j.indice = i+1}
109 @journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
111 @journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
110 Journal.preload_journals_details_custom_fields(@journals)
112 Journal.preload_journals_details_custom_fields(@journals)
111 # TODO: use #select! when ruby1.8 support is dropped
113 @journals.select! {|journal| journal.notes? || journal.visible_details.any?}
112 @journals.reject! {|journal| !journal.notes? && journal.visible_details.empty?}
113 @journals.reverse! if User.current.wants_comments_in_reverse_order?
114 @journals.reverse! if User.current.wants_comments_in_reverse_order?
114
115
115 @changesets = @issue.changesets.visible.all
116 @changesets = @issue.changesets.visible.to_a
116 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
117 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
117
118
118 @relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
119 @relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
@@ -189,7 +190,7 class IssuesController < ApplicationController
189 rescue ActiveRecord::StaleObjectError
190 rescue ActiveRecord::StaleObjectError
190 @conflict = true
191 @conflict = true
191 if params[:last_journal_id]
192 if params[:last_journal_id]
192 @conflict_journals = @issue.journals_after(params[:last_journal_id]).all
193 @conflict_journals = @issue.journals_after(params[:last_journal_id]).to_a
193 @conflict_journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
194 @conflict_journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
194 end
195 end
195 end
196 end
@@ -301,7 +302,7 class IssuesController < ApplicationController
301 else
302 else
302 @saved_issues = @issues
303 @saved_issues = @issues
303 @unsaved_issues = unsaved_issues
304 @unsaved_issues = unsaved_issues
304 @issues = Issue.visible.where(:id => @unsaved_issues.map(&:id)).all
305 @issues = Issue.visible.where(:id => @unsaved_issues.map(&:id)).to_a
305 bulk_edit
306 bulk_edit
306 render :action => 'bulk_edit'
307 render :action => 'bulk_edit'
307 end
308 end
@@ -375,7 +376,9 class IssuesController < ApplicationController
375 def update_issue_from_params
376 def update_issue_from_params
376 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
377 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
377 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
378 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
378 @time_entry.attributes = params[:time_entry]
379 if params[:time_entry]
380 @time_entry.attributes = params[:time_entry]
381 end
379
382
380 @issue.init_journal(User.current)
383 @issue.init_journal(User.current)
381
384
@@ -422,7 +425,9 class IssuesController < ApplicationController
422 @issue.project = @project
425 @issue.project = @project
423 @issue.author ||= User.current
426 @issue.author ||= User.current
424 # Tracker must be set before custom field values
427 # Tracker must be set before custom field values
425 @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
428 tracker_id = (params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id]
429 tracker = tracker_id.present? ? @project.trackers.find(tracker_id) : @project.trackers.first
430 @issue.tracker ||= tracker
426 if @issue.tracker.nil?
431 if @issue.tracker.nil?
427 render_error l(:error_no_tracker_in_project)
432 render_error l(:error_no_tracker_in_project)
428 return false
433 return false
@@ -34,7 +34,6 class JournalsController < ApplicationController
34 retrieve_query
34 retrieve_query
35 sort_init 'id', 'desc'
35 sort_init 'id', 'desc'
36 sort_update(@query.sortable_columns)
36 sort_update(@query.sortable_columns)
37
38 if @query.valid?
37 if @query.valid?
39 @journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC",
38 @journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC",
40 :limit => 25)
39 :limit => 25)
@@ -32,7 +32,7 class MembersController < ApplicationController
32 order("#{Member.table_name}.id").
32 order("#{Member.table_name}.id").
33 limit(@limit).
33 limit(@limit).
34 offset(@offset).
34 offset(@offset).
35 all
35 to_a
36 respond_to do |format|
36 respond_to do |format|
37 format.html { head 406 }
37 format.html { head 406 }
38 format.api
38 format.api
@@ -63,7 +63,10 class MembersController < ApplicationController
63
63
64 respond_to do |format|
64 respond_to do |format|
65 format.html { redirect_to_settings_in_projects }
65 format.html { redirect_to_settings_in_projects }
66 format.js { @members = members }
66 format.js {
67 @members = members
68 @member = Member.new
69 }
67 format.api {
70 format.api {
68 @member = members.first
71 @member = members.first
69 if @member.valid?
72 if @member.valid?
@@ -43,10 +43,10 class MessagesController < ApplicationController
43 @reply_pages = Paginator.new @reply_count, REPLIES_PER_PAGE, page
43 @reply_pages = Paginator.new @reply_count, REPLIES_PER_PAGE, page
44 @replies = @topic.children.
44 @replies = @topic.children.
45 includes(:author, :attachments, {:board => :project}).
45 includes(:author, :attachments, {:board => :project}).
46 reorder("#{Message.table_name}.created_on ASC").
46 reorder("#{Message.table_name}.created_on ASC, #{Message.table_name}.id ASC").
47 limit(@reply_pages.per_page).
47 limit(@reply_pages.per_page).
48 offset(@reply_pages.offset).
48 offset(@reply_pages.offset).
49 all
49 to_a
50
50
51 @reply = Message.new(:subject => "RE: #{@message.subject}")
51 @reply = Message.new(:subject => "RE: #{@message.subject}")
52 render :action => "show", :layout => false if request.xhr?
52 render :action => "show", :layout => false if request.xhr?
@@ -53,8 +53,8 class MyController < ApplicationController
53 @user = User.current
53 @user = User.current
54 @pref = @user.pref
54 @pref = @user.pref
55 if request.post?
55 if request.post?
56 @user.safe_attributes = params[:user]
56 @user.safe_attributes = params[:user] if params[:user]
57 @user.pref.attributes = params[:pref]
57 @user.pref.attributes = params[:pref] if params[:pref]
58 if @user.save
58 if @user.save
59 @user.pref.save
59 @user.pref.save
60 set_language_if_valid @user.language
60 set_language_if_valid @user.language
@@ -46,7 +46,7 class NewsController < ApplicationController
46 order("#{News.table_name}.created_on DESC").
46 order("#{News.table_name}.created_on DESC").
47 limit(@limit).
47 limit(@limit).
48 offset(@offset).
48 offset(@offset).
49 all
49 to_a
50 respond_to do |format|
50 respond_to do |format|
51 format.html {
51 format.html {
52 @news = News.new # for adding news inline
52 @news = News.new # for adding news inline
@@ -20,7 +20,7 class ProjectEnumerationsController < ApplicationController
20 before_filter :authorize
20 before_filter :authorize
21
21
22 def update
22 def update
23 if request.put? && params[:enumerations]
23 if params[:enumerations]
24 Project.transaction do
24 Project.transaction do
25 params[:enumerations].each do |id, activity|
25 params[:enumerations].each do |id, activity|
26 @project.update_or_create_time_entry_activity(id, activity)
26 @project.update_or_create_time_entry_activity(id, activity)
@@ -53,30 +53,30 class ProjectsController < ApplicationController
53 unless params[:closed]
53 unless params[:closed]
54 scope = scope.active
54 scope = scope.active
55 end
55 end
56 @projects = scope.visible.order('lft').all
56 @projects = scope.visible.order('lft').to_a
57 }
57 }
58 format.api {
58 format.api {
59 @offset, @limit = api_offset_and_limit
59 @offset, @limit = api_offset_and_limit
60 @project_count = Project.visible.count
60 @project_count = Project.visible.count
61 @projects = Project.visible.offset(@offset).limit(@limit).order('lft').all
61 @projects = Project.visible.offset(@offset).limit(@limit).order('lft').to_a
62 }
62 }
63 format.atom {
63 format.atom {
64 projects = Project.visible.order('created_on DESC').limit(Setting.feeds_limit.to_i).all
64 projects = Project.visible.order('created_on DESC').limit(Setting.feeds_limit.to_i).to_a
65 render_feed(projects, :title => "#{Setting.app_title}: #{l(:label_project_latest)}")
65 render_feed(projects, :title => "#{Setting.app_title}: #{l(:label_project_latest)}")
66 }
66 }
67 end
67 end
68 end
68 end
69
69
70 def new
70 def new
71 @issue_custom_fields = IssueCustomField.sorted.all
71 @issue_custom_fields = IssueCustomField.sorted.to_a
72 @trackers = Tracker.sorted.all
72 @trackers = Tracker.sorted.to_a
73 @project = Project.new
73 @project = Project.new
74 @project.safe_attributes = params[:project]
74 @project.safe_attributes = params[:project]
75 end
75 end
76
76
77 def create
77 def create
78 @issue_custom_fields = IssueCustomField.sorted.all
78 @issue_custom_fields = IssueCustomField.sorted.to_a
79 @trackers = Tracker.sorted.all
79 @trackers = Tracker.sorted.to_a
80 @project = Project.new
80 @project = Project.new
81 @project.safe_attributes = params[:project]
81 @project.safe_attributes = params[:project]
82
82
@@ -109,8 +109,8 class ProjectsController < ApplicationController
109 end
109 end
110
110
111 def copy
111 def copy
112 @issue_custom_fields = IssueCustomField.sorted.all
112 @issue_custom_fields = IssueCustomField.sorted.to_a
113 @trackers = Tracker.sorted.all
113 @trackers = Tracker.sorted.to_a
114 @source_project = Project.find(params[:id])
114 @source_project = Project.find(params[:id])
115 if request.get?
115 if request.get?
116 @project = Project.copy_from(@source_project)
116 @project = Project.copy_from(@source_project)
@@ -145,8 +145,8 class ProjectsController < ApplicationController
145 end
145 end
146
146
147 @users_by_role = @project.users_by_role
147 @users_by_role = @project.users_by_role
148 @subprojects = @project.children.visible.all
148 @subprojects = @project.children.visible.to_a
149 @news = @project.news.limit(5).includes(:author, :project).reorder("#{News.table_name}.created_on DESC").all
149 @news = @project.news.limit(5).includes(:author, :project).reorder("#{News.table_name}.created_on DESC").to_a
150 @trackers = @project.rolled_up_trackers
150 @trackers = @project.rolled_up_trackers
151
151
152 cond = @project.project_condition(Setting.display_subprojects_issues?)
152 cond = @project.project_condition(Setting.display_subprojects_issues?)
@@ -167,10 +167,10 class ProjectsController < ApplicationController
167 end
167 end
168
168
169 def settings
169 def settings
170 @issue_custom_fields = IssueCustomField.sorted.all
170 @issue_custom_fields = IssueCustomField.sorted.to_a
171 @issue_category ||= IssueCategory.new
171 @issue_category ||= IssueCategory.new
172 @member ||= @project.members.new
172 @member ||= @project.members.new
173 @trackers = Tracker.sorted.all
173 @trackers = Tracker.sorted.to_a
174 @wiki ||= @project.wiki
174 @wiki ||= @project.wiki
175 end
175 end
176
176
@@ -37,8 +37,9 class QueriesController < ApplicationController
37 order("#{Query.table_name}.name").
37 order("#{Query.table_name}.name").
38 limit(@limit).
38 limit(@limit).
39 offset(@offset).
39 offset(@offset).
40 all
40 to_a
41 respond_to do |format|
41 respond_to do |format|
42 format.html {render_error :status => 406}
42 format.api
43 format.api
43 end
44 end
44 end
45 end
@@ -90,6 +90,6 class ReportsController < ApplicationController
90 private
90 private
91
91
92 def find_issue_statuses
92 def find_issue_statuses
93 @statuses = IssueStatus.sorted.all
93 @statuses = IssueStatus.sorted.to_a
94 end
94 end
95 end
95 end
@@ -69,7 +69,7 class RepositoriesController < ApplicationController
69 @repository.merge_extra_info(attrs[:attrs_extra])
69 @repository.merge_extra_info(attrs[:attrs_extra])
70 end
70 end
71 @repository.project = @project
71 @repository.project = @project
72 if request.put? && @repository.save
72 if @repository.save
73 redirect_to settings_project_path(@project, :tab => 'repositories')
73 redirect_to settings_project_path(@project, :tab => 'repositories')
74 else
74 else
75 render :action => 'edit'
75 render :action => 'edit'
@@ -94,7 +94,7 class RepositoriesController < ApplicationController
94 @committers = @repository.committers
94 @committers = @repository.committers
95 @users = @project.users
95 @users = @project.users
96 additional_user_ids = @committers.collect(&:last).collect(&:to_i) - @users.collect(&:id)
96 additional_user_ids = @committers.collect(&:last).collect(&:to_i) - @users.collect(&:id)
97 @users += User.where(:id => additional_user_ids).all unless additional_user_ids.empty?
97 @users += User.where(:id => additional_user_ids).to_a unless additional_user_ids.empty?
98 @users.compact!
98 @users.compact!
99 @users.sort!
99 @users.sort!
100 if request.post? && params[:committers].is_a?(Hash)
100 if request.post? && params[:committers].is_a?(Hash)
@@ -145,7 +145,7 class RepositoriesController < ApplicationController
145 limit(@changeset_pages.per_page).
145 limit(@changeset_pages.per_page).
146 offset(@changeset_pages.offset).
146 offset(@changeset_pages.offset).
147 includes(:user, :repository, :parents).
147 includes(:user, :repository, :parents).
148 all
148 to_a
149
149
150 respond_to do |format|
150 respond_to do |format|
151 format.html { render :layout => false if request.xhr? }
151 format.html { render :layout => false if request.xhr? }
@@ -30,7 +30,7 class RolesController < ApplicationController
30 render :action => "index", :layout => false if request.xhr?
30 render :action => "index", :layout => false if request.xhr?
31 }
31 }
32 format.api {
32 format.api {
33 @roles = Role.givable.all
33 @roles = Role.givable.to_a
34 }
34 }
35 end
35 end
36 end
36 end
@@ -47,7 +47,7 class RolesController < ApplicationController
47 if params[:copy].present? && @copy_from = Role.find_by_id(params[:copy])
47 if params[:copy].present? && @copy_from = Role.find_by_id(params[:copy])
48 @role.copy_from(@copy_from)
48 @role.copy_from(@copy_from)
49 end
49 end
50 @roles = Role.sorted.all
50 @roles = Role.sorted.to_a
51 end
51 end
52
52
53 def create
53 def create
@@ -60,7 +60,7 class RolesController < ApplicationController
60 flash[:notice] = l(:notice_successful_create)
60 flash[:notice] = l(:notice_successful_create)
61 redirect_to roles_path
61 redirect_to roles_path
62 else
62 else
63 @roles = Role.sorted.all
63 @roles = Role.sorted.to_a
64 render :action => 'new'
64 render :action => 'new'
65 end
65 end
66 end
66 end
@@ -69,7 +69,7 class RolesController < ApplicationController
69 end
69 end
70
70
71 def update
71 def update
72 if request.put? and @role.update_attributes(params[:role])
72 if @role.update_attributes(params[:role])
73 flash[:notice] = l(:notice_successful_update)
73 flash[:notice] = l(:notice_successful_update)
74 redirect_to roles_path
74 redirect_to roles_path
75 else
75 else
@@ -86,7 +86,7 class RolesController < ApplicationController
86 end
86 end
87
87
88 def permissions
88 def permissions
89 @roles = Role.sorted.all
89 @roles = Role.sorted.to_a
90 @permissions = Redmine::AccessControl.permissions.select { |p| !p.public? }
90 @permissions = Redmine::AccessControl.permissions.select { |p| !p.public? }
91 if request.post?
91 if request.post?
92 @roles.each do |role|
92 @roles.each do |role|
@@ -31,7 +31,7 class SearchController < ApplicationController
31 when 'my_projects'
31 when 'my_projects'
32 User.current.memberships.collect(&:project)
32 User.current.memberships.collect(&:project)
33 when 'subprojects'
33 when 'subprojects'
34 @project ? (@project.self_and_descendants.active.all) : nil
34 @project ? (@project.self_and_descendants.active.to_a) : nil
35 else
35 else
36 @project
36 @project
37 end
37 end
@@ -19,7 +19,8 class SysController < ActionController::Base
19 before_filter :check_enabled
19 before_filter :check_enabled
20
20
21 def projects
21 def projects
22 p = Project.active.has_module(:repository).order("#{Project.table_name}.identifier").preload(:repository).all
22 p = Project.active.has_module(:repository).
23 order("#{Project.table_name}.identifier").preload(:repository).to_a
23 # extra_info attribute from repository breaks activeresource client
24 # extra_info attribute from repository breaks activeresource client
24 render :xml => p.to_xml(
25 render :xml => p.to_xml(
25 :only => [:id, :identifier, :name, :is_public, :status],
26 :only => [:id, :identifier, :name, :is_public, :status],
@@ -56,7 +57,7 class SysController < ActionController::Base
56 raise ActiveRecord::RecordNotFound unless project
57 raise ActiveRecord::RecordNotFound unless project
57 projects << project
58 projects << project
58 else
59 else
59 projects = scope.all
60 projects = scope.to_a
60 end
61 end
61 projects.each do |project|
62 projects.each do |project|
62 project.repositories.each do |repository|
63 project.repositories.each do |repository|
@@ -52,7 +52,7 class TimelogController < ApplicationController
52 format.html {
52 format.html {
53 @entry_count = scope.count
53 @entry_count = scope.count
54 @entry_pages = Paginator.new @entry_count, per_page_option, params['page']
54 @entry_pages = Paginator.new @entry_count, per_page_option, params['page']
55 @entries = scope.offset(@entry_pages.offset).limit(@entry_pages.per_page).all
55 @entries = scope.offset(@entry_pages.offset).limit(@entry_pages.per_page).to_a
56 @total_hours = scope.sum(:hours).to_f
56 @total_hours = scope.sum(:hours).to_f
57
57
58 render :layout => !request.xhr?
58 render :layout => !request.xhr?
@@ -60,15 +60,15 class TimelogController < ApplicationController
60 format.api {
60 format.api {
61 @entry_count = scope.count
61 @entry_count = scope.count
62 @offset, @limit = api_offset_and_limit
62 @offset, @limit = api_offset_and_limit
63 @entries = scope.offset(@offset).limit(@limit).preload(:custom_values => :custom_field).all
63 @entries = scope.offset(@offset).limit(@limit).preload(:custom_values => :custom_field).to_a
64 }
64 }
65 format.atom {
65 format.atom {
66 entries = scope.limit(Setting.feeds_limit.to_i).reorder("#{TimeEntry.table_name}.created_on DESC").all
66 entries = scope.limit(Setting.feeds_limit.to_i).reorder("#{TimeEntry.table_name}.created_on DESC").to_a
67 render_feed(entries, :title => l(:label_spent_time))
67 render_feed(entries, :title => l(:label_spent_time))
68 }
68 }
69 format.csv {
69 format.csv {
70 # Export all entries
70 # Export all entries
71 @entries = scope.all
71 @entries = scope.to_a
72 send_data(query_to_csv(@entries, @query, params), :type => 'text/csv; header=present', :filename => 'timelog.csv')
72 send_data(query_to_csv(@entries, @query, params), :type => 'text/csv; header=present', :filename => 'timelog.csv')
73 }
73 }
74 end
74 end
@@ -232,7 +232,7 private
232 end
232 end
233
233
234 def find_time_entries
234 def find_time_entries
235 @time_entries = TimeEntry.where(:id => params[:id] || params[:ids]).all
235 @time_entries = TimeEntry.where(:id => params[:id] || params[:ids]).to_a
236 raise ActiveRecord::RecordNotFound if @time_entries.empty?
236 raise ActiveRecord::RecordNotFound if @time_entries.empty?
237 @projects = @time_entries.collect(&:project).compact.uniq
237 @projects = @time_entries.collect(&:project).compact.uniq
238 @project = @projects.first if @projects.size == 1
238 @project = @projects.first if @projects.size == 1
@@ -29,14 +29,14 class TrackersController < ApplicationController
29 render :action => "index", :layout => false if request.xhr?
29 render :action => "index", :layout => false if request.xhr?
30 }
30 }
31 format.api {
31 format.api {
32 @trackers = Tracker.sorted.all
32 @trackers = Tracker.sorted.to_a
33 }
33 }
34 end
34 end
35 end
35 end
36
36
37 def new
37 def new
38 @tracker ||= Tracker.new(params[:tracker])
38 @tracker ||= Tracker.new(params[:tracker])
39 @trackers = Tracker.sorted.all
39 @trackers = Tracker.sorted.to_a
40 @projects = Project.all
40 @projects = Project.all
41 end
41 end
42
42
@@ -95,7 +95,7 class TrackersController < ApplicationController
95 redirect_to fields_trackers_path
95 redirect_to fields_trackers_path
96 return
96 return
97 end
97 end
98 @trackers = Tracker.sorted.all
98 @trackers = Tracker.sorted.to_a
99 @custom_fields = IssueCustomField.all.sort
99 @custom_fields = IssueCustomField.all.sort
100 end
100 end
101 end
101 end
@@ -47,7 +47,7 class UsersController < ApplicationController
47 @user_count = scope.count
47 @user_count = scope.count
48 @user_pages = Paginator.new @user_count, @limit, params['page']
48 @user_pages = Paginator.new @user_count, @limit, params['page']
49 @offset ||= @user_pages.offset
49 @offset ||= @user_pages.offset
50 @users = scope.order(sort_clause).limit(@limit).offset(@offset).all
50 @users = scope.order(sort_clause).limit(@limit).offset(@offset).to_a
51
51
52 respond_to do |format|
52 respond_to do |format|
53 format.html {
53 format.html {
@@ -60,7 +60,7 class UsersController < ApplicationController
60
60
61 def show
61 def show
62 # show projects based on current user visibility
62 # show projects based on current user visibility
63 @memberships = @user.memberships.where(Project.visible_condition(User.current)).all
63 @memberships = @user.memberships.where(Project.visible_condition(User.current)).to_a
64
64
65 events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
65 events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
66 @events_by_day = events.group_by(&:event_date)
66 @events_by_day = events.group_by(&:event_date)
@@ -90,7 +90,7 class UsersController < ApplicationController
90 @user.admin = params[:user][:admin] || false
90 @user.admin = params[:user][:admin] || false
91 @user.login = params[:user][:login]
91 @user.login = params[:user][:login]
92 @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] unless @user.auth_source_id
92 @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] unless @user.auth_source_id
93 @user.pref.attributes = params[:pref]
93 @user.pref.attributes = params[:pref] if params[:pref]
94
94
95 if @user.save
95 if @user.save
96 Mailer.account_information(@user, @user.password).deliver if params[:send_information]
96 Mailer.account_information(@user, @user.password).deliver if params[:send_information]
@@ -134,7 +134,7 class UsersController < ApplicationController
134 # Was the account actived ? (do it before User#save clears the change)
134 # Was the account actived ? (do it before User#save clears the change)
135 was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
135 was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
136 # TODO: Similar to My#account
136 # TODO: Similar to My#account
137 @user.pref.attributes = params[:pref]
137 @user.pref.attributes = params[:pref] if params[:pref]
138
138
139 if @user.save
139 if @user.save
140 @user.pref.save
140 @user.pref.save
@@ -31,7 +31,7 class VersionsController < ApplicationController
31 def index
31 def index
32 respond_to do |format|
32 respond_to do |format|
33 format.html {
33 format.html {
34 @trackers = @project.trackers.sorted.all
34 @trackers = @project.trackers.sorted.to_a
35 retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?})
35 retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?})
36 @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
36 @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
37 project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
37 project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
@@ -56,7 +56,7 class VersionsController < ApplicationController
56 @versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?}
56 @versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?}
57 }
57 }
58 format.api {
58 format.api {
59 @versions = @project.shared_versions.all
59 @versions = @project.shared_versions.to_a
60 }
60 }
61 end
61 end
62 end
62 end
@@ -67,7 +67,7 class VersionsController < ApplicationController
67 @issues = @version.fixed_issues.visible.
67 @issues = @version.fixed_issues.visible.
68 includes(:status, :tracker, :priority).
68 includes(:status, :tracker, :priority).
69 reorder("#{Tracker.table_name}.position, #{Issue.table_name}.id").
69 reorder("#{Tracker.table_name}.position, #{Issue.table_name}.id").
70 all
70 to_a
71 }
71 }
72 format.api
72 format.api
73 end
73 end
@@ -117,7 +117,7 class VersionsController < ApplicationController
117 end
117 end
118
118
119 def update
119 def update
120 if request.put? && params[:version]
120 if params[:version]
121 attributes = params[:version].dup
121 attributes = params[:version].dup
122 attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing'])
122 attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing'])
123 @version.safe_attributes = attributes
123 @version.safe_attributes = attributes
@@ -53,7 +53,7 class WatchersController < ApplicationController
53 def append
53 def append
54 if params[:watcher].is_a?(Hash)
54 if params[:watcher].is_a?(Hash)
55 user_ids = params[:watcher][:user_ids] || [params[:watcher][:user_id]]
55 user_ids = params[:watcher][:user_ids] || [params[:watcher][:user_id]]
56 @users = User.active.where(:id => user_ids).all
56 @users = User.active.where(:id => user_ids).to_a
57 end
57 end
58 if @users.blank?
58 if @users.blank?
59 render :nothing => true
59 render :nothing => true
@@ -92,7 +92,7 class WatchersController < ApplicationController
92 def find_watchables
92 def find_watchables
93 klass = Object.const_get(params[:object_type].camelcase) rescue nil
93 klass = Object.const_get(params[:object_type].camelcase) rescue nil
94 if klass && klass.respond_to?('watched_by')
94 if klass && klass.respond_to?('watched_by')
95 @watchables = klass.where(:id => Array.wrap(params[:object_id])).all
95 @watchables = klass.where(:id => Array.wrap(params[:object_id])).to_a
96 raise Unauthorized if @watchables.any? {|w|
96 raise Unauthorized if @watchables.any? {|w|
97 if w.respond_to?(:visible?)
97 if w.respond_to?(:visible?)
98 !w.visible?
98 !w.visible?
@@ -219,7 +219,7 class WikiController < ApplicationController
219 reorder('version DESC').
219 reorder('version DESC').
220 limit(@version_pages.per_page + 1).
220 limit(@version_pages.per_page + 1).
221 offset(@version_pages.offset).
221 offset(@version_pages.offset).
222 all
222 to_a
223
223
224 render :layout => false if request.xhr?
224 render :layout => false if request.xhr?
225 end
225 end
@@ -280,7 +280,7 class WikiController < ApplicationController
280 @pages = @wiki.pages.
280 @pages = @wiki.pages.
281 order('title').
281 order('title').
282 includes([:content, {:attachments => :author}]).
282 includes([:content, {:attachments => :author}]).
283 all
283 to_a
284 respond_to do |format|
284 respond_to do |format|
285 format.html {
285 format.html {
286 export = render_to_string :action => 'export_multiple', :layout => false
286 export = render_to_string :action => 'export_multiple', :layout => false
@@ -327,7 +327,7 private
327 def find_existing_or_new_page
327 def find_existing_or_new_page
328 @page = @wiki.find_or_new_page(params[:id])
328 @page = @wiki.find_or_new_page(params[:id])
329 if @wiki.page_found_with_redirect?
329 if @wiki.page_found_with_redirect?
330 redirect_to params.update(:id => @page.title)
330 redirect_to_page @page
331 end
331 end
332 end
332 end
333
333
@@ -339,10 +339,14 private
339 return
339 return
340 end
340 end
341 if @wiki.page_found_with_redirect?
341 if @wiki.page_found_with_redirect?
342 redirect_to params.update(:id => @page.title)
342 redirect_to_page @page
343 end
343 end
344 end
344 end
345
345
346 def redirect_to_page(page)
347 redirect_to :action => action_name, :project_id => page.wiki.project, :id => page.title
348 end
349
346 # Returns true if the current user is allowed to edit the page, otherwise false
350 # Returns true if the current user is allowed to edit the page, otherwise false
347 def editable?(page = @page)
351 def editable?(page = @page)
348 page.editable_by?(User.current)
352 page.editable_by?(User.current)
@@ -360,6 +364,6 private
360 reorder("#{WikiPage.table_name}.title").
364 reorder("#{WikiPage.table_name}.title").
361 includes(:wiki => :project).
365 includes(:wiki => :project).
362 includes(:parent).
366 includes(:parent).
363 all
367 to_a
364 end
368 end
365 end
369 end
@@ -86,9 +86,9 class WorkflowsController < ApplicationController
86 @source_role = Role.find_by_id(params[:source_role_id].to_i)
86 @source_role = Role.find_by_id(params[:source_role_id].to_i)
87 end
87 end
88 @target_trackers = params[:target_tracker_ids].blank? ?
88 @target_trackers = params[:target_tracker_ids].blank? ?
89 nil : Tracker.where(:id => params[:target_tracker_ids]).all
89 nil : Tracker.where(:id => params[:target_tracker_ids]).to_a
90 @target_roles = params[:target_role_ids].blank? ?
90 @target_roles = params[:target_role_ids].blank? ?
91 nil : Role.where(:id => params[:target_role_ids]).all
91 nil : Role.where(:id => params[:target_role_ids]).to_a
92 if request.post?
92 if request.post?
93 if params[:source_tracker_id].blank? || params[:source_role_id].blank? || (@source_tracker.nil? && @source_role.nil?)
93 if params[:source_tracker_id].blank? || params[:source_role_id].blank? || (@source_tracker.nil? && @source_role.nil?)
94 flash.now[:error] = l(:error_workflow_copy_source)
94 flash.now[:error] = l(:error_workflow_copy_source)
@@ -113,9 +113,9 class WorkflowsController < ApplicationController
113 def find_roles
113 def find_roles
114 ids = Array.wrap(params[:role_id])
114 ids = Array.wrap(params[:role_id])
115 if ids == ['all']
115 if ids == ['all']
116 @roles = Role.sorted.all
116 @roles = Role.sorted.to_a
117 elsif ids.present?
117 elsif ids.present?
118 @roles = Role.where(:id => ids).all
118 @roles = Role.where(:id => ids).to_a
119 end
119 end
120 @roles = nil if @roles.blank?
120 @roles = nil if @roles.blank?
121 end
121 end
@@ -123,9 +123,9 class WorkflowsController < ApplicationController
123 def find_trackers
123 def find_trackers
124 ids = Array.wrap(params[:tracker_id])
124 ids = Array.wrap(params[:tracker_id])
125 if ids == ['all']
125 if ids == ['all']
126 @trackers = Tracker.sorted.all
126 @trackers = Tracker.sorted.to_a
127 elsif ids.present?
127 elsif ids.present?
128 @trackers = Tracker.where(:id => ids).all
128 @trackers = Tracker.where(:id => ids).to_a
129 end
129 end
130 @trackers = nil if @trackers.blank?
130 @trackers = nil if @trackers.blank?
131 end
131 end
@@ -135,6 +135,6 class WorkflowsController < ApplicationController
135 if @trackers && @used_statuses_only
135 if @trackers && @used_statuses_only
136 @statuses = @trackers.map(&:issue_statuses).flatten.uniq.sort.presence
136 @statuses = @trackers.map(&:issue_statuses).flatten.uniq.sort.presence
137 end
137 end
138 @statuses ||= IssueStatus.sorted.all
138 @statuses ||= IssueStatus.sorted.to_a
139 end
139 end
140 end
140 end
@@ -138,9 +138,7 module ApplicationHelper
138 if project.archived?
138 if project.archived?
139 h(project.name)
139 h(project.name)
140 elsif options.key?(:action)
140 elsif options.key?(:action)
141 ActiveSupport::Deprecation.warn "#link_to_project with :action option is deprecated and will be removed in Redmine 3.0."
141 raise "#link_to_project no longer accepts :action option in Redmine 3.0"
142 url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
143 link_to project.name, url, html_options
144 else
142 else
145 link_to project.name, project_path(project, options), html_options
143 link_to project.name, project_path(project, options), html_options
146 end
144 end
@@ -157,13 +155,6 module ApplicationHelper
157 end
155 end
158 end
156 end
159
157
160 # Generates a link to a version
161 def link_to_version(version, options = {})
162 return '' unless version && version.is_a?(Version)
163 options = {:title => format_date(version.effective_date)}.merge(options)
164 link_to_if version.visible?, format_version_name(version), version_path(version), options
165 end
166
167 # Helper that formats object for html or text rendering
158 # Helper that formats object for html or text rendering
168 def format_object(object, html=true, &block)
159 def format_object(object, html=true, &block)
169 if block_given?
160 if block_given?
@@ -185,7 +176,7 module ApplicationHelper
185 when 'Project'
176 when 'Project'
186 html ? link_to_project(object) : object.to_s
177 html ? link_to_project(object) : object.to_s
187 when 'Version'
178 when 'Version'
188 html ? link_to_version(object) : object.to_s
179 html ? link_to(object.name, version_path(object)) : object.to_s
189 when 'TrueClass'
180 when 'TrueClass'
190 l(:general_text_Yes)
181 l(:general_text_Yes)
191 when 'FalseClass'
182 when 'FalseClass'
@@ -247,7 +238,7 module ApplicationHelper
247 end
238 end
248
239
249 def format_version_name(version)
240 def format_version_name(version)
250 if !version.shared? || version.project == @project
241 if version.project == @project
251 h(version)
242 h(version)
252 else
243 else
253 h("#{version.project} - #{version}")
244 h("#{version.project} - #{version}")
@@ -502,7 +493,7 module ApplicationHelper
502 h(Setting.app_title)
493 h(Setting.app_title)
503 else
494 else
504 b = []
495 b = []
505 ancestors = (@project.root? ? [] : @project.ancestors.visible.all)
496 ancestors = (@project.root? ? [] : @project.ancestors.visible.to_a)
506 if ancestors.any?
497 if ancestors.any?
507 root = ancestors.shift
498 root = ancestors.shift
508 b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
499 b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
@@ -1217,7 +1208,7 module ApplicationHelper
1217 source
1208 source
1218 end
1209 end
1219 end
1210 end
1220 super sources, options
1211 super *sources, options
1221 end
1212 end
1222
1213
1223 # Overrides Rails' image_tag with themes and plugins support.
1214 # Overrides Rails' image_tag with themes and plugins support.
@@ -1250,7 +1241,7 module ApplicationHelper
1250 end
1241 end
1251 end
1242 end
1252 end
1243 end
1253 super sources, options
1244 super *sources, options
1254 end
1245 end
1255
1246
1256 # TODO: remove this in 2.5.0
1247 # TODO: remove this in 2.5.0
@@ -1288,12 +1279,7 module ApplicationHelper
1288 end
1279 end
1289
1280
1290 def sanitize_anchor_name(anchor)
1281 def sanitize_anchor_name(anchor)
1291 if ''.respond_to?(:encoding) || RUBY_PLATFORM == 'java'
1282 anchor.gsub(%r{[^\s\-\p{Word}]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
1292 anchor.gsub(%r{[^\s\-\p{Word}]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
1293 else
1294 # TODO: remove when ruby1.8 is no longer supported
1295 anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
1296 end
1297 end
1283 end
1298
1284
1299 # Returns the javascript tags that are included in the html layout head
1285 # Returns the javascript tags that are included in the html layout head
@@ -30,7 +30,7 module GroupsHelper
30 scope = User.active.sorted.not_in_group(group).like(params[:q])
30 scope = User.active.sorted.not_in_group(group).like(params[:q])
31 principal_count = scope.count
31 principal_count = scope.count
32 principal_pages = Redmine::Pagination::Paginator.new principal_count, 100, params['page']
32 principal_pages = Redmine::Pagination::Paginator.new principal_count, 100, params['page']
33 principals = scope.offset(principal_pages.offset).limit(principal_pages.per_page).all
33 principals = scope.offset(principal_pages.offset).limit(principal_pages.per_page).to_a
34
34
35 s = content_tag('div', principals_check_box_tags('user_ids[]', principals), :id => 'principals')
35 s = content_tag('div', principals_check_box_tags('user_ids[]', principals), :id => 'principals')
36
36
@@ -63,7 +63,7 module IssuesHelper
63
63
64 def render_issue_subject_with_tree(issue)
64 def render_issue_subject_with_tree(issue)
65 s = ''
65 s = ''
66 ancestors = issue.root? ? [] : issue.ancestors.visible.all
66 ancestors = issue.root? ? [] : issue.ancestors.visible.to_a
67 ancestors.each do |ancestor|
67 ancestors.each do |ancestor|
68 s << '<div>' + content_tag('p', link_to_issue(ancestor, :project => (issue.project_id != ancestor.project_id)))
68 s << '<div>' + content_tag('p', link_to_issue(ancestor, :project => (issue.project_id != ancestor.project_id)))
69 end
69 end
@@ -204,7 +204,7 module IssuesHelper
204 order("#{Query.table_name}.name ASC").
204 order("#{Query.table_name}.name ASC").
205 # Project specific queries and global queries
205 # Project specific queries and global queries
206 where(@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id]).
206 where(@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id]).
207 all
207 to_a
208 end
208 end
209 @sidebar_queries
209 @sidebar_queries
210 end
210 end
@@ -408,7 +408,7 module IssuesHelper
408 if association
408 if association
409 record = association.class_name.constantize.find_by_id(id)
409 record = association.class_name.constantize.find_by_id(id)
410 if record
410 if record
411 record.name.force_encoding('UTF-8') if record.name.respond_to?(:force_encoding)
411 record.name.force_encoding('UTF-8')
412 return record.name
412 return record.name
413 end
413 end
414 end
414 end
@@ -22,7 +22,7 module MembersHelper
22 scope = Principal.active.sorted.not_member_of(project).like(params[:q])
22 scope = Principal.active.sorted.not_member_of(project).like(params[:q])
23 principal_count = scope.count
23 principal_count = scope.count
24 principal_pages = Redmine::Pagination::Paginator.new principal_count, 100, params['page']
24 principal_pages = Redmine::Pagination::Paginator.new principal_count, 100, params['page']
25 principals = scope.offset(principal_pages.offset).limit(principal_pages.per_page).all
25 principals = scope.offset(principal_pages.offset).limit(principal_pages.per_page).to_a
26
26
27 s = content_tag('div', principals_check_box_tags('membership[user_ids][]', principals), :id => 'principals')
27 s = content_tag('div', principals_check_box_tags('membership[user_ids][]', principals), :id => 'principals')
28
28
@@ -23,11 +23,12 module MyHelper
23 where(:project_id => User.current.projects.map(&:id)).
23 where(:project_id => User.current.projects.map(&:id)).
24 where("(start_date>=? and start_date<=?) or (due_date>=? and due_date<=?)", startdt, enddt, startdt, enddt).
24 where("(start_date>=? and start_date<=?) or (due_date>=? and due_date<=?)", startdt, enddt, startdt, enddt).
25 includes(:project, :tracker, :priority, :assigned_to).
25 includes(:project, :tracker, :priority, :assigned_to).
26 all
26 references(:project, :tracker, :priority, :assigned_to).
27 to_a
27 end
28 end
28
29
29 def documents_items
30 def documents_items
30 Document.visible.order("#{Document.table_name}.created_on DESC").limit(10).all
31 Document.visible.order("#{Document.table_name}.created_on DESC").limit(10).to_a
31 end
32 end
32
33
33 def issuesassignedtome_items
34 def issuesassignedtome_items
@@ -35,8 +36,9 module MyHelper
35 where(:assigned_to_id => ([User.current.id] + User.current.group_ids)).
36 where(:assigned_to_id => ([User.current.id] + User.current.group_ids)).
36 limit(10).
37 limit(10).
37 includes(:status, :project, :tracker, :priority).
38 includes(:status, :project, :tracker, :priority).
39 references(:status, :project, :tracker, :priority).
38 order("#{IssuePriority.table_name}.position DESC, #{Issue.table_name}.updated_on DESC").
40 order("#{IssuePriority.table_name}.position DESC, #{Issue.table_name}.updated_on DESC").
39 all
41 to_a
40 end
42 end
41
43
42 def issuesreportedbyme_items
44 def issuesreportedbyme_items
@@ -44,12 +46,13 module MyHelper
44 where(:author_id => User.current.id).
46 where(:author_id => User.current.id).
45 limit(10).
47 limit(10).
46 includes(:status, :project, :tracker).
48 includes(:status, :project, :tracker).
49 references(:status, :project, :tracker).
47 order("#{Issue.table_name}.updated_on DESC").
50 order("#{Issue.table_name}.updated_on DESC").
48 all
51 to_a
49 end
52 end
50
53
51 def issueswatched_items
54 def issueswatched_items
52 Issue.visible.on_active_project.watched_by(User.current.id).recently_updated.limit(10).all
55 Issue.visible.on_active_project.watched_by(User.current.id).recently_updated.limit(10).to_a
53 end
56 end
54
57
55 def news_items
58 def news_items
@@ -57,15 +60,17 module MyHelper
57 where(:project_id => User.current.projects.map(&:id)).
60 where(:project_id => User.current.projects.map(&:id)).
58 limit(10).
61 limit(10).
59 includes(:project, :author).
62 includes(:project, :author).
63 references(:project, :author).
60 order("#{News.table_name}.created_on DESC").
64 order("#{News.table_name}.created_on DESC").
61 all
65 to_a
62 end
66 end
63
67
64 def timelog_items
68 def timelog_items
65 TimeEntry.
69 TimeEntry.
66 where("#{TimeEntry.table_name}.user_id = ? AND #{TimeEntry.table_name}.spent_on BETWEEN ? AND ?", User.current.id, Date.today - 6, Date.today).
70 where("#{TimeEntry.table_name}.user_id = ? AND #{TimeEntry.table_name}.spent_on BETWEEN ? AND ?", User.current.id, Date.today - 6, Date.today).
67 includes(:activity, :project, {:issue => [:tracker, :status]}).
71 joins(:activity, :project, {:issue => [:tracker, :status]}).
72 references(:activity, :project, {:issue => [:tracker, :status]}).
68 order("#{TimeEntry.table_name}.spent_on DESC, #{Project.table_name}.name ASC, #{Tracker.table_name}.position ASC, #{Issue.table_name}.id ASC").
73 order("#{TimeEntry.table_name}.spent_on DESC, #{Project.table_name}.name ASC, #{Tracker.table_name}.position ASC, #{Issue.table_name}.id ASC").
69 all
74 to_a
70 end
75 end
71 end
76 end
@@ -18,6 +18,11
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 module ProjectsHelper
20 module ProjectsHelper
21 def link_to_version(version, options = {})
22 return '' unless version && version.is_a?(Version)
23 link_to_if version.visible?, format_version_name(version), version_path(version), options
24 end
25
21 def project_settings_tabs
26 def project_settings_tabs
22 tabs = [{:name => 'info', :action => :edit_project, :partial => 'projects/edit', :label => :label_information_plural},
27 tabs = [{:name => 'info', :action => :edit_project, :partial => 'projects/edit', :label => :label_information_plural},
23 {:name => 'modules', :action => :select_project_modules, :partial => 'projects/settings/modules', :label => :label_module_plural},
28 {:name => 'modules', :action => :select_project_modules, :partial => 'projects/settings/modules', :label => :label_module_plural},
@@ -143,7 +143,7 module QueriesHelper
143 end
143 end
144 end
144 end
145
145
146 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
146 export = CSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
147 # csv header fields
147 # csv header fields
148 csv << columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) }
148 csv << columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) }
149 # csv lines
149 # csv lines
@@ -29,7 +29,6 module SearchHelper
29 result << '...'
29 result << '...'
30 break
30 break
31 end
31 end
32 words = words.mb_chars
33 if i.even?
32 if i.even?
34 result << h(words.length > 100 ? "#{words.slice(0..44)} ... #{words.slice(-45..-1)}" : words)
33 result << h(words.length > 100 ? "#{words.slice(0..44)} ... #{words.slice(-45..-1)}" : words)
35 else
34 else
@@ -84,7 +84,8 module SortHelper
84 def to_sql
84 def to_sql
85 sql = @criteria.collect do |k,o|
85 sql = @criteria.collect do |k,o|
86 if s = @available_criteria[k]
86 if s = @available_criteria[k]
87 (o ? s.to_a : s.to_a.collect {|c| append_desc(c)})
87 s = [s] unless s.is_a?(Array)
88 (o ? s : s.collect {|c| append_desc(c)})
88 end
89 end
89 end.flatten.compact
90 end.flatten.compact
90 sql.blank? ? nil : sql
91 sql.blank? ? nil : sql
@@ -105,7 +105,7 module TimelogHelper
105
105
106 def report_to_csv(report)
106 def report_to_csv(report)
107 decimal_separator = l(:general_csv_decimal_separator)
107 decimal_separator = l(:general_csv_decimal_separator)
108 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
108 export = CSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
109 # Column headers
109 # Column headers
110 headers = report.criteria.collect {|criteria| l(report.available_criteria[criteria][:label]) }
110 headers = report.criteria.collect {|criteria| l(report.available_criteria[criteria][:label]) }
111 headers += report.periods
111 headers += report.periods
@@ -27,6 +27,7 class Attachment < ActiveRecord::Base
27 validates_length_of :disk_filename, :maximum => 255
27 validates_length_of :disk_filename, :maximum => 255
28 validates_length_of :description, :maximum => 255
28 validates_length_of :description, :maximum => 255
29 validate :validate_max_file_size
29 validate :validate_max_file_size
30 attr_protected :id
30
31
31 acts_as_event :title => :filename,
32 acts_as_event :title => :filename,
32 :url => Proc.new {|o| {:controller => 'attachments', :action => 'download', :id => o.id, :filename => o.filename}}
33 :url => Proc.new {|o| {:controller => 'attachments', :action => 'download', :id => o.id, :filename => o.filename}}
@@ -34,16 +35,16 class Attachment < ActiveRecord::Base
34 acts_as_activity_provider :type => 'files',
35 acts_as_activity_provider :type => 'files',
35 :permission => :view_files,
36 :permission => :view_files,
36 :author_key => :author_id,
37 :author_key => :author_id,
37 :find_options => {:select => "#{Attachment.table_name}.*",
38 :scope => select("#{Attachment.table_name}.*").
38 :joins => "LEFT JOIN #{Version.table_name} ON #{Attachment.table_name}.container_type='Version' AND #{Version.table_name}.id = #{Attachment.table_name}.container_id " +
39 joins("LEFT JOIN #{Version.table_name} ON #{Attachment.table_name}.container_type='Version' AND #{Version.table_name}.id = #{Attachment.table_name}.container_id " +
39 "LEFT JOIN #{Project.table_name} ON #{Version.table_name}.project_id = #{Project.table_name}.id OR ( #{Attachment.table_name}.container_type='Project' AND #{Attachment.table_name}.container_id = #{Project.table_name}.id )"}
40 "LEFT JOIN #{Project.table_name} ON #{Version.table_name}.project_id = #{Project.table_name}.id OR ( #{Attachment.table_name}.container_type='Project' AND #{Attachment.table_name}.container_id = #{Project.table_name}.id )")
40
41
41 acts_as_activity_provider :type => 'documents',
42 acts_as_activity_provider :type => 'documents',
42 :permission => :view_documents,
43 :permission => :view_documents,
43 :author_key => :author_id,
44 :author_key => :author_id,
44 :find_options => {:select => "#{Attachment.table_name}.*",
45 :scope => select("#{Attachment.table_name}.*").
45 :joins => "LEFT JOIN #{Document.table_name} ON #{Attachment.table_name}.container_type='Document' AND #{Document.table_name}.id = #{Attachment.table_name}.container_id " +
46 joins("LEFT JOIN #{Document.table_name} ON #{Attachment.table_name}.container_type='Document' AND #{Document.table_name}.id = #{Attachment.table_name}.container_id " +
46 "LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id"}
47 "LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id")
47
48
48 cattr_accessor :storage_path
49 cattr_accessor :storage_path
49 @@storage_path = Redmine::Configuration['attachments_storage_path'] || File.join(Rails.root, "files")
50 @@storage_path = Redmine::Configuration['attachments_storage_path'] || File.join(Rails.root, "files")
@@ -74,7 +75,7 class Attachment < ActiveRecord::Base
74 if @temp_file.size > 0
75 if @temp_file.size > 0
75 if @temp_file.respond_to?(:original_filename)
76 if @temp_file.respond_to?(:original_filename)
76 self.filename = @temp_file.original_filename
77 self.filename = @temp_file.original_filename
77 self.filename.force_encoding("UTF-8") if filename.respond_to?(:force_encoding)
78 self.filename.force_encoding("UTF-8")
78 end
79 end
79 if @temp_file.respond_to?(:content_type)
80 if @temp_file.respond_to?(:content_type)
80 self.content_type = @temp_file.content_type.to_s.chomp
81 self.content_type = @temp_file.content_type.to_s.chomp
@@ -29,6 +29,7 class AuthSource < ActiveRecord::Base
29 validates_presence_of :name
29 validates_presence_of :name
30 validates_uniqueness_of :name
30 validates_uniqueness_of :name
31 validates_length_of :name, :maximum => 60
31 validates_length_of :name, :maximum => 60
32 attr_protected :id
32
33
33 def authenticate(login, password)
34 def authenticate(login, password)
34 end
35 end
@@ -18,8 +18,8
18 class Board < ActiveRecord::Base
18 class Board < ActiveRecord::Base
19 include Redmine::SafeAttributes
19 include Redmine::SafeAttributes
20 belongs_to :project
20 belongs_to :project
21 has_many :topics, :class_name => 'Message', :conditions => "#{Message.table_name}.parent_id IS NULL", :order => "#{Message.table_name}.created_on DESC"
21 has_many :topics, lambda {where("#{Message.table_name}.parent_id IS NULL").order("#{Message.table_name}.created_on DESC")}, :class_name => 'Message'
22 has_many :messages, :dependent => :destroy, :order => "#{Message.table_name}.created_on DESC"
22 has_many :messages, lambda {order("#{Message.table_name}.created_on DESC")}, :dependent => :destroy
23 belongs_to :last_message, :class_name => 'Message', :foreign_key => :last_message_id
23 belongs_to :last_message, :class_name => 'Message', :foreign_key => :last_message_id
24 acts_as_tree :dependent => :nullify
24 acts_as_tree :dependent => :nullify
25 acts_as_list :scope => '(project_id = #{project_id} AND parent_id #{parent_id ? "= #{parent_id}" : "IS NULL"})'
25 acts_as_list :scope => '(project_id = #{project_id} AND parent_id #{parent_id ? "= #{parent_id}" : "IS NULL"})'
@@ -29,9 +29,12 class Board < ActiveRecord::Base
29 validates_length_of :name, :maximum => 30
29 validates_length_of :name, :maximum => 30
30 validates_length_of :description, :maximum => 255
30 validates_length_of :description, :maximum => 255
31 validate :validate_board
31 validate :validate_board
32 attr_protected :id
32
33
33 scope :visible, lambda {|*args|
34 scope :visible, lambda {|*args|
34 includes(:project).where(Project.allowed_to_condition(args.shift || User.current, :view_messages, *args))
35 joins(:project).
36 references(:project).
37 where(Project.allowed_to_condition(args.shift || User.current, :view_messages, *args))
35 }
38 }
36
39
37 safe_attributes 'name', 'description', 'parent_id', 'move_to'
40 safe_attributes 'name', 'description', 'parent_id', 'move_to'
@@ -21,6 +21,7 class Change < ActiveRecord::Base
21 validates_presence_of :changeset_id, :action, :path
21 validates_presence_of :changeset_id, :action, :path
22 before_save :init_path
22 before_save :init_path
23 before_validation :replace_invalid_utf8_of_path
23 before_validation :replace_invalid_utf8_of_path
24 attr_protected :id
24
25
25 def relative_path
26 def relative_path
26 changeset.repository.relative_path(path)
27 changeset.repository.relative_path(path)
@@ -35,20 +35,23 class Changeset < ActiveRecord::Base
35 :url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :repository_id => o.repository.identifier_param, :rev => o.identifier}}
35 :url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :repository_id => o.repository.identifier_param, :rev => o.identifier}}
36
36
37 acts_as_searchable :columns => 'comments',
37 acts_as_searchable :columns => 'comments',
38 :include => {:repository => :project},
38 :scope => preload(:repository => :project),
39 :project_key => "#{Repository.table_name}.project_id",
39 :project_key => "#{Repository.table_name}.project_id",
40 :date_column => 'committed_on'
40 :date_column => 'committed_on'
41
41
42 acts_as_activity_provider :timestamp => "#{table_name}.committed_on",
42 acts_as_activity_provider :timestamp => "#{table_name}.committed_on",
43 :author_key => :user_id,
43 :author_key => :user_id,
44 :find_options => {:include => [:user, {:repository => :project}]}
44 :scope => preload(:user, {:repository => :project})
45
45
46 validates_presence_of :repository_id, :revision, :committed_on, :commit_date
46 validates_presence_of :repository_id, :revision, :committed_on, :commit_date
47 validates_uniqueness_of :revision, :scope => :repository_id
47 validates_uniqueness_of :revision, :scope => :repository_id
48 validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
48 validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
49 attr_protected :id
49
50
50 scope :visible, lambda {|*args|
51 scope :visible, lambda {|*args|
51 includes(:repository => :project).where(Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args))
52 joins(:repository => :project).
53 references(:repository => :project).
54 where(Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args))
52 }
55 }
53
56
54 after_create :scan_for_issues
57 after_create :scan_for_issues
@@ -21,6 +21,7 class Comment < ActiveRecord::Base
21 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
21 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
22
22
23 validates_presence_of :commented, :author, :comments
23 validates_presence_of :commented, :author, :comments
24 attr_protected :id
24
25
25 after_create :send_notification
26 after_create :send_notification
26
27
@@ -29,6 +29,7 class CustomField < ActiveRecord::Base
29 validates_length_of :name, :maximum => 30
29 validates_length_of :name, :maximum => 30
30 validates_inclusion_of :field_format, :in => Proc.new { Redmine::FieldFormat.available_formats }
30 validates_inclusion_of :field_format, :in => Proc.new { Redmine::FieldFormat.available_formats }
31 validate :validate_custom_field
31 validate :validate_custom_field
32 attr_protected :id
32
33
33 before_validation :set_searchable
34 before_validation :set_searchable
34 before_save do |field|
35 before_save do |field|
@@ -117,7 +118,7 class CustomField < ActiveRecord::Base
117 values = read_attribute(:possible_values)
118 values = read_attribute(:possible_values)
118 if values.is_a?(Array)
119 if values.is_a?(Array)
119 values.each do |value|
120 values.each do |value|
120 value.force_encoding('UTF-8') if value.respond_to?(:force_encoding)
121 value.force_encoding('UTF-8')
121 end
122 end
122 values
123 values
123 else
124 else
@@ -218,7 +219,7 class CustomField < ActiveRecord::Base
218
219
219 # to move in project_custom_field
220 # to move in project_custom_field
220 def self.for_all
221 def self.for_all
221 where(:is_for_all => true).order('position').all
222 where(:is_for_all => true).order('position').to_a
222 end
223 end
223
224
224 def type_name
225 def type_name
@@ -18,6 +18,7
18 class CustomValue < ActiveRecord::Base
18 class CustomValue < ActiveRecord::Base
19 belongs_to :custom_field
19 belongs_to :custom_field
20 belongs_to :customized, :polymorphic => true
20 belongs_to :customized, :polymorphic => true
21 attr_protected :id
21
22
22 def initialize(attributes=nil, *args)
23 def initialize(attributes=nil, *args)
23 super
24 super
@@ -21,19 +21,23 class Document < ActiveRecord::Base
21 belongs_to :category, :class_name => "DocumentCategory", :foreign_key => "category_id"
21 belongs_to :category, :class_name => "DocumentCategory", :foreign_key => "category_id"
22 acts_as_attachable :delete_permission => :delete_documents
22 acts_as_attachable :delete_permission => :delete_documents
23
23
24 acts_as_searchable :columns => ['title', "#{table_name}.description"], :include => :project
24 acts_as_searchable :columns => ['title', "#{table_name}.description"],
25 :scope => preload(:project)
25 acts_as_event :title => Proc.new {|o| "#{l(:label_document)}: #{o.title}"},
26 acts_as_event :title => Proc.new {|o| "#{l(:label_document)}: #{o.title}"},
26 :author => Proc.new {|o| o.attachments.reorder("#{Attachment.table_name}.created_on ASC").first.try(:author) },
27 :author => Proc.new {|o| o.attachments.reorder("#{Attachment.table_name}.created_on ASC").first.try(:author) },
27 :url => Proc.new {|o| {:controller => 'documents', :action => 'show', :id => o.id}}
28 :url => Proc.new {|o| {:controller => 'documents', :action => 'show', :id => o.id}}
28 acts_as_activity_provider :find_options => {:include => :project}
29 acts_as_activity_provider :scope => preload(:project)
29
30
30 validates_presence_of :project, :title, :category
31 validates_presence_of :project, :title, :category
31 validates_length_of :title, :maximum => 60
32 validates_length_of :title, :maximum => 60
33 attr_protected :id
32
34
33 after_create :send_notification
35 after_create :send_notification
34
36
35 scope :visible, lambda {|*args|
37 scope :visible, lambda {|*args|
36 includes(:project).where(Project.allowed_to_condition(args.shift || User.current, :view_documents, *args))
38 joins(:project).
39 references(:project).
40 where(Project.allowed_to_condition(args.shift || User.current, :view_documents, *args))
37 }
41 }
38
42
39 safe_attributes 'category_id', 'title', 'description'
43 safe_attributes 'category_id', 'title', 'description'
@@ -21,6 +21,7 class EnabledModule < ActiveRecord::Base
21
21
22 validates_presence_of :name
22 validates_presence_of :name
23 validates_uniqueness_of :name, :scope => :project_id
23 validates_uniqueness_of :name, :scope => :project_id
24 attr_protected :id
24
25
25 after_create :module_enabled
26 after_create :module_enabled
26
27
@@ -18,7 +18,7
18 class Enumeration < ActiveRecord::Base
18 class Enumeration < ActiveRecord::Base
19 include Redmine::SubclassFactory
19 include Redmine::SubclassFactory
20
20
21 default_scope :order => "#{Enumeration.table_name}.position ASC"
21 default_scope lambda {order("#{Enumeration.table_name}.position ASC")}
22
22
23 belongs_to :project
23 belongs_to :project
24
24
@@ -28,6 +28,7 class Group < Principal
28 validates_presence_of :lastname
28 validates_presence_of :lastname
29 validates_uniqueness_of :lastname, :case_sensitive => false
29 validates_uniqueness_of :lastname, :case_sensitive => false
30 validates_length_of :lastname, :maximum => 255
30 validates_length_of :lastname, :maximum => 255
31 attr_protected :id
31
32
32 before_destroy :remove_references_before_destroy
33 before_destroy :remove_references_before_destroy
33
34
@@ -81,7 +82,8 class Group < Principal
81 def user_removed(user)
82 def user_removed(user)
82 members.each do |member|
83 members.each do |member|
83 MemberRole.
84 MemberRole.
84 includes(:member).
85 joins(:member).
86 references(:member).
85 where("#{Member.table_name}.user_id = ? AND #{MemberRole.table_name}.inherited_from IN (?)", user.id, member.member_role_ids).
87 where("#{Member.table_name}.user_id = ? AND #{MemberRole.table_name}.inherited_from IN (?)", user.id, member.member_role_ids).
86 each(&:destroy)
88 each(&:destroy)
87 end
89 end
@@ -95,10 +97,6 class Group < Principal
95 super(attr_name, *args)
97 super(attr_name, *args)
96 end
98 end
97
99
98 def self.builtin_id(arg)
99 (arg.anonymous? ? GroupAnonymous : GroupNonMember).instance_id
100 end
101
102 def self.anonymous
100 def self.anonymous
103 GroupAnonymous.load_instance
101 GroupAnonymous.load_instance
104 end
102 end
@@ -23,8 +23,4 class GroupAnonymous < GroupBuiltin
23 def builtin_type
23 def builtin_type
24 "anonymous"
24 "anonymous"
25 end
25 end
26
27 def self.instance_id
28 @@instance_id ||= load_instance.id
29 end
30 end
26 end
@@ -37,7 +37,7 class GroupBuiltin < Group
37 class << self
37 class << self
38 def load_instance
38 def load_instance
39 return nil if self == GroupBuiltin
39 return nil if self == GroupBuiltin
40 instance = first(:order => 'id') || create_instance
40 instance = order('id').first || create_instance
41 end
41 end
42
42
43 def create_instance
43 def create_instance
@@ -23,8 +23,4 class GroupNonMember < GroupBuiltin
23 def builtin_type
23 def builtin_type
24 "non_member"
24 "non_member"
25 end
25 end
26
27 def self.instance_id
28 @@instance_id ||= load_instance.id
29 end
30 end
26 end
@@ -31,15 +31,12 class Issue < ActiveRecord::Base
31
31
32 has_many :journals, :as => :journalized, :dependent => :destroy
32 has_many :journals, :as => :journalized, :dependent => :destroy
33 has_many :visible_journals,
33 has_many :visible_journals,
34 lambda {where(["(#{Journal.table_name}.private_notes = ? OR (#{Project.allowed_to_condition(User.current, :view_private_notes)}))", false])},
34 :class_name => 'Journal',
35 :class_name => 'Journal',
35 :as => :journalized,
36 :as => :journalized
36 :conditions => Proc.new {
37 ["(#{Journal.table_name}.private_notes = ? OR (#{Project.allowed_to_condition(User.current, :view_private_notes)}))", false]
38 },
39 :readonly => true
40
37
41 has_many :time_entries, :dependent => :destroy
38 has_many :time_entries, :dependent => :destroy
42 has_and_belongs_to_many :changesets, :order => "#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC"
39 has_and_belongs_to_many :changesets, lambda {order("#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC")}
43
40
44 has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all
41 has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all
45 has_many :relations_to, :class_name => 'IssueRelation', :foreign_key => 'issue_to_id', :dependent => :delete_all
42 has_many :relations_to, :class_name => 'IssueRelation', :foreign_key => 'issue_to_id', :dependent => :delete_all
@@ -49,14 +46,19 class Issue < ActiveRecord::Base
49 acts_as_customizable
46 acts_as_customizable
50 acts_as_watchable
47 acts_as_watchable
51 acts_as_searchable :columns => ['subject', "#{table_name}.description", "#{Journal.table_name}.notes"],
48 acts_as_searchable :columns => ['subject', "#{table_name}.description", "#{Journal.table_name}.notes"],
52 :include => [:project, :visible_journals],
53 # sort by id so that limited eager loading doesn't break with postgresql
49 # sort by id so that limited eager loading doesn't break with postgresql
54 :order_column => "#{table_name}.id"
50 :order_column => "#{table_name}.id",
51 :scope => lambda { joins(:project).
52 joins("LEFT OUTER JOIN #{Journal.table_name} ON #{Journal.table_name}.journalized_type='Issue'" +
53 " AND #{Journal.table_name}.journalized_id = #{Issue.table_name}.id" +
54 " AND (#{Journal.table_name}.private_notes = #{connection.quoted_false}" +
55 " OR (#{Project.allowed_to_condition(User.current, :view_private_notes)}))") }
56
55 acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id} (#{o.status}): #{o.subject}"},
57 acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id} (#{o.status}): #{o.subject}"},
56 :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}},
58 :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}},
57 :type => Proc.new {|o| 'issue' + (o.closed? ? ' closed' : '') }
59 :type => Proc.new {|o| 'issue' + (o.closed? ? ' closed' : '') }
58
60
59 acts_as_activity_provider :find_options => {:include => [:project, :author, :tracker]},
61 acts_as_activity_provider :scope => preload(:project, :author, :tracker),
60 :author_key => :author_id
62 :author_key => :author_id
61
63
62 DONE_RATIO_OPTIONS = %w(issue_field issue_status)
64 DONE_RATIO_OPTIONS = %w(issue_field issue_status)
@@ -72,19 +74,26 class Issue < ActiveRecord::Base
72 validates :start_date, :date => true
74 validates :start_date, :date => true
73 validates :due_date, :date => true
75 validates :due_date, :date => true
74 validate :validate_issue, :validate_required_fields
76 validate :validate_issue, :validate_required_fields
77 attr_protected :id
75
78
76 scope :visible, lambda {|*args|
79 scope :visible, lambda {|*args|
77 includes(:project).where(Issue.visible_condition(args.shift || User.current, *args))
80 joins(:project).
81 references(:project).
82 where(Issue.visible_condition(args.shift || User.current, *args))
78 }
83 }
79
84
80 scope :open, lambda {|*args|
85 scope :open, lambda {|*args|
81 is_closed = args.size > 0 ? !args.first : false
86 is_closed = args.size > 0 ? !args.first : false
82 includes(:status).where("#{IssueStatus.table_name}.is_closed = ?", is_closed)
87 joins(:status).
88 references(:status).
89 where("#{IssueStatus.table_name}.is_closed = ?", is_closed)
83 }
90 }
84
91
85 scope :recently_updated, lambda { order("#{Issue.table_name}.updated_on DESC") }
92 scope :recently_updated, lambda { order("#{Issue.table_name}.updated_on DESC") }
86 scope :on_active_project, lambda {
93 scope :on_active_project, lambda {
87 includes(:status, :project, :tracker).where("#{Project.table_name}.status = ?", Project::STATUS_ACTIVE)
94 joins(:project).
95 references(:project).
96 where("#{Project.table_name}.status = ?", Project::STATUS_ACTIVE)
88 }
97 }
89 scope :fixed_version, lambda {|versions|
98 scope :fixed_version, lambda {|versions|
90 ids = [versions].flatten.compact.map {|v| v.is_a?(Version) ? v.id : v}
99 ids = [versions].flatten.compact.map {|v| v.is_a?(Version) ? v.id : v}
@@ -107,7 +116,7 class Issue < ActiveRecord::Base
107 # Returns a SQL conditions string used to find all issues visible by the specified user
116 # Returns a SQL conditions string used to find all issues visible by the specified user
108 def self.visible_condition(user, options={})
117 def self.visible_condition(user, options={})
109 Project.allowed_to_condition(user, :view_issues, options) do |role, user|
118 Project.allowed_to_condition(user, :view_issues, options) do |role, user|
110 if user.logged?
119 if user.id && user.logged?
111 case role.issues_visibility
120 case role.issues_visibility
112 when 'all'
121 when 'all'
113 nil
122 nil
@@ -351,6 +360,10 class Issue < ActiveRecord::Base
351 # Do not redefine alias chain on reload (see #4838)
360 # Do not redefine alias chain on reload (see #4838)
352 alias_method_chain(:assign_attributes, :project_and_tracker_first) unless method_defined?(:assign_attributes_without_project_and_tracker_first)
361 alias_method_chain(:assign_attributes, :project_and_tracker_first) unless method_defined?(:assign_attributes_without_project_and_tracker_first)
353
362
363 def attributes=(new_attributes)
364 assign_attributes new_attributes
365 end
366
354 def estimated_hours=(h)
367 def estimated_hours=(h)
355 write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h)
368 write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h)
356 end
369 end
@@ -423,7 +436,7 class Issue < ActiveRecord::Base
423 def safe_attributes=(attrs, user=User.current)
436 def safe_attributes=(attrs, user=User.current)
424 return unless attrs.is_a?(Hash)
437 return unless attrs.is_a?(Hash)
425
438
426 attrs = attrs.dup
439 attrs = attrs.deep_dup
427
440
428 # Project and Tracker must be set before since new_statuses_allowed_to depends on it.
441 # Project and Tracker must be set before since new_statuses_allowed_to depends on it.
429 if (p = attrs.delete('project_id')) && safe_attribute?('project_id')
442 if (p = attrs.delete('project_id')) && safe_attribute?('project_id')
@@ -458,14 +471,12 class Issue < ActiveRecord::Base
458
471
459 if attrs['custom_field_values'].present?
472 if attrs['custom_field_values'].present?
460 editable_custom_field_ids = editable_custom_field_values(user).map {|v| v.custom_field_id.to_s}
473 editable_custom_field_ids = editable_custom_field_values(user).map {|v| v.custom_field_id.to_s}
461 # TODO: use #select when ruby1.8 support is dropped
474 attrs['custom_field_values'].select! {|k, v| editable_custom_field_ids.include?(k.to_s)}
462 attrs['custom_field_values'] = attrs['custom_field_values'].reject {|k, v| !editable_custom_field_ids.include?(k.to_s)}
463 end
475 end
464
476
465 if attrs['custom_fields'].present?
477 if attrs['custom_fields'].present?
466 editable_custom_field_ids = editable_custom_field_values(user).map {|v| v.custom_field_id.to_s}
478 editable_custom_field_ids = editable_custom_field_values(user).map {|v| v.custom_field_id.to_s}
467 # TODO: use #select when ruby1.8 support is dropped
479 attrs['custom_fields'].select! {|c| editable_custom_field_ids.include?(c['id'].to_s)}
468 attrs['custom_fields'] = attrs['custom_fields'].reject {|c| !editable_custom_field_ids.include?(c['id'].to_s)}
469 end
480 end
470
481
471 # mass-assignment security bypass
482 # mass-assignment security bypass
@@ -733,7 +744,7 class Issue < ActiveRecord::Base
733 def assignable_versions
744 def assignable_versions
734 return @assignable_versions if @assignable_versions
745 return @assignable_versions if @assignable_versions
735
746
736 versions = project.shared_versions.open.all
747 versions = project.shared_versions.open.to_a
737 if fixed_version
748 if fixed_version
738 if fixed_version_id_changed?
749 if fixed_version_id_changed?
739 # nothing to do
750 # nothing to do
@@ -879,10 +890,14 class Issue < ActiveRecord::Base
879 if issues.any?
890 if issues.any?
880 issue_ids = issues.map(&:id)
891 issue_ids = issues.map(&:id)
881 # Relations with issue_from in given issues and visible issue_to
892 # Relations with issue_from in given issues and visible issue_to
882 relations_from = IssueRelation.includes(:issue_to => [:status, :project]).where(visible_condition(user)).where(:issue_from_id => issue_ids).all
893 relations_from = IssueRelation.joins(:issue_to => :project).
894 references(:issue_to => :project).
895 where(visible_condition(user)).where(:issue_from_id => issue_ids).to_a
883 # Relations with issue_to in given issues and visible issue_from
896 # Relations with issue_to in given issues and visible issue_from
884 relations_to = IssueRelation.includes(:issue_from => [:status, :project]).where(visible_condition(user)).where(:issue_to_id => issue_ids).all
897 relations_to = IssueRelation.joins(:issue_from => :project).
885
898 references(:issue_from => :project).
899 where(visible_condition(user)).
900 where(:issue_to_id => issue_ids).to_a
886 issues.each do |issue|
901 issues.each do |issue|
887 relations =
902 relations =
888 relations_from.select {|relation| relation.issue_from_id == issue.id} +
903 relations_from.select {|relation| relation.issue_from_id == issue.id} +
@@ -1121,6 +1136,7 class Issue < ActiveRecord::Base
1121 def parent_issue_id=(arg)
1136 def parent_issue_id=(arg)
1122 s = arg.to_s.strip.presence
1137 s = arg.to_s.strip.presence
1123 if s && (m = s.match(%r{\A#?(\d+)\z})) && (@parent_issue = Issue.find_by_id(m[1]))
1138 if s && (m = s.match(%r{\A#?(\d+)\z})) && (@parent_issue = Issue.find_by_id(m[1]))
1139 @parent_issue.id
1124 @invalid_parent_issue_id = nil
1140 @invalid_parent_issue_id = nil
1125 elsif s.blank?
1141 elsif s.blank?
1126 @parent_issue = nil
1142 @parent_issue = nil
@@ -1349,7 +1365,7 class Issue < ActiveRecord::Base
1349 self.root_id = (@parent_issue.nil? ? id : @parent_issue.root_id)
1365 self.root_id = (@parent_issue.nil? ? id : @parent_issue.root_id)
1350 cond = ["root_id = ? AND lft >= ? AND rgt <= ? ", old_root_id, lft, rgt]
1366 cond = ["root_id = ? AND lft >= ? AND rgt <= ? ", old_root_id, lft, rgt]
1351 self.class.base_class.select('id').lock(true).where(cond)
1367 self.class.base_class.select('id').lock(true).where(cond)
1352 offset = right_most_bound + 1 - lft
1368 offset = rdm_right_most_bound + 1 - lft
1353 Issue.where(cond).
1369 Issue.where(cond).
1354 update_all(["root_id = ?, lft = lft + ?, rgt = rgt + ?", root_id, offset, offset])
1370 update_all(["root_id = ?, lft = lft + ?, rgt = rgt + ?", root_id, offset, offset])
1355 end
1371 end
@@ -1367,6 +1383,14 class Issue < ActiveRecord::Base
1367 recalculate_attributes_for(former_parent_id) if former_parent_id
1383 recalculate_attributes_for(former_parent_id) if former_parent_id
1368 end
1384 end
1369
1385
1386 def rdm_right_most_bound
1387 right_most_node =
1388 self.class.base_class.unscoped.
1389 order("#{quoted_right_column_full_name} desc").limit(1).lock(true).first
1390 right_most_node ? (right_most_node[right_column_name] || 0 ) : 0
1391 end
1392 private :rdm_right_most_bound
1393
1370 def update_parent_attributes
1394 def update_parent_attributes
1371 recalculate_attributes_for(parent_id) if parent_id
1395 recalculate_attributes_for(parent_id) if parent_id
1372 end
1396 end
@@ -1395,7 +1419,7 class Issue < ActiveRecord::Base
1395 end
1419 end
1396 done = p.leaves.joins(:status).
1420 done = p.leaves.joins(:status).
1397 sum("COALESCE(CASE WHEN estimated_hours > 0 THEN estimated_hours ELSE NULL END, #{average}) " +
1421 sum("COALESCE(CASE WHEN estimated_hours > 0 THEN estimated_hours ELSE NULL END, #{average}) " +
1398 "* (CASE WHEN is_closed = #{connection.quoted_true} THEN 100 ELSE COALESCE(done_ratio, 0) END)").to_f
1422 "* (CASE WHEN is_closed = #{self.class.connection.quoted_true} THEN 100 ELSE COALESCE(done_ratio, 0) END)").to_f
1399 progress = done / (average * leaves_count)
1423 progress = done / (average * leaves_count)
1400 p.done_ratio = progress.round
1424 p.done_ratio = progress.round
1401 end
1425 end
@@ -1415,7 +1439,8 class Issue < ActiveRecord::Base
1415 def self.update_versions(conditions=nil)
1439 def self.update_versions(conditions=nil)
1416 # Only need to update issues with a fixed_version from
1440 # Only need to update issues with a fixed_version from
1417 # a different project and that is not systemwide shared
1441 # a different project and that is not systemwide shared
1418 Issue.includes(:project, :fixed_version).
1442 Issue.joins(:project, :fixed_version).
1443 references(:version, :fixed_version).
1419 where("#{Issue.table_name}.fixed_version_id IS NOT NULL" +
1444 where("#{Issue.table_name}.fixed_version_id IS NOT NULL" +
1420 " AND #{Issue.table_name}.project_id <> #{Version.table_name}.project_id" +
1445 " AND #{Issue.table_name}.project_id <> #{Version.table_name}.project_id" +
1421 " AND #{Version.table_name}.sharing <> 'system'").
1446 " AND #{Version.table_name}.sharing <> 'system'").
@@ -24,6 +24,7 class IssueCategory < ActiveRecord::Base
24 validates_presence_of :name
24 validates_presence_of :name
25 validates_uniqueness_of :name, :scope => [:project_id]
25 validates_uniqueness_of :name, :scope => [:project_id]
26 validates_length_of :name, :maximum => 30
26 validates_length_of :name, :maximum => 30
27 attr_protected :id
27
28
28 safe_attributes 'name', 'assigned_to_id'
29 safe_attributes 'name', 'assigned_to_id'
29
30
@@ -32,7 +32,7 class IssueCustomField < CustomField
32 sql = super
32 sql = super
33 id_column ||= id
33 id_column ||= id
34 tracker_condition = "#{Issue.table_name}.tracker_id IN (SELECT tracker_id FROM #{table_name_prefix}custom_fields_trackers#{table_name_suffix} WHERE custom_field_id = #{id_column})"
34 tracker_condition = "#{Issue.table_name}.tracker_id IN (SELECT tracker_id FROM #{table_name_prefix}custom_fields_trackers#{table_name_suffix} WHERE custom_field_id = #{id_column})"
35 project_condition = "EXISTS (SELECT 1 FROM #{CustomField.table_name} ifa WHERE ifa.is_for_all = #{connection.quoted_true} AND ifa.id = #{id_column})" +
35 project_condition = "EXISTS (SELECT 1 FROM #{CustomField.table_name} ifa WHERE ifa.is_for_all = #{self.class.connection.quoted_true} AND ifa.id = #{id_column})" +
36 " OR #{Issue.table_name}.project_id IN (SELECT project_id FROM #{table_name_prefix}custom_fields_projects#{table_name_suffix} WHERE custom_field_id = #{id_column})"
36 " OR #{Issue.table_name}.project_id IN (SELECT project_id FROM #{table_name_prefix}custom_fields_projects#{table_name_suffix} WHERE custom_field_id = #{id_column})"
37
37
38 "((#{sql}) AND (#{tracker_condition}) AND (#{project_condition}))"
38 "((#{sql}) AND (#{tracker_condition}) AND (#{project_condition}))"
@@ -45,7 +45,9 class IssueQuery < Query
45 scope :visible, lambda {|*args|
45 scope :visible, lambda {|*args|
46 user = args.shift || User.current
46 user = args.shift || User.current
47 base = Project.allowed_to_condition(user, :view_issues, *args)
47 base = Project.allowed_to_condition(user, :view_issues, *args)
48 scope = includes(:project).where("#{table_name}.project_id IS NULL OR (#{base})")
48 scope = joins("LEFT OUTER JOIN #{Project.table_name} ON #{table_name}.project_id = #{Project.table_name}.id").
49 references(:project).
50 where("#{table_name}.project_id IS NULL OR (#{base})")
49
51
50 if user.admin?
52 if user.admin?
51 scope.where("#{table_name}.visibility <> ? OR #{table_name}.user_id = ?", VISIBILITY_PRIVATE, user.id)
53 scope.where("#{table_name}.visibility <> ? OR #{table_name}.user_id = ?", VISIBILITY_PRIVATE, user.id)
@@ -132,17 +134,17 class IssueQuery < Query
132 if project
134 if project
133 principals += project.principals.sort
135 principals += project.principals.sort
134 unless project.leaf?
136 unless project.leaf?
135 subprojects = project.descendants.visible.all
137 subprojects = project.descendants.visible.to_a
136 principals += Principal.member_of(subprojects)
138 principals += Principal.member_of(subprojects)
137 end
139 end
138 versions = project.shared_versions.all
140 versions = project.shared_versions.to_a
139 categories = project.issue_categories.all
141 categories = project.issue_categories.to_a
140 issue_custom_fields = project.all_issue_custom_fields
142 issue_custom_fields = project.all_issue_custom_fields
141 else
143 else
142 if all_projects.any?
144 if all_projects.any?
143 principals += Principal.member_of(all_projects)
145 principals += Principal.member_of(all_projects)
144 end
146 end
145 versions = Version.visible.where(:sharing => 'system').all
147 versions = Version.visible.where(:sharing => 'system').to_a
146 issue_custom_fields = IssueCustomField.where(:is_for_all => true)
148 issue_custom_fields = IssueCustomField.where(:is_for_all => true)
147 end
149 end
148 principals.uniq!
150 principals.uniq!
@@ -339,7 +341,7 class IssueQuery < Query
339 scope = scope.preload(:author)
341 scope = scope.preload(:author)
340 end
342 end
341
343
342 issues = scope.all
344 issues = scope.to_a
343
345
344 if has_column?(:spent_hours)
346 if has_column?(:spent_hours)
345 Issue.load_visible_spent_hours(issues)
347 Issue.load_visible_spent_hours(issues)
@@ -360,12 +362,13 class IssueQuery < Query
360 joins(:status, :project).
362 joins(:status, :project).
361 where(statement).
363 where(statement).
362 includes(([:status, :project] + (options[:include] || [])).uniq).
364 includes(([:status, :project] + (options[:include] || [])).uniq).
365 references(([:status, :project] + (options[:include] || [])).uniq).
363 where(options[:conditions]).
366 where(options[:conditions]).
364 order(order_option).
367 order(order_option).
365 joins(joins_for_order_statement(order_option.join(','))).
368 joins(joins_for_order_statement(order_option.join(','))).
366 limit(options[:limit]).
369 limit(options[:limit]).
367 offset(options[:offset]).
370 offset(options[:offset]).
368 find_ids
371 pluck(:id)
369 rescue ::ActiveRecord::StatementInvalid => e
372 rescue ::ActiveRecord::StatementInvalid => e
370 raise StatementInvalid.new(e.message)
373 raise StatementInvalid.new(e.message)
371 end
374 end
@@ -380,7 +383,7 class IssueQuery < Query
380 limit(options[:limit]).
383 limit(options[:limit]).
381 offset(options[:offset]).
384 offset(options[:offset]).
382 preload(:details, :user, {:issue => [:project, :author, :tracker, :status]}).
385 preload(:details, :user, {:issue => [:project, :author, :tracker, :status]}).
383 all
386 to_a
384 rescue ::ActiveRecord::StatementInvalid => e
387 rescue ::ActiveRecord::StatementInvalid => e
385 raise StatementInvalid.new(e.message)
388 raise StatementInvalid.new(e.message)
386 end
389 end
@@ -392,7 +395,8 class IssueQuery < Query
392 where(project_statement).
395 where(project_statement).
393 where(options[:conditions]).
396 where(options[:conditions]).
394 includes(:project).
397 includes(:project).
395 all
398 references(:project).
399 to_a
396 rescue ::ActiveRecord::StatementInvalid => e
400 rescue ::ActiveRecord::StatementInvalid => e
397 raise StatementInvalid.new(e.message)
401 raise StatementInvalid.new(e.message)
398 end
402 end
@@ -411,7 +415,7 class IssueQuery < Query
411 groups = Group.givable
415 groups = Group.givable
412 operator = '!' # Override the operator since we want to find by assigned_to
416 operator = '!' # Override the operator since we want to find by assigned_to
413 else
417 else
414 groups = Group.where(:id => value).all
418 groups = Group.where(:id => value).to_a
415 end
419 end
416 groups ||= []
420 groups ||= []
417
421
@@ -431,7 +435,7 class IssueQuery < Query
431 " WHERE #{Member.table_name}.project_id = #{Issue.table_name}.project_id))"
435 " WHERE #{Member.table_name}.project_id = #{Issue.table_name}.project_id))"
432 when "=", "!"
436 when "=", "!"
433 role_cond = value.any? ?
437 role_cond = value.any? ?
434 "#{MemberRole.table_name}.role_id IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")" :
438 "#{MemberRole.table_name}.role_id IN (" + value.collect{|val| "'#{self.class.connection.quote_string(val)}'"}.join(",") + ")" :
435 "1=0"
439 "1=0"
436
440
437 sw = operator == "!" ? 'NOT' : ''
441 sw = operator == "!" ? 'NOT' : ''
@@ -443,7 +447,7 class IssueQuery < Query
443
447
444 def sql_for_is_private_field(field, operator, value)
448 def sql_for_is_private_field(field, operator, value)
445 op = (operator == "=" ? 'IN' : 'NOT IN')
449 op = (operator == "=" ? 'IN' : 'NOT IN')
446 va = value.map {|v| v == '0' ? connection.quoted_false : connection.quoted_true}.uniq.join(',')
450 va = value.map {|v| v == '0' ? self.class.connection.quoted_false : self.class.connection.quoted_true}.uniq.join(',')
447
451
448 "#{Issue.table_name}.is_private #{op} (#{va})"
452 "#{Issue.table_name}.is_private #{op} (#{va})"
449 end
453 end
@@ -462,14 +466,14 class IssueQuery < Query
462 sql = case operator
466 sql = case operator
463 when "*", "!*"
467 when "*", "!*"
464 op = (operator == "*" ? 'IN' : 'NOT IN')
468 op = (operator == "*" ? 'IN' : 'NOT IN')
465 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}')"
469 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{self.class.connection.quote_string(relation_type)}')"
466 when "=", "!"
470 when "=", "!"
467 op = (operator == "=" ? 'IN' : 'NOT IN')
471 op = (operator == "=" ? 'IN' : 'NOT IN')
468 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = #{value.first.to_i})"
472 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{self.class.connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = #{value.first.to_i})"
469 when "=p", "=!p", "!p"
473 when "=p", "=!p", "!p"
470 op = (operator == "!p" ? 'NOT IN' : 'IN')
474 op = (operator == "!p" ? 'NOT IN' : 'IN')
471 comp = (operator == "=!p" ? '<>' : '=')
475 comp = (operator == "=!p" ? '<>' : '=')
472 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name}, #{Issue.table_name} relissues WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = relissues.id AND relissues.project_id #{comp} #{value.first.to_i})"
476 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name}, #{Issue.table_name} relissues WHERE #{IssueRelation.table_name}.relation_type = '#{self.class.connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = relissues.id AND relissues.project_id #{comp} #{value.first.to_i})"
473 end
477 end
474
478
475 if relation_options[:sym] == field && !options[:reverse]
479 if relation_options[:sym] == field && !options[:reverse]
@@ -27,6 +27,7 class IssueStatus < ActiveRecord::Base
27 validates_uniqueness_of :name
27 validates_uniqueness_of :name
28 validates_length_of :name, :maximum => 30
28 validates_length_of :name, :maximum => 30
29 validates_inclusion_of :default_done_ratio, :in => 0..100, :allow_nil => true
29 validates_inclusion_of :default_done_ratio, :in => 0..100, :allow_nil => true
30 attr_protected :id
30
31
31 scope :sorted, lambda { order("#{table_name}.position ASC") }
32 scope :sorted, lambda { order("#{table_name}.position ASC") }
32 scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)}
33 scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)}
@@ -79,7 +80,7 class IssueStatus < ActiveRecord::Base
79 includes(:new_status).
80 includes(:new_status).
80 where(["role_id IN (:role_ids) AND tracker_id = :tracker_id AND (#{conditions})",
81 where(["role_id IN (:role_ids) AND tracker_id = :tracker_id AND (#{conditions})",
81 {:role_ids => roles.collect(&:id), :tracker_id => tracker.id, :true => true, :false => false}
82 {:role_ids => roles.collect(&:id), :tracker_id => tracker.id, :true => true, :false => false}
82 ]).all.
83 ]).to_a.
83 map(&:new_status).compact.sort
84 map(&:new_status).compact.sort
84 else
85 else
85 []
86 []
@@ -24,6 +24,7 class Journal < ActiveRecord::Base
24 belongs_to :user
24 belongs_to :user
25 has_many :details, :class_name => "JournalDetail", :dependent => :delete_all
25 has_many :details, :class_name => "JournalDetail", :dependent => :delete_all
26 attr_accessor :indice
26 attr_accessor :indice
27 attr_protected :id
27
28
28 acts_as_event :title => Proc.new {|o| status = ((s = o.new_status) ? " (#{s})" : nil); "#{o.issue.tracker} ##{o.issue.id}#{status}: #{o.issue.subject}" },
29 acts_as_event :title => Proc.new {|o| status = ((s = o.new_status) ? " (#{s})" : nil); "#{o.issue.tracker} ##{o.issue.id}#{status}: #{o.issue.subject}" },
29 :description => :notes,
30 :description => :notes,
@@ -34,17 +35,18 class Journal < ActiveRecord::Base
34
35
35 acts_as_activity_provider :type => 'issues',
36 acts_as_activity_provider :type => 'issues',
36 :author_key => :user_id,
37 :author_key => :user_id,
37 :find_options => {:include => [{:issue => :project}, :details, :user],
38 :scope => preload({:issue => :project}, :user).
38 :conditions => "#{Journal.table_name}.journalized_type = 'Issue' AND" +
39 joins("LEFT OUTER JOIN #{JournalDetail.table_name} ON #{JournalDetail.table_name}.journal_id = #{Journal.table_name}.id").
39 " (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')"}
40 where("#{Journal.table_name}.journalized_type = 'Issue' AND" +
41 " (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')")
40
42
41 before_create :split_private_notes
43 before_create :split_private_notes
42 after_create :send_notification
44 after_create :send_notification
43
45
44 scope :visible, lambda {|*args|
46 scope :visible, lambda {|*args|
45 user = args.shift || User.current
47 user = args.shift || User.current
46
48 joins(:issue => :project).
47 includes(:issue => :project).
49 references(:project).
48 where(Issue.visible_condition(user, *args)).
50 where(Issue.visible_condition(user, *args)).
49 where("(#{Journal.table_name}.private_notes = ? OR (#{Project.allowed_to_condition(user, :view_private_notes, *args)}))", false)
51 where("(#{Journal.table_name}.private_notes = ? OR (#{Project.allowed_to_condition(user, :view_private_notes, *args)}))", false)
50 }
52 }
@@ -18,6 +18,7
18 class JournalDetail < ActiveRecord::Base
18 class JournalDetail < ActiveRecord::Base
19 belongs_to :journal
19 belongs_to :journal
20 before_save :normalize_values
20 before_save :normalize_values
21 attr_protected :id
21
22
22 def custom_field
23 def custom_field
23 if property == 'cf'
24 if property == 'cf'
@@ -42,7 +42,7 class MailHandler < ActionMailer::Base
42 @@handler_options[:no_notification] = (@@handler_options[:no_notification].to_s == '1')
42 @@handler_options[:no_notification] = (@@handler_options[:no_notification].to_s == '1')
43 @@handler_options[:no_permission_check] = (@@handler_options[:no_permission_check].to_s == '1')
43 @@handler_options[:no_permission_check] = (@@handler_options[:no_permission_check].to_s == '1')
44
44
45 email.force_encoding('ASCII-8BIT') if email.respond_to?(:force_encoding)
45 email.force_encoding('ASCII-8BIT')
46 super(email)
46 super(email)
47 end
47 end
48
48
@@ -417,7 +417,7 class MailHandler < ActionMailer::Base
417 end
417 end
418
418
419 parts.reject! do |part|
419 parts.reject! do |part|
420 part.header[:content_disposition].try(:disposition_type) == 'attachment'
420 part.attachment?
421 end
421 end
422
422
423 @plain_text_body = parts.map do |p|
423 @plain_text_body = parts.map do |p|
@@ -25,6 +25,7 class Member < ActiveRecord::Base
25 validates_presence_of :principal, :project
25 validates_presence_of :principal, :project
26 validates_uniqueness_of :user_id, :scope => :project_id
26 validates_uniqueness_of :user_id, :scope => :project_id
27 validate :validate_role
27 validate :validate_role
28 attr_protected :id
28
29
29 before_destroy :set_issue_category_nil
30 before_destroy :set_issue_category_nil
30
31
@@ -26,6 +26,7 class MemberRole < ActiveRecord::Base
26
26
27 validates_presence_of :role
27 validates_presence_of :role
28 validate :validate_role_member
28 validate :validate_role_member
29 attr_protected :id
29
30
30 def validate_role_member
31 def validate_role_member
31 errors.add :role_id, :invalid if role && !role.member?
32 errors.add :role_id, :invalid if role && !role.member?
@@ -22,9 +22,10 class Message < ActiveRecord::Base
22 acts_as_tree :counter_cache => :replies_count, :order => "#{Message.table_name}.created_on ASC"
22 acts_as_tree :counter_cache => :replies_count, :order => "#{Message.table_name}.created_on ASC"
23 acts_as_attachable
23 acts_as_attachable
24 belongs_to :last_reply, :class_name => 'Message', :foreign_key => 'last_reply_id'
24 belongs_to :last_reply, :class_name => 'Message', :foreign_key => 'last_reply_id'
25 attr_protected :id
25
26
26 acts_as_searchable :columns => ['subject', 'content'],
27 acts_as_searchable :columns => ['subject', 'content'],
27 :include => {:board => :project},
28 :scope => preload(:board => :project),
28 :project_key => "#{Board.table_name}.project_id",
29 :project_key => "#{Board.table_name}.project_id",
29 :date_column => "#{table_name}.created_on"
30 :date_column => "#{table_name}.created_on"
30 acts_as_event :title => Proc.new {|o| "#{o.board.name}: #{o.subject}"},
31 acts_as_event :title => Proc.new {|o| "#{o.board.name}: #{o.subject}"},
@@ -34,7 +35,7 class Message < ActiveRecord::Base
34 :url => Proc.new {|o| {:controller => 'messages', :action => 'show', :board_id => o.board_id}.merge(o.parent_id.nil? ? {:id => o.id} :
35 :url => Proc.new {|o| {:controller => 'messages', :action => 'show', :board_id => o.board_id}.merge(o.parent_id.nil? ? {:id => o.id} :
35 {:id => o.parent_id, :r => o.id, :anchor => "message-#{o.id}"})}
36 {:id => o.parent_id, :r => o.id, :anchor => "message-#{o.id}"})}
36
37
37 acts_as_activity_provider :find_options => {:include => [{:board => :project}, :author]},
38 acts_as_activity_provider :scope => preload({:board => :project}, :author),
38 :author_key => :author_id
39 :author_key => :author_id
39 acts_as_watchable
40 acts_as_watchable
40
41
@@ -48,7 +49,9 class Message < ActiveRecord::Base
48 after_create :send_notification
49 after_create :send_notification
49
50
50 scope :visible, lambda {|*args|
51 scope :visible, lambda {|*args|
51 includes(:board => :project).where(Project.allowed_to_condition(args.shift || User.current, :view_messages, *args))
52 joins(:board => :project).
53 references(:board => :project).
54 where(Project.allowed_to_condition(args.shift || User.current, :view_messages, *args))
52 }
55 }
53
56
54 safe_attributes 'subject', 'content'
57 safe_attributes 'subject', 'content'
@@ -19,16 +19,18 class News < ActiveRecord::Base
19 include Redmine::SafeAttributes
19 include Redmine::SafeAttributes
20 belongs_to :project
20 belongs_to :project
21 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
21 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
22 has_many :comments, :as => :commented, :dependent => :delete_all, :order => "created_on"
22 has_many :comments, lambda {order("created_on")}, :as => :commented, :dependent => :delete_all
23
23
24 validates_presence_of :title, :description
24 validates_presence_of :title, :description
25 validates_length_of :title, :maximum => 60
25 validates_length_of :title, :maximum => 60
26 validates_length_of :summary, :maximum => 255
26 validates_length_of :summary, :maximum => 255
27 attr_protected :id
27
28
28 acts_as_attachable :delete_permission => :manage_news
29 acts_as_attachable :delete_permission => :manage_news
29 acts_as_searchable :columns => ['title', 'summary', "#{table_name}.description"], :include => :project
30 acts_as_searchable :columns => ['title', 'summary', "#{table_name}.description"],
31 :scope => preload(:project)
30 acts_as_event :url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.id}}
32 acts_as_event :url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.id}}
31 acts_as_activity_provider :find_options => {:include => [:project, :author]},
33 acts_as_activity_provider :scope => preload(:project, :author),
32 :author_key => :author_id
34 :author_key => :author_id
33 acts_as_watchable
35 acts_as_watchable
34
36
@@ -36,7 +38,9 class News < ActiveRecord::Base
36 after_create :send_notification
38 after_create :send_notification
37
39
38 scope :visible, lambda {|*args|
40 scope :visible, lambda {|*args|
39 includes(:project).where(Project.allowed_to_condition(args.shift || User.current, :view_news, *args))
41 joins(:project).
42 references([:author, :project]).
43 where(Project.allowed_to_condition(args.shift || User.current, :view_news, *args))
40 }
44 }
41
45
42 safe_attributes 'title', 'summary', 'description'
46 safe_attributes 'title', 'summary', 'description'
@@ -68,7 +72,7 class News < ActiveRecord::Base
68
72
69 # returns latest news for projects visible by user
73 # returns latest news for projects visible by user
70 def self.latest(user = User.current, count = 5)
74 def self.latest(user = User.current, count = 5)
71 visible(user).includes([:author, :project]).order("#{News.table_name}.created_on DESC").limit(count).all
75 visible(user).joins([:author, :project]).order("#{News.table_name}.created_on DESC").limit(count).to_a
72 end
76 end
73
77
74 private
78 private
@@ -25,11 +25,13 class Principal < ActiveRecord::Base
25 STATUS_LOCKED = 3
25 STATUS_LOCKED = 3
26
26
27 has_many :members, :foreign_key => 'user_id', :dependent => :destroy
27 has_many :members, :foreign_key => 'user_id', :dependent => :destroy
28 has_many :memberships, :class_name => 'Member',
28 has_many :memberships,
29 :foreign_key => 'user_id',
29 lambda {preload(:project, :roles).
30 :include => [:project, :roles],
30 joins(:project).
31 :conditions => "#{Project.table_name}.status<>#{Project::STATUS_ARCHIVED}",
31 where("#{Project.table_name}.status<>#{Project::STATUS_ARCHIVED}").
32 :order => "#{Project.table_name}.name"
32 order("#{Project.table_name}.name")},
33 :class_name => 'Member',
34 :foreign_key => 'user_id'
33 has_many :projects, :through => :memberships
35 has_many :projects, :through => :memberships
34 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
36 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
35
37
@@ -56,8 +58,8 class Principal < ActiveRecord::Base
56
58
57 # Principals that are members of a collection of projects
59 # Principals that are members of a collection of projects
58 scope :member_of, lambda {|projects|
60 scope :member_of, lambda {|projects|
59 projects = [projects] unless projects.is_a?(Array)
61 projects = [projects] if projects.is_a?(Project)
60 if projects.empty?
62 if projects.blank?
61 where("1=0")
63 where("1=0")
62 else
64 else
63 ids = projects.map(&:id)
65 ids = projects.map(&:id)
@@ -28,31 +28,35 class Project < ActiveRecord::Base
28
28
29 # Specific overridden Activities
29 # Specific overridden Activities
30 has_many :time_entry_activities
30 has_many :time_entry_activities
31 has_many :members, :include => [:principal, :roles], :conditions => "#{Principal.table_name}.type='User' AND #{Principal.table_name}.status=#{Principal::STATUS_ACTIVE}"
31 has_many :members,
32 lambda { joins(:principal, :roles).
33 references(:principal, :roles).
34 where("#{Principal.table_name}.type='User' AND #{Principal.table_name}.status=#{Principal::STATUS_ACTIVE}") }
32 has_many :memberships, :class_name => 'Member'
35 has_many :memberships, :class_name => 'Member'
33 has_many :member_principals, :class_name => 'Member',
36 has_many :member_principals,
34 :include => :principal,
37 lambda { joins(:principal).
35 :conditions => "#{Principal.table_name}.status=#{Principal::STATUS_ACTIVE}"
38 references(:principal).
36
39 where("#{Principal.table_name}.status=#{Principal::STATUS_ACTIVE}")},
40 :class_name => 'Member'
37 has_many :enabled_modules, :dependent => :delete_all
41 has_many :enabled_modules, :dependent => :delete_all
38 has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
42 has_and_belongs_to_many :trackers, lambda {order("#{Tracker.table_name}.position")}
39 has_many :issues, :dependent => :destroy, :include => [:status, :tracker]
43 has_many :issues, :dependent => :destroy
40 has_many :issue_changes, :through => :issues, :source => :journals
44 has_many :issue_changes, :through => :issues, :source => :journals
41 has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC"
45 has_many :versions, lambda {order("#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC")}, :dependent => :destroy
42 has_many :time_entries, :dependent => :destroy
46 has_many :time_entries, :dependent => :destroy
43 has_many :queries, :class_name => 'IssueQuery', :dependent => :delete_all
47 has_many :queries, :class_name => 'IssueQuery', :dependent => :delete_all
44 has_many :documents, :dependent => :destroy
48 has_many :documents, :dependent => :destroy
45 has_many :news, :dependent => :destroy, :include => :author
49 has_many :news, lambda {includes(:author)}, :dependent => :destroy
46 has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name"
50 has_many :issue_categories, lambda {order("#{IssueCategory.table_name}.name")}, :dependent => :delete_all
47 has_many :boards, :dependent => :destroy, :order => "position ASC"
51 has_many :boards, lambda {order("position ASC")}, :dependent => :destroy
48 has_one :repository, :conditions => ["is_default = ?", true]
52 has_one :repository, lambda {where(["is_default = ?", true])}
49 has_many :repositories, :dependent => :destroy
53 has_many :repositories, :dependent => :destroy
50 has_many :changesets, :through => :repository
54 has_many :changesets, :through => :repository
51 has_one :wiki, :dependent => :destroy
55 has_one :wiki, :dependent => :destroy
52 # Custom field for the project issues
56 # Custom field for the project issues
53 has_and_belongs_to_many :issue_custom_fields,
57 has_and_belongs_to_many :issue_custom_fields,
58 lambda {order("#{CustomField.table_name}.position")},
54 :class_name => 'IssueCustomField',
59 :class_name => 'IssueCustomField',
55 :order => "#{CustomField.table_name}.position",
56 :join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}",
60 :join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}",
57 :association_foreign_key => 'custom_field_id'
61 :association_foreign_key => 'custom_field_id'
58
62
@@ -126,9 +130,9 class Project < ActiveRecord::Base
126 if !initialized.key?('trackers') && !initialized.key?('tracker_ids')
130 if !initialized.key?('trackers') && !initialized.key?('tracker_ids')
127 default = Setting.default_projects_tracker_ids
131 default = Setting.default_projects_tracker_ids
128 if default.is_a?(Array)
132 if default.is_a?(Array)
129 self.trackers = Tracker.where(:id => default.map(&:to_i)).sorted.all
133 self.trackers = Tracker.where(:id => default.map(&:to_i)).sorted.to_a
130 else
134 else
131 self.trackers = Tracker.sorted.all
135 self.trackers = Tracker.sorted.to_a
132 end
136 end
133 end
137 end
134 end
138 end
@@ -144,7 +148,7 class Project < ActiveRecord::Base
144 # returns latest created projects
148 # returns latest created projects
145 # non public projects will be returned only if user is a member of those
149 # non public projects will be returned only if user is a member of those
146 def self.latest(user=nil, count=5)
150 def self.latest(user=nil, count=5)
147 visible(user).limit(count).order("created_on DESC").all
151 visible(user).limit(count).order("created_on DESC").to_a
148 end
152 end
149
153
150 # Returns true if the project is visible to +user+ or to the current user.
154 # Returns true if the project is visible to +user+ or to the current user.
@@ -212,9 +216,9 class Project < ActiveRecord::Base
212 end
216 end
213
217
214 def override_roles(role)
218 def override_roles(role)
215 @override_members ||= memberships.where(:user_id => [GroupAnonymous.instance_id, GroupNonMember.instance_id]).all
219 group_class = role.anonymous? ? GroupAnonymous : GroupNonMember
216 member = @override_members.detect {|m| role.anonymous? ^ (m.user_id == GroupNonMember.instance_id)}
220 member = member_principals.where("#{Principal.table_name}.type = ?", group_class.name).first
217 member ? member.roles : [role]
221 member ? member.roles.to_a : [role]
218 end
222 end
219
223
220 def principals
224 def principals
@@ -364,7 +368,7 class Project < ActiveRecord::Base
364 # by the current user
368 # by the current user
365 def allowed_parents
369 def allowed_parents
366 return @allowed_parents if @allowed_parents
370 return @allowed_parents if @allowed_parents
367 @allowed_parents = Project.where(Project.allowed_to_condition(User.current, :add_subprojects)).all
371 @allowed_parents = Project.where(Project.allowed_to_condition(User.current, :add_subprojects)).to_a
368 @allowed_parents = @allowed_parents - self_and_descendants
372 @allowed_parents = @allowed_parents - self_and_descendants
369 if User.current.allowed_to?(:add_project, nil, :global => true) || (!new_record? && parent.nil?)
373 if User.current.allowed_to?(:add_project, nil, :global => true) || (!new_record? && parent.nil?)
370 @allowed_parents << nil
374 @allowed_parents << nil
@@ -435,11 +439,12 class Project < ActiveRecord::Base
435 @rolled_up_trackers ||=
439 @rolled_up_trackers ||=
436 Tracker.
440 Tracker.
437 joins(:projects).
441 joins(:projects).
442 references(:project).
438 joins("JOIN #{EnabledModule.table_name} ON #{EnabledModule.table_name}.project_id = #{Project.table_name}.id AND #{EnabledModule.table_name}.name = 'issue_tracking'").
443 joins("JOIN #{EnabledModule.table_name} ON #{EnabledModule.table_name}.project_id = #{Project.table_name}.id AND #{EnabledModule.table_name}.name = 'issue_tracking'").
439 select("DISTINCT #{Tracker.table_name}.*").
444 select("DISTINCT #{Tracker.table_name}.*").
440 where("#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status <> #{STATUS_ARCHIVED}", lft, rgt).
445 where("#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status <> #{STATUS_ARCHIVED}", lft, rgt).
441 sorted.
446 sorted.
442 all
447 to_a
443 end
448 end
444
449
445 # Closes open and locked project versions that are completed
450 # Closes open and locked project versions that are completed
@@ -457,7 +462,8 class Project < ActiveRecord::Base
457 def rolled_up_versions
462 def rolled_up_versions
458 @rolled_up_versions ||=
463 @rolled_up_versions ||=
459 Version.
464 Version.
460 includes(:project).
465 joins(:project).
466 references(:project).
461 where("#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status <> ?", lft, rgt, STATUS_ARCHIVED)
467 where("#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status <> ?", lft, rgt, STATUS_ARCHIVED)
462 end
468 end
463
469
@@ -465,13 +471,17 class Project < ActiveRecord::Base
465 def shared_versions
471 def shared_versions
466 if new_record?
472 if new_record?
467 Version.
473 Version.
468 includes(:project).
474 joins(:project).
475 references(:project).
476 preload(:project).
469 where("#{Project.table_name}.status <> ? AND #{Version.table_name}.sharing = 'system'", STATUS_ARCHIVED)
477 where("#{Project.table_name}.status <> ? AND #{Version.table_name}.sharing = 'system'", STATUS_ARCHIVED)
470 else
478 else
471 @shared_versions ||= begin
479 @shared_versions ||= begin
472 r = root? ? self : root
480 r = root? ? self : root
473 Version.
481 Version.
474 includes(:project).
482 joins(:project).
483 references(:project).
484 preload(:project).
475 where("#{Project.table_name}.id = #{id}" +
485 where("#{Project.table_name}.id = #{id}" +
476 " OR (#{Project.table_name}.status <> #{Project::STATUS_ARCHIVED} AND (" +
486 " OR (#{Project.table_name}.status <> #{Project::STATUS_ARCHIVED} AND (" +
477 " #{Version.table_name}.sharing = 'system'" +
487 " #{Version.table_name}.sharing = 'system'" +
@@ -497,7 +507,7 class Project < ActiveRecord::Base
497 # Deletes all project's members
507 # Deletes all project's members
498 def delete_all_members
508 def delete_all_members
499 me, mr = Member.table_name, MemberRole.table_name
509 me, mr = Member.table_name, MemberRole.table_name
500 connection.delete("DELETE FROM #{mr} WHERE #{mr}.member_id IN (SELECT #{me}.id FROM #{me} WHERE #{me}.project_id = #{id})")
510 self.class.connection.delete("DELETE FROM #{mr} WHERE #{mr}.member_id IN (SELECT #{me}.id FROM #{me} WHERE #{me}.project_id = #{id})")
501 Member.delete_all(['project_id = ?', id])
511 Member.delete_all(['project_id = ?', id])
502 end
512 end
503
513
@@ -728,7 +738,7 class Project < ActiveRecord::Base
728 project = project.is_a?(Project) ? project : Project.find(project)
738 project = project.is_a?(Project) ? project : Project.find(project)
729
739
730 to_be_copied = %w(wiki versions issue_categories issues members queries boards)
740 to_be_copied = %w(wiki versions issue_categories issues members queries boards)
731 to_be_copied = to_be_copied & options[:only].to_a unless options[:only].nil?
741 to_be_copied = to_be_copied & Array.wrap(options[:only]) unless options[:only].nil?
732
742
733 Project.transaction do
743 Project.transaction do
734 if save
744 if save
@@ -740,6 +750,7 class Project < ActiveRecord::Base
740 save
750 save
741 end
751 end
742 end
752 end
753 true
743 end
754 end
744
755
745 # Returns a new unsaved Project instance with attributes copied from +project+
756 # Returns a new unsaved Project instance with attributes copied from +project+
@@ -958,11 +969,10 class Project < ActiveRecord::Base
958 def copy_queries(project)
969 def copy_queries(project)
959 project.queries.each do |query|
970 project.queries.each do |query|
960 new_query = IssueQuery.new
971 new_query = IssueQuery.new
961 new_query.attributes = query.attributes.dup.except("id", "project_id", "sort_criteria", "user_id", "type")
972 new_query.attributes = query.attributes.dup.except("id", "project_id", "sort_criteria")
962 new_query.sort_criteria = query.sort_criteria if query.sort_criteria
973 new_query.sort_criteria = query.sort_criteria if query.sort_criteria
963 new_query.project = self
974 new_query.project = self
964 new_query.user_id = query.user_id
975 new_query.user_id = query.user_id
965 new_query.role_ids = query.role_ids if query.visibility == IssueQuery::VISIBILITY_ROLES
966 self.queries << new_query
976 self.queries << new_query
967 end
977 end
968 end
978 end
@@ -288,7 +288,7 class Query < ActiveRecord::Base
288 end
288 end
289
289
290 def trackers
290 def trackers
291 @trackers ||= project.nil? ? Tracker.sorted.all : project.rolled_up_trackers
291 @trackers ||= project.nil? ? Tracker.sorted.to_a : project.rolled_up_trackers
292 end
292 end
293
293
294 # Returns a hash of localized labels for all filter operators
294 # Returns a hash of localized labels for all filter operators
@@ -306,7 +306,7 class Query < ActiveRecord::Base
306 end
306 end
307
307
308 def all_projects
308 def all_projects
309 @all_projects ||= Project.visible.all
309 @all_projects ||= Project.visible.to_a
310 end
310 end
311
311
312 def all_projects_values
312 def all_projects_values
@@ -655,7 +655,7 class Query < ActiveRecord::Base
655 sql = "#{db_table}.#{db_field} BETWEEN #{value.first.to_f - 1e-5} AND #{value.first.to_f + 1e-5}"
655 sql = "#{db_table}.#{db_field} BETWEEN #{value.first.to_f - 1e-5} AND #{value.first.to_f + 1e-5}"
656 end
656 end
657 else
657 else
658 sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
658 sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{self.class.connection.quote_string(val)}'"}.join(",") + ")"
659 end
659 end
660 else
660 else
661 # IN an empty set
661 # IN an empty set
@@ -663,7 +663,7 class Query < ActiveRecord::Base
663 end
663 end
664 when "!"
664 when "!"
665 if value.any?
665 if value.any?
666 sql = "(#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} NOT IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + "))"
666 sql = "(#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} NOT IN (" + value.collect{|val| "'#{self.class.connection.quote_string(val)}'"}.join(",") + "))"
667 else
667 else
668 # NOT IN an empty set
668 # NOT IN an empty set
669 sql = "1=1"
669 sql = "1=1"
@@ -705,9 +705,9 class Query < ActiveRecord::Base
705 end
705 end
706 end
706 end
707 when "o"
707 when "o"
708 sql = "#{queried_table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{connection.quoted_false})" if field == "status_id"
708 sql = "#{queried_table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{self.class.connection.quoted_false})" if field == "status_id"
709 when "c"
709 when "c"
710 sql = "#{queried_table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{connection.quoted_true})" if field == "status_id"
710 sql = "#{queried_table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{self.class.connection.quoted_true})" if field == "status_id"
711 when "><t-"
711 when "><t-"
712 # between today - n days and today
712 # between today - n days and today
713 sql = relative_date_clause(db_table, db_field, - value.first.to_i, 0)
713 sql = relative_date_clause(db_table, db_field, - value.first.to_i, 0)
@@ -769,9 +769,9 class Query < ActiveRecord::Base
769 date = Date.today
769 date = Date.today
770 sql = date_clause(db_table, db_field, date.beginning_of_year, date.end_of_year)
770 sql = date_clause(db_table, db_field, date.beginning_of_year, date.end_of_year)
771 when "~"
771 when "~"
772 sql = "LOWER(#{db_table}.#{db_field}) LIKE '%#{connection.quote_string(value.first.to_s.downcase)}%'"
772 sql = "LOWER(#{db_table}.#{db_field}) LIKE '%#{self.class.connection.quote_string(value.first.to_s.downcase)}%'"
773 when "!~"
773 when "!~"
774 sql = "LOWER(#{db_table}.#{db_field}) NOT LIKE '%#{connection.quote_string(value.first.to_s.downcase)}%'"
774 sql = "LOWER(#{db_table}.#{db_field}) NOT LIKE '%#{self.class.connection.quote_string(value.first.to_s.downcase)}%'"
775 else
775 else
776 raise "Unknown query operator #{operator}"
776 raise "Unknown query operator #{operator}"
777 end
777 end
@@ -834,7 +834,7 class Query < ActiveRecord::Base
834 if self.class.default_timezone == :utc
834 if self.class.default_timezone == :utc
835 from = from.utc
835 from = from.utc
836 end
836 end
837 s << ("#{table}.#{field} > '%s'" % [connection.quoted_date(from)])
837 s << ("#{table}.#{field} > '%s'" % [self.class.connection.quoted_date(from)])
838 end
838 end
839 if to
839 if to
840 if to.is_a?(Date)
840 if to.is_a?(Date)
@@ -843,7 +843,7 class Query < ActiveRecord::Base
843 if self.class.default_timezone == :utc
843 if self.class.default_timezone == :utc
844 to = to.utc
844 to = to.utc
845 end
845 end
846 s << ("#{table}.#{field} <= '%s'" % [connection.quoted_date(to)])
846 s << ("#{table}.#{field} <= '%s'" % [self.class.connection.quoted_date(to)])
847 end
847 end
848 s.join(' AND ')
848 s.join(' AND ')
849 end
849 end
@@ -25,7 +25,7 class Repository < ActiveRecord::Base
25 IDENTIFIER_MAX_LENGTH = 255
25 IDENTIFIER_MAX_LENGTH = 255
26
26
27 belongs_to :project
27 belongs_to :project
28 has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
28 has_many :changesets, lambda{order("#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC")}
29 has_many :filechanges, :class_name => 'Change', :through => :changesets
29 has_many :filechanges, :class_name => 'Change', :through => :changesets
30
30
31 serialize :extra_info
31 serialize :extra_info
@@ -45,6 +45,7 class Repository < ActiveRecord::Base
45 validates_format_of :identifier, :with => /\A(?!\d+$)[a-z0-9\-_]*\z/, :allow_blank => true
45 validates_format_of :identifier, :with => /\A(?!\d+$)[a-z0-9\-_]*\z/, :allow_blank => true
46 # Checks if the SCM is enabled when creating a repository
46 # Checks if the SCM is enabled when creating a repository
47 validate :repo_create_validation, :on => :create
47 validate :repo_create_validation, :on => :create
48 attr_protected :id
48
49
49 safe_attributes 'identifier',
50 safe_attributes 'identifier',
50 'login',
51 'login',
@@ -264,7 +265,7 class Repository < ActiveRecord::Base
264 reorder("#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC").
265 reorder("#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC").
265 limit(limit).
266 limit(limit).
266 preload(:user).
267 preload(:user).
267 all
268 to_a
268 else
269 else
269 filechanges.
270 filechanges.
270 where("path = ?", path.with_leading_slash).
271 where("path = ?", path.with_leading_slash).
@@ -313,7 +314,8 class Repository < ActiveRecord::Base
313 return @found_committer_users[committer] if @found_committer_users.has_key?(committer)
314 return @found_committer_users[committer] if @found_committer_users.has_key?(committer)
314
315
315 user = nil
316 user = nil
316 c = changesets.where(:committer => committer).includes(:user).first
317 c = changesets.where(:committer => committer).
318 includes(:user).references(:user).first
317 if c && c.user
319 if c && c.user
318 user = c.user
320 user = c.user
319 elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
321 elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
@@ -484,10 +486,10 class Repository < ActiveRecord::Base
484 ci = "#{table_name_prefix}changesets_issues#{table_name_suffix}"
486 ci = "#{table_name_prefix}changesets_issues#{table_name_suffix}"
485 cp = "#{table_name_prefix}changeset_parents#{table_name_suffix}"
487 cp = "#{table_name_prefix}changeset_parents#{table_name_suffix}"
486
488
487 connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
489 self.class.connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
488 connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
490 self.class.connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
489 connection.delete("DELETE FROM #{cp} WHERE #{cp}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
491 self.class.connection.delete("DELETE FROM #{cp} WHERE #{cp}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
490 connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
492 self.class.connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
491 clear_extra_info_of_changesets
493 clear_extra_info_of_changesets
492 end
494 end
493
495
@@ -199,7 +199,7 class Repository::Cvs < Repository
199 # Need to retrieve existing revision numbers to sort them as integers
199 # Need to retrieve existing revision numbers to sort them as integers
200 sql = "SELECT revision FROM #{Changeset.table_name} "
200 sql = "SELECT revision FROM #{Changeset.table_name} "
201 sql << "WHERE repository_id = #{id} AND revision NOT LIKE 'tmp%'"
201 sql << "WHERE repository_id = #{id} AND revision NOT LIKE 'tmp%'"
202 @current_revision_number ||= (connection.select_values(sql).collect(&:to_i).max || 0)
202 @current_revision_number ||= (self.class.connection.select_values(sql).collect(&:to_i).max || 0)
203 @current_revision_number += 1
203 @current_revision_number += 1
204 end
204 end
205 end
205 end
@@ -241,7 +241,7 class Repository::Git < Repository
241 def latest_changesets(path,rev,limit=10)
241 def latest_changesets(path,rev,limit=10)
242 revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false)
242 revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false)
243 return [] if revisions.nil? || revisions.empty?
243 return [] if revisions.nil? || revisions.empty?
244 changesets.where(:scmid => revisions.map {|c| c.scmid}).all
244 changesets.where(:scmid => revisions.map {|c| c.scmid}).to_a
245 end
245 end
246
246
247 def clear_extra_info_of_changesets
247 def clear_extra_info_of_changesets
@@ -20,7 +20,7 require 'redmine/scm/adapters/mercurial_adapter'
20 class Repository::Mercurial < Repository
20 class Repository::Mercurial < Repository
21 # sort changesets by revision number
21 # sort changesets by revision number
22 has_many :changesets,
22 has_many :changesets,
23 :order => "#{Changeset.table_name}.id DESC",
23 lambda {order("#{Changeset.table_name}.id DESC")},
24 :foreign_key => 'repository_id'
24 :foreign_key => 'repository_id'
25
25
26 attr_protected :root_url
26 attr_protected :root_url
@@ -117,9 +117,10 class Repository::Mercurial < Repository
117 changesets.
117 changesets.
118 includes(:user).
118 includes(:user).
119 where(latest_changesets_cond(path, rev, limit)).
119 where(latest_changesets_cond(path, rev, limit)).
120 references(:user).
120 limit(limit).
121 limit(limit).
121 order("#{Changeset.table_name}.id DESC").
122 order("#{Changeset.table_name}.id DESC").
122 all
123 to_a
123 end
124 end
124
125
125 def is_short_id_in_db?
126 def is_short_id_in_db?
@@ -42,7 +42,7 class Repository::Subversion < Repository
42 revisions = scm.revisions(path, rev, nil, :limit => limit)
42 revisions = scm.revisions(path, rev, nil, :limit => limit)
43 if revisions
43 if revisions
44 identifiers = revisions.collect(&:identifier).compact
44 identifiers = revisions.collect(&:identifier).compact
45 changesets.where(:revision => identifiers).reorder("committed_on DESC").includes(:repository, :user).all
45 changesets.where(:revision => identifiers).reorder("committed_on DESC").includes(:repository, :user).to_a
46 else
46 else
47 []
47 []
48 end
48 end
@@ -166,7 +166,7 class Role < ActiveRecord::Base
166
166
167 # Find all the roles that can be given to a project member
167 # Find all the roles that can be given to a project member
168 def self.find_all_givable
168 def self.find_all_givable
169 Role.givable.all
169 Role.givable.to_a
170 end
170 end
171
171
172 # Return the builtin 'non member' role. If the role doesn't exist,
172 # Return the builtin 'non member' role. If the role doesn't exist,
@@ -86,6 +86,7 class Setting < ActiveRecord::Base
86 validates_numericality_of :value, :only_integer => true, :if => Proc.new { |setting|
86 validates_numericality_of :value, :only_integer => true, :if => Proc.new { |setting|
87 (s = @@available_settings[setting.name]) && s['format'] == 'int'
87 (s = @@available_settings[setting.name]) && s['format'] == 'int'
88 }
88 }
89 attr_protected :id
89
90
90 # Hash used to cache setting values
91 # Hash used to cache setting values
91 @cached_settings = {}
92 @cached_settings = {}
@@ -142,6 +143,7 END_SRC
142 def self.set_from_params(name, params)
143 def self.set_from_params(name, params)
143 params = params.dup
144 params = params.dup
144 params.delete_if {|v| v.blank? } if params.is_a?(Array)
145 params.delete_if {|v| v.blank? } if params.is_a?(Array)
146 params.symbolize_keys! if params.is_a?(Hash)
145
147
146 m = "#{name}_from_params"
148 m = "#{name}_from_params"
147 if respond_to? m
149 if respond_to? m
@@ -35,7 +35,7 class TimeEntry < ActiveRecord::Base
35
35
36 acts_as_activity_provider :timestamp => "#{table_name}.created_on",
36 acts_as_activity_provider :timestamp => "#{table_name}.created_on",
37 :author_key => :user_id,
37 :author_key => :user_id,
38 :find_options => {:include => :project}
38 :scope => preload(:project)
39
39
40 validates_presence_of :user_id, :activity_id, :project_id, :hours, :spent_on
40 validates_presence_of :user_id, :activity_id, :project_id, :hours, :spent_on
41 validates_numericality_of :hours, :allow_nil => true, :message => :invalid
41 validates_numericality_of :hours, :allow_nil => true, :message => :invalid
@@ -45,13 +45,19 class TimeEntry < ActiveRecord::Base
45 validate :validate_time_entry
45 validate :validate_time_entry
46
46
47 scope :visible, lambda {|*args|
47 scope :visible, lambda {|*args|
48 includes(:project).where(Project.allowed_to_condition(args.shift || User.current, :view_time_entries, *args))
48 joins(:project).
49 references(:project).
50 where(Project.allowed_to_condition(args.shift || User.current, :view_time_entries, *args))
49 }
51 }
50 scope :on_issue, lambda {|issue|
52 scope :on_issue, lambda {|issue|
51 includes(:issue).where("#{Issue.table_name}.root_id = #{issue.root_id} AND #{Issue.table_name}.lft >= #{issue.lft} AND #{Issue.table_name}.rgt <= #{issue.rgt}")
53 joins(:issue).
54 references(:issue).
55 where("#{Issue.table_name}.root_id = #{issue.root_id} AND #{Issue.table_name}.lft >= #{issue.lft} AND #{Issue.table_name}.rgt <= #{issue.rgt}")
52 }
56 }
53 scope :on_project, lambda {|project, include_subprojects|
57 scope :on_project, lambda {|project, include_subprojects|
54 includes(:project).where(project.project_condition(include_subprojects))
58 joins(:project).
59 references(:project).
60 where(project.project_condition(include_subprojects))
55 }
61 }
56 scope :spent_between, lambda {|from, to|
62 scope :spent_between, lambda {|from, to|
57 if from && to
63 if from && to
@@ -42,7 +42,7 class TimeEntryQuery < Query
42 if project
42 if project
43 principals += project.principals.sort
43 principals += project.principals.sort
44 unless project.leaf?
44 unless project.leaf?
45 subprojects = project.descendants.visible.all
45 subprojects = project.descendants.visible.to_a
46 if subprojects.any?
46 if subprojects.any?
47 add_available_filter "subproject_id",
47 add_available_filter "subproject_id",
48 :type => :list_subprojects,
48 :type => :list_subprojects,
@@ -109,7 +109,8 class TimeEntryQuery < Query
109 where(statement).
109 where(statement).
110 order(order_option).
110 order(order_option).
111 joins(joins_for_order_statement(order_option.join(','))).
111 joins(joins_for_order_statement(order_option.join(','))).
112 includes(:activity)
112 includes(:activity).
113 references(:activity)
113 end
114 end
114
115
115 def sql_for_activity_id_field(field, operator, value)
116 def sql_for_activity_id_field(field, operator, value)
@@ -18,6 +18,7
18 class Token < ActiveRecord::Base
18 class Token < ActiveRecord::Base
19 belongs_to :user
19 belongs_to :user
20 validates_uniqueness_of :value
20 validates_uniqueness_of :value
21 attr_protected :id
21
22
22 before_create :delete_previous_tokens, :generate_new_token
23 before_create :delete_previous_tokens, :generate_new_token
23
24
@@ -63,7 +63,7 class Tracker < ActiveRecord::Base
63 connection.select_rows("SELECT DISTINCT old_status_id, new_status_id FROM #{WorkflowTransition.table_name} WHERE tracker_id = #{id} AND type = 'WorkflowTransition'").
63 connection.select_rows("SELECT DISTINCT old_status_id, new_status_id FROM #{WorkflowTransition.table_name} WHERE tracker_id = #{id} AND type = 'WorkflowTransition'").
64 flatten.
64 flatten.
65 uniq
65 uniq
66 @issue_statuses = IssueStatus.where(:id => ids).all.sort
66 @issue_statuses = IssueStatus.where(:id => ids).to_a.sort
67 end
67 end
68
68
69 def disabled_core_fields
69 def disabled_core_fields
@@ -92,7 +92,7 class Tracker < ActiveRecord::Base
92 # Returns the fields that are disabled for all the given trackers
92 # Returns the fields that are disabled for all the given trackers
93 def self.disabled_core_fields(trackers)
93 def self.disabled_core_fields(trackers)
94 if trackers.present?
94 if trackers.present?
95 trackers.uniq.map(&:disabled_core_fields).reduce(:&)
95 trackers.map(&:disabled_core_fields).reduce(:&)
96 else
96 else
97 []
97 []
98 end
98 end
@@ -79,8 +79,8 class User < Principal
79 :after_remove => Proc.new {|user, group| group.user_removed(user)}
79 :after_remove => Proc.new {|user, group| group.user_removed(user)}
80 has_many :changesets, :dependent => :nullify
80 has_many :changesets, :dependent => :nullify
81 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
81 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
82 has_one :rss_token, :class_name => 'Token', :conditions => "action='feeds'"
82 has_one :rss_token, lambda {where "action='feeds'"}, :class_name => 'Token'
83 has_one :api_token, :class_name => 'Token', :conditions => "action='api'"
83 has_one :api_token, lambda {where "action='api'"}, :class_name => 'Token'
84 belongs_to :auth_source
84 belongs_to :auth_source
85
85
86 scope :logged, lambda { where("#{User.table_name}.status <> #{STATUS_ANONYMOUS}") }
86 scope :logged, lambda { where("#{User.table_name}.status <> #{STATUS_ANONYMOUS}") }
@@ -105,9 +105,13 class User < Principal
105 validates_length_of :firstname, :lastname, :maximum => 30
105 validates_length_of :firstname, :lastname, :maximum => 30
106 validates_format_of :mail, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :allow_blank => true
106 validates_format_of :mail, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :allow_blank => true
107 validates_length_of :mail, :maximum => MAIL_LENGTH_LIMIT, :allow_nil => true
107 validates_length_of :mail, :maximum => MAIL_LENGTH_LIMIT, :allow_nil => true
108 validates_confirmation_of :password, :allow_nil => true
109 validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
108 validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
110 validate :validate_password_length
109 validate :validate_password_length
110 validate do
111 if password_confirmation && password != password_confirmation
112 errors.add(:password, :confirmation)
113 end
114 end
111
115
112 before_create :set_mail_notification
116 before_create :set_mail_notification
113 before_save :generate_password_if_needed, :update_hashed_password
117 before_save :generate_password_if_needed, :update_hashed_password
@@ -151,6 +155,15 class User < Principal
151 write_attribute(:mail, arg.to_s.strip)
155 write_attribute(:mail, arg.to_s.strip)
152 end
156 end
153
157
158 def self.find_or_initialize_by_identity_url(url)
159 user = where(:identity_url => url).first
160 unless user
161 user = User.new
162 user.identity_url = url
163 end
164 user
165 end
166
154 def identity_url=(url)
167 def identity_url=(url)
155 if url.blank?
168 if url.blank?
156 write_attribute(:identity_url, '')
169 write_attribute(:identity_url, '')
@@ -496,10 +509,12 class User < Principal
496
509
497 hash = Hash.new([])
510 hash = Hash.new([])
498
511
499 members = Member.joins(:project).
512 group_class = anonymous? ? GroupAnonymous : GroupNonMember
513 members = Member.joins(:project, :principal).
500 where("#{Project.table_name}.status <> 9").
514 where("#{Project.table_name}.status <> 9").
501 where("#{Member.table_name}.user_id = ? OR (#{Project.table_name}.is_public = ? AND #{Member.table_name}.user_id = ?)", self.id, true, Group.builtin_id(self)).
515 where("#{Member.table_name}.user_id = ? OR (#{Project.table_name}.is_public = ? AND #{Principal.table_name}.type = ?)", self.id, true, group_class.name).
502 preload(:project, :roles)
516 preload(:project, :roles).
517 to_a
503
518
504 members.reject! {|member| member.user_id != id && project_ids.include?(member.project_id)}
519 members.reject! {|member| member.user_id != id && project_ids.include?(member.project_id)}
505 members.each do |member|
520 members.each do |member|
@@ -558,6 +573,8 class User < Principal
558 # Authorize if user is authorized on every element of the array
573 # Authorize if user is authorized on every element of the array
559 context.map {|project| allowed_to?(action, project, options, &block)}.reduce(:&)
574 context.map {|project| allowed_to?(action, project, options, &block)}.reduce(:&)
560 end
575 end
576 elsif context
577 raise ArgumentError.new("#allowed_to? context argument must be a Project, an Array of projects or nil")
561 elsif options[:global]
578 elsif options[:global]
562 # Admin users are always authorized
579 # Admin users are always authorized
563 return true if admin?
580 return true if admin?
@@ -710,17 +727,17 class User < Principal
710 return if self.id.nil?
727 return if self.id.nil?
711
728
712 substitute = User.anonymous
729 substitute = User.anonymous
713 Attachment.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
730 Attachment.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
714 Comment.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
731 Comment.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
715 Issue.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
732 Issue.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
716 Issue.where(['assigned_to_id = ?', id]).update_all('assigned_to_id = NULL')
733 Issue.where(['assigned_to_id = ?', id]).update_all('assigned_to_id = NULL')
717 Journal.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
734 Journal.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
718 JournalDetail.
735 JournalDetail.
719 where(["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s]).
736 where(["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s]).
720 update_all(['old_value = ?', substitute.id.to_s])
737 update_all(['old_value = ?', substitute.id.to_s])
721 JournalDetail.
738 JournalDetail.
722 where(["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s]).
739 where(["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s]).
723 update_all(['value = ?', substitute.id.to_s])
740 update_all(['value = ?', substitute.id.to_s])
724 Message.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
741 Message.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
725 News.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
742 News.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
726 # Remove private queries and keep public ones
743 # Remove private queries and keep public ones
@@ -33,11 +33,14 class Version < ActiveRecord::Base
33 validates :effective_date, :date => true
33 validates :effective_date, :date => true
34 validates_inclusion_of :status, :in => VERSION_STATUSES
34 validates_inclusion_of :status, :in => VERSION_STATUSES
35 validates_inclusion_of :sharing, :in => VERSION_SHARINGS
35 validates_inclusion_of :sharing, :in => VERSION_SHARINGS
36 attr_protected :id
36
37
37 scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)}
38 scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)}
38 scope :open, lambda { where(:status => 'open') }
39 scope :open, lambda { where(:status => 'open') }
39 scope :visible, lambda {|*args|
40 scope :visible, lambda {|*args|
40 includes(:project).where(Project.allowed_to_condition(args.first || User.current, :view_issues))
41 joins(:project).
42 references(:project).
43 where(Project.allowed_to_condition(args.first || User.current, :view_issues))
41 }
44 }
42
45
43 safe_attributes 'name',
46 safe_attributes 'name',
@@ -230,11 +233,6 class Version < ActiveRecord::Base
230 end
233 end
231 end
234 end
232
235
233 # Returns true if the version is shared, otherwise false
234 def shared?
235 sharing != 'none'
236 end
237
238 private
236 private
239
237
240 def load_issue_counts
238 def load_issue_counts
@@ -22,6 +22,7 class Watcher < ActiveRecord::Base
22 validates_presence_of :user
22 validates_presence_of :user
23 validates_uniqueness_of :user_id, :scope => [:watchable_type, :watchable_id]
23 validates_uniqueness_of :user_id, :scope => [:watchable_type, :watchable_id]
24 validate :validate_user
24 validate :validate_user
25 attr_protected :id
25
26
26 # Returns true if at least one object among objects is watched by user
27 # Returns true if at least one object among objects is watched by user
27 def self.any_watched?(objects, user)
28 def self.any_watched?(objects, user)
@@ -18,13 +18,14
18 class Wiki < ActiveRecord::Base
18 class Wiki < ActiveRecord::Base
19 include Redmine::SafeAttributes
19 include Redmine::SafeAttributes
20 belongs_to :project
20 belongs_to :project
21 has_many :pages, :class_name => 'WikiPage', :dependent => :destroy, :order => 'title'
21 has_many :pages, lambda {order('title')}, :class_name => 'WikiPage', :dependent => :destroy
22 has_many :redirects, :class_name => 'WikiRedirect', :dependent => :delete_all
22 has_many :redirects, :class_name => 'WikiRedirect', :dependent => :delete_all
23
23
24 acts_as_watchable
24 acts_as_watchable
25
25
26 validates_presence_of :start_page
26 validates_presence_of :start_page
27 validates_format_of :start_page, :with => /\A[^,\.\/\?\;\|\:]*\z/
27 validates_format_of :start_page, :with => /\A[^,\.\/\?\;\|\:]*\z/
28 attr_protected :id
28
29
29 safe_attributes 'start_page'
30 safe_attributes 'start_page'
30
31
@@ -23,6 +23,7 class WikiContent < ActiveRecord::Base
23 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
23 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
24 validates_presence_of :text
24 validates_presence_of :text
25 validates_length_of :comments, :maximum => 255, :allow_nil => true
25 validates_length_of :comments, :maximum => 255, :allow_nil => true
26 attr_protected :id
26
27
27 acts_as_versioned
28 acts_as_versioned
28
29
@@ -68,13 +69,13 class WikiContent < ActiveRecord::Base
68 :timestamp => "#{WikiContent.versioned_table_name}.updated_on",
69 :timestamp => "#{WikiContent.versioned_table_name}.updated_on",
69 :author_key => "#{WikiContent.versioned_table_name}.author_id",
70 :author_key => "#{WikiContent.versioned_table_name}.author_id",
70 :permission => :view_wiki_edits,
71 :permission => :view_wiki_edits,
71 :find_options => {:select => "#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
72 :scope => select("#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
72 "#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title, " +
73 "#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title, " +
73 "#{WikiContent.versioned_table_name}.page_id, #{WikiContent.versioned_table_name}.author_id, " +
74 "#{WikiContent.versioned_table_name}.page_id, #{WikiContent.versioned_table_name}.author_id, " +
74 "#{WikiContent.versioned_table_name}.id",
75 "#{WikiContent.versioned_table_name}.id").
75 :joins => "LEFT JOIN #{WikiPage.table_name} ON #{WikiPage.table_name}.id = #{WikiContent.versioned_table_name}.page_id " +
76 joins("LEFT JOIN #{WikiPage.table_name} ON #{WikiPage.table_name}.id = #{WikiContent.versioned_table_name}.page_id " +
76 "LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id " +
77 "LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id " +
77 "LEFT JOIN #{Project.table_name} ON #{Project.table_name}.id = #{Wiki.table_name}.project_id"}
78 "LEFT JOIN #{Project.table_name} ON #{Project.table_name}.id = #{Wiki.table_name}.project_id")
78
79
79 after_destroy :page_update_after_destroy
80 after_destroy :page_update_after_destroy
80
81
@@ -104,7 +105,7 class WikiContent < ActiveRecord::Base
104 # uncompressed data
105 # uncompressed data
105 data
106 data
106 end
107 end
107 str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
108 str.force_encoding("UTF-8")
108 str
109 str
109 end
110 end
110 end
111 end
@@ -33,7 +33,7 class WikiPage < ActiveRecord::Base
33 :url => Proc.new {|o| {:controller => 'wiki', :action => 'show', :project_id => o.wiki.project, :id => o.title}}
33 :url => Proc.new {|o| {:controller => 'wiki', :action => 'show', :project_id => o.wiki.project, :id => o.title}}
34
34
35 acts_as_searchable :columns => ['title', "#{WikiContent.table_name}.text"],
35 acts_as_searchable :columns => ['title', "#{WikiContent.table_name}.text"],
36 :include => [{:wiki => :project}, :content],
36 :scope => preload(:wiki => :project).joins(:content, {:wiki => :project}),
37 :permission => :view_wiki_pages,
37 :permission => :view_wiki_pages,
38 :project_key => "#{Wiki.table_name}.project_id"
38 :project_key => "#{Wiki.table_name}.project_id"
39
39
@@ -43,6 +43,7 class WikiPage < ActiveRecord::Base
43 validates_format_of :title, :with => /\A[^,\.\/\?\;\|\s]*\z/
43 validates_format_of :title, :with => /\A[^,\.\/\?\;\|\s]*\z/
44 validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false
44 validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false
45 validates_associated :content
45 validates_associated :content
46 attr_protected :id
46
47
47 validate :validate_parent_title
48 validate :validate_parent_title
48 before_destroy :remove_redirects
49 before_destroy :remove_redirects
@@ -180,12 +181,10 class WikiPage < ActiveRecord::Base
180 def save_with_content(content)
181 def save_with_content(content)
181 ret = nil
182 ret = nil
182 transaction do
183 transaction do
183 self.content = content
184 ret = save
184 if new_record?
185 if content.text_changed?
185 # Rails automatically saves associated content
186 self.content = content
186 ret = save
187 ret = ret && content.changed?
187 else
188 ret = save && (content.text_changed? ? content.save : true)
189 end
188 end
190 raise ActiveRecord::Rollback unless ret
189 raise ActiveRecord::Rollback unless ret
191 end
190 end
@@ -20,4 +20,5 class WikiRedirect < ActiveRecord::Base
20
20
21 validates_presence_of :title, :redirects_to
21 validates_presence_of :title, :redirects_to
22 validates_length_of :title, :redirects_to, :maximum => 255
22 validates_length_of :title, :redirects_to, :maximum => 255
23 attr_protected :id
23 end
24 end
@@ -24,6 +24,7 class WorkflowRule < ActiveRecord::Base
24 belongs_to :new_status, :class_name => 'IssueStatus', :foreign_key => 'new_status_id'
24 belongs_to :new_status, :class_name => 'IssueStatus', :foreign_key => 'new_status_id'
25
25
26 validates_presence_of :role, :tracker, :old_status
26 validates_presence_of :role, :tracker, :old_status
27 attr_protected :id
27
28
28 # Copies workflows from source to targets
29 # Copies workflows from source to targets
29 def self.copy(source_tracker, source_role, target_trackers, target_roles)
30 def self.copy(source_tracker, source_role, target_trackers, target_roles)
@@ -34,7 +35,7 class WorkflowRule < ActiveRecord::Base
34 target_trackers = [target_trackers].flatten.compact
35 target_trackers = [target_trackers].flatten.compact
35 target_roles = [target_roles].flatten.compact
36 target_roles = [target_roles].flatten.compact
36
37
37 target_trackers = Tracker.sorted.all if target_trackers.empty?
38 target_trackers = Tracker.sorted.to_a if target_trackers.empty?
38 target_roles = Role.all if target_roles.empty?
39 target_roles = Role.all if target_roles.empty?
39
40
40 target_trackers.each do |target_tracker|
41 target_trackers.each do |target_tracker|
@@ -40,7 +40,7 class WorkflowTransition < WorkflowRule
40 roles = Array.wrap roles
40 roles = Array.wrap roles
41
41
42 transaction do
42 transaction do
43 records = WorkflowTransition.where(:tracker_id => trackers.map(&:id), :role_id => roles.map(&:id)).all
43 records = WorkflowTransition.where(:tracker_id => trackers.map(&:id), :role_id => roles.map(&:id)).to_a
44
44
45 transitions.each do |old_status_id, transitions_by_new_status|
45 transitions.each do |old_status_id, transitions_by_new_status|
46 transitions_by_new_status.each do |new_status_id, transition_by_rule|
46 transitions_by_new_status.each do |new_status_id, transition_by_rule|
@@ -8,8 +8,8
8 <% end -%>
8 <% end -%>
9 </ul>
9 </ul>
10 <div class="tabs-buttons" style="display:none;">
10 <div class="tabs-buttons" style="display:none;">
11 <button class="tab-left" type="button" onclick="moveTabLeft(this);"></button>
11 <button class="tab-left" onclick="moveTabLeft(this); return false;"></button>
12 <button class="tab-right" type="button" onclick="moveTabRight(this);"></button>
12 <button class="tab-right" onclick="moveTabRight(this); return false;"></button>
13 </div>
13 </div>
14 </div>
14 </div>
15
15
@@ -1,5 +1,5
1 <% roles = Role.find_all_givable %>
1 <% roles = Role.find_all_givable %>
2 <% projects = Project.active.all %>
2 <% projects = Project.active.to_a %>
3
3
4 <div class="splitcontentleft">
4 <div class="splitcontentleft">
5 <% if @group.memberships.any? %>
5 <% if @group.memberships.any? %>
@@ -1,6 +1,6
1 <%= error_messages_for 'member' %>
1 <%= error_messages_for 'member' %>
2 <% roles = Role.find_all_givable
2 <% roles = Role.find_all_givable
3 members = @project.member_principals.includes(:member_roles, :roles, :principal).all.sort %>
3 members = @project.member_principals.includes(:member_roles, :roles, :principal).to_a.sort %>
4
4
5 <div class="splitcontentleft">
5 <div class="splitcontentleft">
6 <% if members.any? %>
6 <% if members.any? %>
@@ -8,7 +8,7
8 <% elsif params[:issue_id] %>
8 <% elsif params[:issue_id] %>
9 <%= hidden_field_tag 'issue_id', params[:issue_id] %>
9 <%= hidden_field_tag 'issue_id', params[:issue_id] %>
10 <% else %>
10 <% else %>
11 <p><%= f.select :project_id, project_tree_options_for_select(Project.allowed_to(:log_time).all, :selected => @time_entry.project, :include_blank => true) %></p>
11 <p><%= f.select :project_id, project_tree_options_for_select(Project.allowed_to(:log_time).to_a, :selected => @time_entry.project, :include_blank => true) %></p>
12 <% end %>
12 <% end %>
13 <% end %>
13 <% end %>
14 <p>
14 <p>
@@ -1,5 +1,5
1 <% roles = Role.find_all_givable %>
1 <% roles = Role.find_all_givable %>
2 <% projects = Project.active.all %>
2 <% projects = Project.active.to_a %>
3
3
4 <div class="splitcontentleft">
4 <div class="splitcontentleft">
5 <% if @user.memberships.any? %>
5 <% if @user.memberships.any? %>
@@ -23,7 +23,7
23 <%= fp.select :parent_id,
23 <%= fp.select :parent_id,
24 content_tag('option', '', :value => '') +
24 content_tag('option', '', :value => '') +
25 wiki_page_options_for_select(
25 wiki_page_options_for_select(
26 @wiki.pages.includes(:parent).all -
26 @wiki.pages.includes(:parent).to_a -
27 @page.self_and_descendants, @page.parent) %>
27 @page.self_and_descendants, @page.parent) %>
28 </p>
28 </p>
29 <% end %>
29 <% end %>
@@ -13,7 +13,7
13 <p><%= f.select :parent_id,
13 <p><%= f.select :parent_id,
14 content_tag('option', '', :value => '') +
14 content_tag('option', '', :value => '') +
15 wiki_page_options_for_select(
15 wiki_page_options_for_select(
16 @wiki.pages.includes(:parent).all - @page.self_and_descendants,
16 @wiki.pages.includes(:parent).to_a - @page.self_and_descendants,
17 @page.parent),
17 @page.parent),
18 :label => :field_parent_title %></p>
18 :label => :field_parent_title %></p>
19 </div>
19 </div>
@@ -2,12 +2,7 require File.expand_path('../boot', __FILE__)
2
2
3 require 'rails/all'
3 require 'rails/all'
4
4
5 if defined?(Bundler)
5 Bundler.require(*Rails.groups)
6 # If you precompile assets before deploying to production, use this line
7 Bundler.require(*Rails.groups(:assets => %w(development test)))
8 # If you want your assets lazily compiled in production, use this line
9 # Bundler.require(:default, :assets, Rails.env)
10 end
11
6
12 module RedmineApp
7 module RedmineApp
13 class Application < Rails::Application
8 class Application < Rails::Application
@@ -33,7 +28,7 module RedmineApp
33 # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
28 # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
34 # config.i18n.default_locale = :de
29 # config.i18n.default_locale = :de
35
30
36 I18n.enforce_available_locales = false
31 I18n.enforce_available_locales = true
37
32
38 # Configure the default encoding used in templates for Ruby 1.9.
33 # Configure the default encoding used in templates for Ruby 1.9.
39 config.encoding = "utf-8"
34 config.encoding = "utf-8"
@@ -1,5 +1,3
1 require 'rubygems'
2
3 # Set up gems listed in the Gemfile.
1 # Set up gems listed in the Gemfile.
4 ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
2 ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
5
3
@@ -1,5 +1,4
1 # Default setup is given for MySQL with ruby1.9. If you're running Redmine
1 # Default setup is given for MySQL with ruby1.9.
2 # with MySQL and ruby1.8, replace the adapter name with `mysql`.
3 # Examples for PostgreSQL, SQLite3 and SQL Server can be found at the end.
2 # Examples for PostgreSQL, SQLite3 and SQL Server can be found at the end.
4 # Line indentation must be 2 spaces (no tabs).
3 # Line indentation must be 2 spaces (no tabs).
5
4
@@ -1,4 +1,4
1 # Load the rails application
1 # Load the Rails application
2 require File.expand_path('../application', __FILE__)
2 require File.expand_path('../application', __FILE__)
3
3
4 # Make sure there's no plugin in vendor/plugin before starting
4 # Make sure there's no plugin in vendor/plugin before starting
@@ -10,5 +10,5 if Dir.glob(File.join(vendor_plugins_dir, "*")).any?
10 exit 1
10 exit 1
11 end
11 end
12
12
13 # Initialize the rails application
13 # Initialize the Rails application
14 RedmineApp::Application.initialize!
14 Rails.application.initialize!
@@ -1,19 +1,21
1 # Settings specified here will take precedence over those in config/application.rb
1 Rails.application.configure do
2 RedmineApp::Application.configure do
2 # Settings specified here will take precedence over those in config/application.rb
3
3 # In the development environment your application's code is reloaded on
4 # In the development environment your application's code is reloaded on
4 # every request. This slows down response time but is perfect for development
5 # every request. This slows down response time but is perfect for development
5 # since you don't have to restart the webserver when you make code changes.
6 # since you don't have to restart the webserver when you make code changes.
6 config.cache_classes = false
7 config.cache_classes = false
7
8
8 # Log error messages when you accidentally call methods on nil.
9 # Do not eager load code on boot.
9 config.whiny_nils = true
10 config.eager_load = false
10
11
11 # Show full error reports and disable caching
12 # Show full error reports and disable caching
12 #config.action_controller.consider_all_requests_local = true
13 config.consider_all_requests_local = true
13 config.action_controller.perform_caching = false
14 config.action_controller.perform_caching = false
14
15
15 # Don't care if the mailer can't send
16 # Disable delivery errors
16 config.action_mailer.raise_delivery_errors = false
17 config.action_mailer.raise_delivery_errors = false
17
18
19 # Print deprecation notices to stderr and the Rails logger.
18 config.active_support.deprecation = [:stderr, :log]
20 config.active_support.deprecation = [:stderr, :log]
19 end
21 end
@@ -1,33 +1,25
1 # Settings specified here will take precedence over those in config/application.rb
1 Rails.application.configure do
2 RedmineApp::Application.configure do
2 # Settings specified here will take precedence over those in config/application.rb
3 # The production environment is meant for finished, "live" apps.
3
4 # Code is not reloaded between requests
4 # Code is not reloaded between requests.
5 config.cache_classes = true
5 config.cache_classes = true
6
6
7 #####
7 # Eager load code on boot. This eager loads most of Rails and
8 # Customize the default logger
8 # your application in memory, allowing both threaded web servers
9 # http://www.ruby-doc.org/stdlib-1.8.7/libdoc/logger/rdoc/Logger.html
9 # and those relying on copy on write to perform better.
10 #
10 # Rake tasks automatically ignore this option for performance.
11 # Use a different logger for distributed setups
11 config.eager_load = true
12 # config.logger = SyslogLogger.new
13 #
14 # Rotate logs bigger than 1MB, keeps no more than 7 rotated logs around.
15 # When setting a new Logger, make sure to set it's log level too.
16 #
17 # config.logger = Logger.new(config.log_path, 7, 1048576)
18 # config.logger.level = Logger::INFO
19
12
20 # Full error reports are disabled and caching is turned on
13 # Full error reports are disabled and caching is turned on.
14 config.consider_all_requests_local = false
21 config.action_controller.perform_caching = true
15 config.action_controller.perform_caching = true
22
16
23 # Enable serving of images, stylesheets, and javascripts from an asset server
17 # Disable delivery errors
24 # config.action_controller.asset_host = "http://assets.example.com"
25
26 # Disable delivery errors if you bad email addresses should just be ignored
27 config.action_mailer.raise_delivery_errors = false
18 config.action_mailer.raise_delivery_errors = false
28
19
29 # No email in production log
20 # No email in production log
30 config.action_mailer.logger = nil
21 config.action_mailer.logger = nil
31
22
23 # Print deprecation notices to the Rails logger.
32 config.active_support.deprecation = :log
24 config.active_support.deprecation = :log
33 end
25 end
@@ -1,16 +1,19
1 # Settings specified here will take precedence over those in config/application.rb
1 Rails.application.configure do
2 RedmineApp::Application.configure do
2 # Settings specified here will take precedence over those in config/application.rb
3
3 # The test environment is used exclusively to run your application's
4 # The test environment is used exclusively to run your application's
4 # test suite. You never need to work with it otherwise. Remember that
5 # test suite. You never need to work with it otherwise. Remember that
5 # your test database is "scratch space" for the test suite and is wiped
6 # your test database is "scratch space" for the test suite and is wiped
6 # and recreated between test runs. Don't rely on the data there!
7 # and recreated between test runs. Don't rely on the data there!
7 config.cache_classes = true
8 config.cache_classes = true
8
9
9 # Log error messages when you accidentally call methods on nil.
10 # Do not eager load code on boot. This avoids loading your whole application
10 config.whiny_nils = true
11 # just for the purpose of running a single test. If you are using a tool that
12 # preloads Rails for running tests, you may have to set it to true.
13 config.eager_load = false
11
14
12 # Show full error reports and disable caching
15 # Show full error reports and disable caching
13 #config.action_controller.consider_all_requests_local = true
16 config.consider_all_requests_local = true
14 config.action_controller.perform_caching = false
17 config.action_controller.perform_caching = false
15
18
16 config.action_mailer.perform_deliveries = true
19 config.action_mailer.perform_deliveries = true
@@ -20,10 +23,10 RedmineApp::Application.configure do
20 # ActionMailer::Base.deliveries array.
23 # ActionMailer::Base.deliveries array.
21 config.action_mailer.delivery_method = :test
24 config.action_mailer.delivery_method = :test
22
25
23 # Skip protect_from_forgery in requests
26 # Disable request forgery protection in test environment.
24 # http://m.onkey.org/2007/9/28/csrf-protection-for-your-existing-rails-application
25 config.action_controller.allow_forgery_protection = false
27 config.action_controller.allow_forgery_protection = false
26
28
29 # Print deprecation notices to stderr and the Rails logger.
27 config.active_support.deprecation = [:stderr, :log]
30 config.active_support.deprecation = [:stderr, :log]
28
31
29 config.secret_token = 'a secret token for running the tests'
32 config.secret_token = 'a secret token for running the tests'
@@ -1,1 +1,2
1 # Same as test.rb
1 instance_eval File.read(File.join(File.dirname(__FILE__), 'test.rb'))
2 instance_eval File.read(File.join(File.dirname(__FILE__), 'test.rb'))
@@ -1,1 +1,2
1 # Same as test.rb
1 instance_eval File.read(File.join(File.dirname(__FILE__), 'test.rb'))
2 instance_eval File.read(File.join(File.dirname(__FILE__), 'test.rb'))
@@ -54,56 +54,23 module ActionView
54 end
54 end
55 end
55 end
56
56
57 # Do not HTML escape text templates
58 module ActionView
59 class Template
60 module Handlers
61 class ERB
62 def call(template)
63 if template.source.encoding_aware?
64 # First, convert to BINARY, so in case the encoding is
65 # wrong, we can still find an encoding tag
66 # (<%# encoding %>) inside the String using a regular
67 # expression
68 template_source = template.source.dup.force_encoding("BINARY")
69
70 erb = template_source.gsub(ENCODING_TAG, '')
71 encoding = $2
72
73 erb.force_encoding valid_encoding(template.source.dup, encoding)
74
75 # Always make sure we return a String in the default_internal
76 erb.encode!
77 else
78 erb = template.source.dup
79 end
80
81 self.class.erb_implementation.new(
82 erb,
83 :trim => (self.class.erb_trim_mode == "-"),
84 :escape => template.identifier =~ /\.text/ # only escape HTML templates
85 ).src
86 end
87 end
88 end
89 end
90 end
91
92 ActionView::Base.field_error_proc = Proc.new{ |html_tag, instance| html_tag || ''.html_safe }
57 ActionView::Base.field_error_proc = Proc.new{ |html_tag, instance| html_tag || ''.html_safe }
93
58
94 # HTML5: <option value=""></option> is invalid, use <option value="">&nbsp;</option> instead
59 # HTML5: <option value=""></option> is invalid, use <option value="">&nbsp;</option> instead
95 module ActionView
60 module ActionView
96 module Helpers
61 module Helpers
97 class InstanceTag
62 module Tags
98 private
63 class Base
99 def add_options_with_non_empty_blank_option(option_tags, options, value = nil)
64 private
100 if options[:include_blank] == true
65 def add_options_with_non_empty_blank_option(option_tags, options, value = nil)
101 options = options.dup
66 if options[:include_blank] == true
102 options[:include_blank] = '&nbsp;'.html_safe
67 options = options.dup
68 options[:include_blank] = '&nbsp;'.html_safe
69 end
70 add_options_without_non_empty_blank_option(option_tags, options, value)
103 end
71 end
104 add_options_without_non_empty_blank_option(option_tags, options, value)
72 alias_method_chain :add_options, :non_empty_blank_option
105 end
73 end
106 alias_method_chain :add_options, :non_empty_blank_option
107 end
74 end
108
75
109 module FormTagHelper
76 module FormTagHelper
@@ -1,5 +1,7
1 I18n.default_locale = 'en'
1 I18n.default_locale = 'en'
2 I18n.backend = Redmine::I18n::Backend.new
2 I18n.backend = Redmine::I18n::Backend.new
3 # Forces I18n to load available locales from the backend
4 I18n.config.available_locales = nil
3
5
4 require 'redmine'
6 require 'redmine'
5
7
@@ -20,9 +22,3 Redmine::Plugin.load
20 unless Redmine::Configuration['mirror_plugins_assets_on_startup'] == false
22 unless Redmine::Configuration['mirror_plugins_assets_on_startup'] == false
21 Redmine::Plugin.mirror_assets
23 Redmine::Plugin.mirror_assets
22 end
24 end
23
24 Rails.application.config.to_prepare do
25 Redmine::FieldFormat::RecordList.subclasses.each do |klass|
26 klass.instance.reset_target_class
27 end
28 end No newline at end of file
@@ -249,8 +249,8 ja:
249 field_is_for_all: 全プロジェクト向け
249 field_is_for_all: 全プロジェクト向け
250 field_possible_values: 選択肢
250 field_possible_values: 選択肢
251 field_regexp: 正規表現
251 field_regexp: 正規表現
252 field_min_length: 短長
252 field_min_length: 小値
253 field_max_length: 最大
253 field_max_length: 最大
254 field_value:
254 field_value:
255 field_category: カテゴリ
255 field_category: カテゴリ
256 field_title: タイトル
256 field_title: タイトル
@@ -305,7 +305,7 ja:
305 field_activity: 活動
305 field_activity: 活動
306 field_spent_on: 日付
306 field_spent_on: 日付
307 field_identifier: 識別子
307 field_identifier: 識別子
308 field_is_filter: フィルタとして使
308 field_is_filter: フィルタとして使
309 field_issue_to: 関連するチケット
309 field_issue_to: 関連するチケット
310 field_delay: 遅延
310 field_delay: 遅延
311 field_assignable: このロールにチケットを割り当て可能
311 field_assignable: このロールにチケットを割り当て可能
@@ -314,7 +314,7 ja:
314 field_column_names: 項目
314 field_column_names: 項目
315 field_time_entries: 時間を記録
315 field_time_entries: 時間を記録
316 field_time_zone: タイムゾーン
316 field_time_zone: タイムゾーン
317 field_searchable: 検索対象
317 field_searchable: 検索条件に設定可能とする
318 field_default_value: デフォルト値
318 field_default_value: デフォルト値
319 field_comments_sorting: コメントの表示順
319 field_comments_sorting: コメントの表示順
320 field_parent_title: 親ページ
320 field_parent_title: 親ページ
@@ -541,7 +541,7 ja:
541 label_subproject_plural: サブプロジェクト
541 label_subproject_plural: サブプロジェクト
542 label_subproject_new: 新しいサブプロジェクト
542 label_subproject_new: 新しいサブプロジェクト
543 label_and_its_subprojects: "%{value} とサブプロジェクト"
543 label_and_its_subprojects: "%{value} とサブプロジェクト"
544 label_min_max_length: - 最大長
544 label_min_max_length: 小値 - 最大値の
545 label_list: リストから選択
545 label_list: リストから選択
546 label_date: 日付
546 label_date: 日付
547 label_integer: 整数
547 label_integer: 整数
@@ -997,7 +997,6
997 label_only: 僅於
997 label_only: 僅於
998 label_drop_down_list: 下拉式清單
998 label_drop_down_list: 下拉式清單
999 label_checkboxes: 核取方塊
999 label_checkboxes: 核取方塊
1000 label_radio_buttons: 選項按鈕
1001 label_link_values_to: 連結欄位值至此網址
1000 label_link_values_to: 連結欄位值至此網址
1002 label_custom_field_select_type: 請選擇連結此自訂欄位的物件類型
1001 label_custom_field_select_type: 請選擇連結此自訂欄位的物件類型
1003 label_check_for_updates: 檢查更新
1002 label_check_for_updates: 檢查更新
@@ -1196,3 +1195,4
1196 description_date_from: 輸入起始日期
1195 description_date_from: 輸入起始日期
1197 description_date_to: 輸入結束日期
1196 description_date_to: 輸入結束日期
1198 text_repository_identifier_info: '僅允許使用小寫英文字母 (a-z), 阿拉伯數字, 虛線與底線。<br />一旦儲存之後, 代碼便無法再次被更改。'
1197 text_repository_identifier_info: '僅允許使用小寫英文字母 (a-z), 阿拉伯數字, 虛線與底線。<br />一旦儲存之後, 代碼便無法再次被更改。'
1198 label_radio_buttons: radio buttons
@@ -15,7 +15,7
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 RedmineApp::Application.routes.draw do
18 Rails.application.routes.draw do
19 root :to => 'welcome#index', :as => 'home'
19 root :to => 'welcome#index', :as => 'home'
20
20
21 match 'login', :to => 'account#login', :as => 'signin', :via => [:get, :post]
21 match 'login', :to => 'account#login', :as => 'signin', :via => [:get, :post]
@@ -25,10 +25,10 RedmineApp::Application.routes.draw do
25 match 'account/activate', :to => 'account#activate', :via => :get
25 match 'account/activate', :to => 'account#activate', :via => :get
26 get 'account/activation_email', :to => 'account#activation_email', :as => 'activation_email'
26 get 'account/activation_email', :to => 'account#activation_email', :as => 'activation_email'
27
27
28 match '/news/preview', :controller => 'previews', :action => 'news', :as => 'preview_news', :via => [:get, :post, :put]
28 match '/news/preview', :controller => 'previews', :action => 'news', :as => 'preview_news', :via => [:get, :post, :put, :patch]
29 match '/issues/preview/new/:project_id', :to => 'previews#issue', :as => 'preview_new_issue', :via => [:get, :post, :put]
29 match '/issues/preview/new/:project_id', :to => 'previews#issue', :as => 'preview_new_issue', :via => [:get, :post, :put, :patch]
30 match '/issues/preview/edit/:id', :to => 'previews#issue', :as => 'preview_edit_issue', :via => [:get, :post, :put]
30 match '/issues/preview/edit/:id', :to => 'previews#issue', :as => 'preview_edit_issue', :via => [:get, :post, :put, :patch]
31 match '/issues/preview', :to => 'previews#issue', :as => 'preview_issue', :via => [:get, :post, :put]
31 match '/issues/preview', :to => 'previews#issue', :as => 'preview_issue', :via => [:get, :post, :put, :patch]
32
32
33 match 'projects/:id/wiki', :to => 'wikis#edit', :via => :post
33 match 'projects/:id/wiki', :to => 'wikis#edit', :via => :post
34 match 'projects/:id/wiki/destroy', :to => 'wikis#destroy', :via => [:get, :post]
34 match 'projects/:id/wiki/destroy', :to => 'wikis#destroy', :via => [:get, :post]
@@ -74,7 +74,7 RedmineApp::Application.routes.draw do
74 match 'my/order_blocks', :controller => 'my', :action => 'order_blocks', :via => :post
74 match 'my/order_blocks', :controller => 'my', :action => 'order_blocks', :via => :post
75
75
76 resources :users
76 resources :users
77 match 'users/:id/memberships/:membership_id', :to => 'users#edit_membership', :via => :put, :as => 'user_membership'
77 match 'users/:id/memberships/:membership_id', :to => 'users#edit_membership', :via => [:put, :patch], :as => 'user_membership'
78 match 'users/:id/memberships/:membership_id', :to => 'users#destroy_membership', :via => :delete
78 match 'users/:id/memberships/:membership_id', :to => 'users#destroy_membership', :via => :delete
79 match 'users/:id/memberships', :to => 'users#edit_membership', :via => :post, :as => 'user_memberships'
79 match 'users/:id/memberships', :to => 'users#edit_membership', :via => :post, :as => 'user_memberships'
80
80
@@ -113,7 +113,7 RedmineApp::Application.routes.draw do
113 get 'issues/:copy_from/copy', :to => 'issues#new', :as => 'copy_issue'
113 get 'issues/:copy_from/copy', :to => 'issues#new', :as => 'copy_issue'
114 resources :issues, :only => [:index, :new, :create]
114 resources :issues, :only => [:index, :new, :create]
115 # issue form update
115 # issue form update
116 match 'issues/update_form', :controller => 'issues', :action => 'update_form', :via => [:put, :post], :as => 'issue_form'
116 match 'issues/update_form', :controller => 'issues', :action => 'update_form', :via => [:put, :patch, :post], :as => 'issue_form'
117
117
118 resources :files, :only => [:index, :new, :create]
118 resources :files, :only => [:index, :new, :create]
119
119
@@ -151,7 +151,7 RedmineApp::Application.routes.draw do
151 post 'rename'
151 post 'rename'
152 get 'history'
152 get 'history'
153 get 'diff'
153 get 'diff'
154 match 'preview', :via => [:post, :put]
154 match 'preview', :via => [:post, :put, :patch]
155 post 'protect'
155 post 'protect'
156 post 'add_attachment'
156 post 'add_attachment'
157 end
157 end
@@ -17,7 +17,10
17
17
18 class Setup < ActiveRecord::Migration
18 class Setup < ActiveRecord::Migration
19
19
20 class User < ActiveRecord::Base; end
20 class User < ActiveRecord::Base
21 attr_protected :id
22 end
23
21 # model removed
24 # model removed
22 class Permission < ActiveRecord::Base; end
25 class Permission < ActiveRecord::Base; end
23
26
@@ -4,7 +4,7 class AddEnumerationsPosition < ActiveRecord::Migration
4 Enumeration.all.group_by(&:opt).each do |opt, enums|
4 Enumeration.all.group_by(&:opt).each do |opt, enums|
5 enums.each_with_index do |enum, i|
5 enums.each_with_index do |enum, i|
6 # do not call model callbacks
6 # do not call model callbacks
7 Enumeration.update_all "position = #{i+1}", {:id => enum.id}
7 Enumeration.where({:id => enum.id}).update_all("position = #{i+1}")
8 end
8 end
9 end
9 end
10 end
10 end
@@ -4,7 +4,7 class AddCustomFieldsPosition < ActiveRecord::Migration
4 CustomField.all.group_by(&:type).each do |t, fields|
4 CustomField.all.group_by(&:type).each do |t, fields|
5 fields.each_with_index do |field, i|
5 fields.each_with_index do |field, i|
6 # do not call model callbacks
6 # do not call model callbacks
7 CustomField.update_all "position = #{i+1}", {:id => field.id}
7 CustomField.where({:id => field.id}).update_all("position = #{i+1}")
8 end
8 end
9 end
9 end
10 end
10 end
@@ -7,7 +7,7 class PopulateChangesetsUserId < ActiveRecord::Migration
7 username, email = $1.strip, $3
7 username, email = $1.strip, $3
8 u = User.find_by_login(username)
8 u = User.find_by_login(username)
9 u ||= User.find_by_mail(email) unless email.blank?
9 u ||= User.find_by_mail(email) unless email.blank?
10 Changeset.update_all("user_id = #{u.id}", ["committer = ?", committer]) unless u.nil?
10 Changeset.where(["committer = ?", committer]).update_all("user_id = #{u.id}") unless u.nil?
11 end
11 end
12 end
12 end
13 end
13 end
@@ -5,8 +5,8 class RemoveEnumerationsOpt < ActiveRecord::Migration
5
5
6 def self.down
6 def self.down
7 add_column :enumerations, :opt, :string, :limit => 4, :default => '', :null => false
7 add_column :enumerations, :opt, :string, :limit => 4, :default => '', :null => false
8 Enumeration.update_all("opt = 'IPRI'", "type = 'IssuePriority'")
8 Enumeration.where("type = 'IssuePriority'").update_all("opt = 'IPRI'")
9 Enumeration.update_all("opt = 'DCAT'", "type = 'DocumentCategory'")
9 Enumeration.where("type = 'DocumentCategory'").update_all("opt = 'DCAT'")
10 Enumeration.update_all("opt = 'ACTI'", "type = 'TimeEntryActivity'")
10 Enumeration.where("type = 'TimeEntryActivity'").update_all("opt = 'ACTI'")
11 end
11 end
12 end
12 end
@@ -1,6 +1,6
1 class EnableCalendarAndGanttModulesWhereAppropriate < ActiveRecord::Migration
1 class EnableCalendarAndGanttModulesWhereAppropriate < ActiveRecord::Migration
2 def self.up
2 def self.up
3 EnabledModule.where(:name => 'issue_tracking').all.each do |e|
3 EnabledModule.where(:name => 'issue_tracking').each do |e|
4 EnabledModule.create(:name => 'calendar', :project_id => e.project_id)
4 EnabledModule.create(:name => 'calendar', :project_id => e.project_id)
5 EnabledModule.create(:name => 'gantt', :project_id => e.project_id)
5 EnabledModule.create(:name => 'gantt', :project_id => e.project_id)
6 end
6 end
@@ -15,7 +15,8 class PopulateIssuesClosedOn < ActiveRecord::Migration
15
15
16 # Then set closed_on for closed issues that weren't up updated by the above UPDATE
16 # Then set closed_on for closed issues that weren't up updated by the above UPDATE
17 # No journal was found so we assume that they were closed on creation
17 # No journal was found so we assume that they were closed on creation
18 Issue.update_all "closed_on = created_on", {:status_id => closed_status_ids, :closed_on => nil}
18 Issue.where({:status_id => closed_status_ids, :closed_on => nil}).
19 update_all("closed_on = created_on")
19 end
20 end
20 end
21 end
21
22
@@ -86,7 +86,7 http://www.redmine.org/
86 * Defect #16655: start_date not set despite settings[default_issue_start_date_to_creation_date] being set.
86 * Defect #16655: start_date not set despite settings[default_issue_start_date_to_creation_date] being set.
87 * Defect #16668: Redmine links broken when object name contains special characters
87 * Defect #16668: Redmine links broken when object name contains special characters
88 * Defect #16669: Markdown formatter should use the :no_intra_emphasis extension
88 * Defect #16669: Markdown formatter should use the :no_intra_emphasis extension
89 * Defect #16708: Form is submitted when switching tab
89 * Defect #16708: Form is submitted when swithing tab
90 * Defect #16739: custom_fields.json only returns single tracker instead of array of trackers
90 * Defect #16739: custom_fields.json only returns single tracker instead of array of trackers
91 * Defect #16747: Remove useless settings when editing a query from the gantt
91 * Defect #16747: Remove useless settings when editing a query from the gantt
92 * Defect #16755: Field set as read-only still available in the issues list context menu
92 * Defect #16755: Field set as read-only still available in the issues list context menu
@@ -7,7 +7,7 http://www.redmine.org/
7
7
8 == Requirements
8 == Requirements
9
9
10 * Ruby 1.8.7, 1.9.2, 1.9.3, 2.0 or 2.1
10 * Ruby >= 1.9.3
11 * RubyGems
11 * RubyGems
12 * Bundler >= 1.0.21
12 * Bundler >= 1.0.21
13
13
@@ -30,9 +30,6 Optional:
30 3. Configure the database parameters in config/database.yml
30 3. Configure the database parameters in config/database.yml
31 for the "production" environment (default database is MySQL and ruby1.9)
31 for the "production" environment (default database is MySQL and ruby1.9)
32
32
33 If you're running Redmine with MySQL and ruby1.8, replace the adapter name
34 with `mysql`
35
36 4. Install the required gems by running:
33 4. Install the required gems by running:
37 bundle install --without development test
34 bundle install --without development test
38
35
@@ -29,7 +29,7 module Redmine
29 send :include, Redmine::Acts::ActivityProvider::InstanceMethods
29 send :include, Redmine::Acts::ActivityProvider::InstanceMethods
30 end
30 end
31
31
32 options.assert_valid_keys(:type, :permission, :timestamp, :author_key, :find_options)
32 options.assert_valid_keys(:type, :permission, :timestamp, :author_key, :find_options, :scope)
33 self.activity_provider_options ||= {}
33 self.activity_provider_options ||= {}
34
34
35 # One model can provide different event types
35 # One model can provide different event types
@@ -54,7 +54,7 module Redmine
54 provider_options = activity_provider_options[event_type]
54 provider_options = activity_provider_options[event_type]
55 raise "#{self.name} can not provide #{event_type} events." if provider_options.nil?
55 raise "#{self.name} can not provide #{event_type} events." if provider_options.nil?
56
56
57 scope = self
57 scope = (provider_options[:scope] || self)
58
58
59 if from && to
59 if from && to
60 scope = scope.where("#{provider_options[:timestamp]} BETWEEN ? AND ?", from, to)
60 scope = scope.where("#{provider_options[:timestamp]} BETWEEN ? AND ?", from, to)
@@ -79,7 +79,7 module Redmine
79 scope = scope.where(Project.allowed_to_condition(user, "view_#{self.name.underscore.pluralize}".to_sym, options))
79 scope = scope.where(Project.allowed_to_condition(user, "view_#{self.name.underscore.pluralize}".to_sym, options))
80 end
80 end
81
81
82 scope.all(provider_options[:find_options].dup)
82 scope.to_a
83 end
83 end
84 end
84 end
85 end
85 end
@@ -29,9 +29,8 module Redmine
29 attachable_options[:view_permission] = options.delete(:view_permission) || "view_#{self.name.pluralize.underscore}".to_sym
29 attachable_options[:view_permission] = options.delete(:view_permission) || "view_#{self.name.pluralize.underscore}".to_sym
30 attachable_options[:delete_permission] = options.delete(:delete_permission) || "edit_#{self.name.pluralize.underscore}".to_sym
30 attachable_options[:delete_permission] = options.delete(:delete_permission) || "edit_#{self.name.pluralize.underscore}".to_sym
31
31
32 has_many :attachments, options.merge(:as => :container,
32 has_many :attachments, lambda {order("#{Attachment.table_name}.created_on ASC, #{Attachment.table_name}.id ASC")},
33 :order => "#{Attachment.table_name}.created_on ASC, #{Attachment.table_name}.id ASC",
33 options.merge(:as => :container, :dependent => :destroy)
34 :dependent => :destroy)
35 send :include, Redmine::Acts::Attachable::InstanceMethods
34 send :include, Redmine::Acts::Attachable::InstanceMethods
36 before_save :attach_saved_attachments
35 before_save :attach_saved_attachments
37 end
36 end
@@ -27,9 +27,8 module Redmine
27 return if self.included_modules.include?(Redmine::Acts::Customizable::InstanceMethods)
27 return if self.included_modules.include?(Redmine::Acts::Customizable::InstanceMethods)
28 cattr_accessor :customizable_options
28 cattr_accessor :customizable_options
29 self.customizable_options = options
29 self.customizable_options = options
30 has_many :custom_values, :as => :customized,
30 has_many :custom_values, lambda {includes(:custom_field).order("#{CustomField.table_name}.position")},
31 :include => :custom_field,
31 :as => :customized,
32 :order => "#{CustomField.table_name}.position",
33 :dependent => :delete_all,
32 :dependent => :delete_all,
34 :validate => false
33 :validate => false
35
34
@@ -46,7 +45,7 module Redmine
46 end
45 end
47
46
48 def available_custom_fields
47 def available_custom_fields
49 CustomField.where("type = '#{self.class.name}CustomField'").sorted.all
48 CustomField.where("type = '#{self.class.name}CustomField'").sorted.to_a
50 end
49 end
51
50
52 # Sets the values of the object's custom fields
51 # Sets the values of the object's custom fields
@@ -31,6 +31,7 module Redmine
31 # * :permission - permission required to search the model (default to :view_"objects")
31 # * :permission - permission required to search the model (default to :view_"objects")
32 def acts_as_searchable(options = {})
32 def acts_as_searchable(options = {})
33 return if self.included_modules.include?(Redmine::Acts::Searchable::InstanceMethods)
33 return if self.included_modules.include?(Redmine::Acts::Searchable::InstanceMethods)
34 options.assert_valid_keys(:columns, :project_key, :date_column, :order_column, :search_custom_fields, :permission, :scope)
34
35
35 cattr_accessor :searchable_options
36 cattr_accessor :searchable_options
36 self.searchable_options = options
37 self.searchable_options = options
@@ -70,7 +71,7 module Redmine
70 # TODO: make user an argument
71 # TODO: make user an argument
71 user = User.current
72 user = User.current
72 tokens = [] << tokens unless tokens.is_a?(Array)
73 tokens = [] << tokens unless tokens.is_a?(Array)
73 projects = [] << projects unless projects.nil? || projects.is_a?(Array)
74 projects = [] << projects if projects.is_a?(Project)
74
75
75 limit_options = {}
76 limit_options = {}
76 limit_options[:limit] = options[:limit] if options[:limit]
77 limit_options[:limit] = options[:limit] if options[:limit]
@@ -100,7 +101,10 module Redmine
100
101
101 tokens_conditions = [sql, * (tokens.collect {|w| "%#{w.downcase}%"} * token_clauses.size).sort]
102 tokens_conditions = [sql, * (tokens.collect {|w| "%#{w.downcase}%"} * token_clauses.size).sort]
102
103
103 scope = self.scoped
104 scope = (searchable_options[:scope] || self)
105 if scope.is_a? Proc
106 scope = scope.call
107 end
104 project_conditions = []
108 project_conditions = []
105 if searchable_options.has_key?(:permission)
109 if searchable_options.has_key?(:permission)
106 project_conditions << Project.allowed_to_condition(user, searchable_options[:permission] || :view_project)
110 project_conditions << Project.allowed_to_condition(user, searchable_options[:permission] || :view_project)
@@ -118,10 +122,11 module Redmine
118 results_count = 0
122 results_count = 0
119
123
120 scope = scope.
124 scope = scope.
121 includes(searchable_options[:include]).
125 joins(searchable_options[:include]).
122 order("#{searchable_options[:order_column]} " + (options[:before] ? 'DESC' : 'ASC')).
126 order("#{searchable_options[:order_column]} " + (options[:before] ? 'DESC' : 'ASC')).
123 where(project_conditions).
127 where(project_conditions).
124 where(tokens_conditions)
128 where(tokens_conditions).
129 uniq
125
130
126 results_count = scope.count
131 results_count = scope.count
127
132
@@ -129,7 +134,7 module Redmine
129 if options[:offset]
134 if options[:offset]
130 scope_with_limit = scope_with_limit.where("#{searchable_options[:date_column]} #{options[:before] ? '<' : '>'} ?", options[:offset])
135 scope_with_limit = scope_with_limit.where("#{searchable_options[:date_column]} #{options[:before] ? '<' : '>'} ?", options[:offset])
131 end
136 end
132 results = scope_with_limit.all
137 results = scope_with_limit.to_a
133
138
134 [results, results_count]
139 [results, results_count]
135 end
140 end
@@ -44,7 +44,7 module ActiveRecord
44 configuration.update(options) if options.is_a?(Hash)
44 configuration.update(options) if options.is_a?(Hash)
45
45
46 belongs_to :parent, :class_name => name, :foreign_key => configuration[:foreign_key], :counter_cache => configuration[:counter_cache]
46 belongs_to :parent, :class_name => name, :foreign_key => configuration[:foreign_key], :counter_cache => configuration[:counter_cache]
47 has_many :children, :class_name => name, :foreign_key => configuration[:foreign_key], :order => configuration[:order], :dependent => configuration[:dependent]
47 has_many :children, lambda {order(configuration[:order])}, :class_name => name, :foreign_key => configuration[:foreign_key], :dependent => configuration[:dependent]
48
48
49 scope :roots, lambda {
49 scope :roots, lambda {
50 where("#{configuration[:foreign_key]} IS NULL").
50 where("#{configuration[:foreign_key]} IS NULL").
@@ -68,7 +68,7 module Redmine
68 end
68 end
69
69
70 def notified_watchers
70 def notified_watchers
71 notified = watcher_users.active
71 notified = watcher_users.active.to_a
72 notified.reject! {|user| user.mail.blank? || user.mail_notification == 'none'}
72 notified.reject! {|user| user.mail.blank? || user.mail_notification == 'none'}
73 if respond_to?(:visible?)
73 if respond_to?(:visible?)
74 notified.reject! {|user| !visible?(user)}
74 notified.reject! {|user| !visible?(user)}
@@ -63,12 +63,7 require 'redmine/themes'
63 require 'redmine/hook'
63 require 'redmine/hook'
64 require 'redmine/plugin'
64 require 'redmine/plugin'
65
65
66 if RUBY_VERSION < '1.9'
66 require 'csv'
67 require 'fastercsv'
68 else
69 require 'csv'
70 FCSV = CSV
71 end
72
67
73 Redmine::Scm::Base.add "Subversion"
68 Redmine::Scm::Base.add "Subversion"
74 Redmine::Scm::Base.add "Darcs"
69 Redmine::Scm::Base.add "Darcs"
@@ -58,9 +58,11 module Redmine
58 if action.is_a?(Symbol)
58 if action.is_a?(Symbol)
59 perm = permission(action)
59 perm = permission(action)
60 !perm.nil? && perm.read?
60 !perm.nil? && perm.read?
61 else
61 elsif action.is_a?(Hash)
62 s = "#{action[:controller]}/#{action[:action]}"
62 s = "#{action[:controller]}/#{action[:action]}"
63 permissions.detect {|p| p.actions.include?(s) && p.read?}.present?
63 permissions.detect {|p| p.actions.include?(s) && p.read?}.present?
64 else
65 raise ArgumentError.new("Symbol or a Hash expected, #{action.class.name} given: #{action}")
64 end
66 end
65 end
67 end
66
68
@@ -1,160 +1,71
1 if RUBY_VERSION < '1.9'
2 require 'iconv'
3 end
4
1
5 module Redmine
2 module Redmine
6 module CodesetUtil
3 module CodesetUtil
7
4
8 def self.replace_invalid_utf8(str)
5 def self.replace_invalid_utf8(str)
9 return str if str.nil?
6 return str if str.nil?
10 if str.respond_to?(:force_encoding)
7 str.force_encoding('UTF-8')
11 str.force_encoding('UTF-8')
8 if ! str.valid_encoding?
12 if ! str.valid_encoding?
9 str = str.encode("US-ASCII", :invalid => :replace,
13 str = str.encode("US-ASCII", :invalid => :replace,
10 :undef => :replace, :replace => '?').encode("UTF-8")
14 :undef => :replace, :replace => '?').encode("UTF-8")
15 end
16 elsif RUBY_PLATFORM == 'java'
17 begin
18 ic = Iconv.new('UTF-8', 'UTF-8')
19 str = ic.iconv(str)
20 rescue
21 str = str.gsub(%r{[^\r\n\t\x20-\x7e]}, '?')
22 end
23 else
24 ic = Iconv.new('UTF-8', 'UTF-8')
25 txtar = ""
26 begin
27 txtar += ic.iconv(str)
28 rescue Iconv::IllegalSequence
29 txtar += $!.success
30 str = '?' + $!.failed[1,$!.failed.length]
31 retry
32 rescue
33 txtar += $!.success
34 end
35 str = txtar
36 end
11 end
37 str
12 str
38 end
13 end
39
14
40 def self.to_utf8(str, encoding)
15 def self.to_utf8(str, encoding)
41 return str if str.nil?
16 return str if str.nil?
42 str.force_encoding("ASCII-8BIT") if str.respond_to?(:force_encoding)
17 str.force_encoding("ASCII-8BIT")
43 if str.empty?
18 if str.empty?
44 str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
19 str.force_encoding("UTF-8")
45 return str
20 return str
46 end
21 end
47 enc = encoding.blank? ? "UTF-8" : encoding
22 enc = encoding.blank? ? "UTF-8" : encoding
48 if str.respond_to?(:force_encoding)
23 if enc.upcase != "UTF-8"
49 if enc.upcase != "UTF-8"
24 str.force_encoding(enc)
50 str.force_encoding(enc)
25 str = str.encode("UTF-8", :invalid => :replace,
51 str = str.encode("UTF-8", :invalid => :replace,
26 :undef => :replace, :replace => '?')
52 :undef => :replace, :replace => '?')
53 else
54 str.force_encoding("UTF-8")
55 if ! str.valid_encoding?
56 str = str.encode("US-ASCII", :invalid => :replace,
57 :undef => :replace, :replace => '?').encode("UTF-8")
58 end
59 end
60 elsif RUBY_PLATFORM == 'java'
61 begin
62 ic = Iconv.new('UTF-8', enc)
63 str = ic.iconv(str)
64 rescue
65 str = str.gsub(%r{[^\r\n\t\x20-\x7e]}, '?')
66 end
67 else
27 else
68 ic = Iconv.new('UTF-8', enc)
28 str.force_encoding("UTF-8")
69 txtar = ""
29 if ! str.valid_encoding?
70 begin
30 str = str.encode("US-ASCII", :invalid => :replace,
71 txtar += ic.iconv(str)
31 :undef => :replace, :replace => '?').encode("UTF-8")
72 rescue Iconv::IllegalSequence
73 txtar += $!.success
74 str = '?' + $!.failed[1,$!.failed.length]
75 retry
76 rescue
77 txtar += $!.success
78 end
32 end
79 str = txtar
80 end
33 end
81 str
34 str
82 end
35 end
83
36
84 def self.to_utf8_by_setting(str)
37 def self.to_utf8_by_setting(str)
85 return str if str.nil?
38 return str if str.nil?
86 str = self.to_utf8_by_setting_internal(str)
39 self.to_utf8_by_setting_internal(str).force_encoding('UTF-8')
87 if str.respond_to?(:force_encoding)
88 str.force_encoding('UTF-8')
89 end
90 str
91 end
40 end
92
41
93 def self.to_utf8_by_setting_internal(str)
42 def self.to_utf8_by_setting_internal(str)
94 return str if str.nil?
43 return str if str.nil?
95 if str.respond_to?(:force_encoding)
44 str.force_encoding('ASCII-8BIT')
96 str.force_encoding('ASCII-8BIT')
97 end
98 return str if str.empty?
45 return str if str.empty?
99 return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
46 return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
100 if str.respond_to?(:force_encoding)
47 str.force_encoding('UTF-8')
101 str.force_encoding('UTF-8')
102 end
103 encodings = Setting.repositories_encodings.split(',').collect(&:strip)
48 encodings = Setting.repositories_encodings.split(',').collect(&:strip)
104 encodings.each do |encoding|
49 encodings.each do |encoding|
105 if str.respond_to?(:force_encoding)
50 begin
106 begin
51 str.force_encoding(encoding)
107 str.force_encoding(encoding)
52 utf8 = str.encode('UTF-8')
108 utf8 = str.encode('UTF-8')
53 return utf8 if utf8.valid_encoding?
109 return utf8 if utf8.valid_encoding?
54 rescue
110 rescue
55 # do nothing here and try the next encoding
111 # do nothing here and try the next encoding
112 end
113 else
114 begin
115 return Iconv.conv('UTF-8', encoding, str)
116 rescue Iconv::Failure
117 # do nothing here and try the next encoding
118 end
119 end
56 end
120 end
57 end
121 str = self.replace_invalid_utf8(str)
58 self.replace_invalid_utf8(str).force_encoding('UTF-8')
122 if str.respond_to?(:force_encoding)
123 str.force_encoding('UTF-8')
124 end
125 str
126 end
59 end
127
60
128 def self.from_utf8(str, encoding)
61 def self.from_utf8(str, encoding)
129 str ||= ''
62 str ||= ''
130 if str.respond_to?(:force_encoding)
63 str.force_encoding('UTF-8')
131 str.force_encoding('UTF-8')
64 if encoding.upcase != 'UTF-8'
132 if encoding.upcase != 'UTF-8'
65 str = str.encode(encoding, :invalid => :replace,
133 str = str.encode(encoding, :invalid => :replace,
66 :undef => :replace, :replace => '?')
134 :undef => :replace, :replace => '?')
135 else
136 str = self.replace_invalid_utf8(str)
137 end
138 elsif RUBY_PLATFORM == 'java'
139 begin
140 ic = Iconv.new(encoding, 'UTF-8')
141 str = ic.iconv(str)
142 rescue
143 str = str.gsub(%r{[^\r\n\t\x20-\x7e]}, '?')
144 end
145 else
67 else
146 ic = Iconv.new(encoding, 'UTF-8')
68 str = self.replace_invalid_utf8(str)
147 txtar = ""
148 begin
149 txtar += ic.iconv(str)
150 rescue Iconv::IllegalSequence
151 txtar += $!.success
152 str = '?' + $!.failed[1, $!.failed.length]
153 retry
154 rescue
155 txtar += $!.success
156 end
157 str = txtar
158 end
69 end
159 end
70 end
160 end
71 end
@@ -15,35 +15,10
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 module ActiveRecord
19 module FinderMethods
20 def find_ids(*args)
21 find_ids_with_associations
22 end
23
24 private
25
26 def find_ids_with_associations
27 join_dependency = construct_join_dependency_for_association_find
28 relation = construct_relation_for_association_find_ids(join_dependency)
29 rows = connection.select_all(relation, 'SQL', relation.bind_values)
30 rows.map {|row| row["id"].to_i}
31 rescue ThrowResult
32 []
33 end
34
35 def construct_relation_for_association_find_ids(join_dependency)
36 relation = except(:includes, :eager_load, :preload, :select).select("#{table_name}.id")
37 apply_join_dependency(relation, join_dependency)
38 end
39 end
40 end
41
42 class DateValidator < ActiveModel::EachValidator
18 class DateValidator < ActiveModel::EachValidator
43 def validate_each(record, attribute, value)
19 def validate_each(record, attribute, value)
44 before_type_cast = record.attributes_before_type_cast[attribute.to_s]
20 before_type_cast = record.attributes_before_type_cast[attribute.to_s]
45 if before_type_cast.is_a?(String) && before_type_cast.present?
21 if before_type_cast.is_a?(String) && before_type_cast.present?
46 # TODO: #*_date_before_type_cast returns a Mysql::Time with ruby1.8+mysql gem
47 unless before_type_cast =~ /\A\d{4}-\d{2}-\d{2}( 00:00:00)?\z/ && value
22 unless before_type_cast =~ /\A\d{4}-\d{2}-\d{2}( 00:00:00)?\z/ && value
48 record.errors.add attribute, :not_a_date
23 record.errors.add attribute, :not_a_date
49 end
24 end
@@ -36,13 +36,6 module Redmine #:nodoc:
36 s.gsub!(',', '.')
36 s.gsub!(',', '.')
37 begin; Kernel.Float(s); rescue; nil; end
37 begin; Kernel.Float(s); rescue; nil; end
38 end
38 end
39
40 # Object#to_a removed in ruby1.9
41 if RUBY_VERSION > '1.9'
42 def to_a
43 [self.dup]
44 end
45 end
46 end
39 end
47 end
40 end
48 end
41 end
@@ -678,9 +678,7 module Redmine
678 def self.rdm_from_utf8(txt, encoding)
678 def self.rdm_from_utf8(txt, encoding)
679 txt ||= ''
679 txt ||= ''
680 txt = Redmine::CodesetUtil.from_utf8(txt, encoding)
680 txt = Redmine::CodesetUtil.from_utf8(txt, encoding)
681 if txt.respond_to?(:force_encoding)
681 txt.force_encoding('ASCII-8BIT')
682 txt.force_encoding('ASCII-8BIT')
683 end
684 txt
682 txt
685 end
683 end
686
684
@@ -597,18 +597,13 module Redmine
597 def target_class
597 def target_class
598 @target_class ||= self.class.name[/^(.*::)?(.+)Format$/, 2].constantize rescue nil
598 @target_class ||= self.class.name[/^(.*::)?(.+)Format$/, 2].constantize rescue nil
599 end
599 end
600
601 def reset_target_class
602 @target_class = nil
603 end
604
600
605 def possible_custom_value_options(custom_value)
601 def possible_custom_value_options(custom_value)
606 options = possible_values_options(custom_value.custom_field, custom_value.customized)
602 options = possible_values_options(custom_value.custom_field, custom_value.customized)
607 missing = [custom_value.value_was].flatten.reject(&:blank?) - options.map(&:last)
603 missing = [custom_value.value_was].flatten.reject(&:blank?) - options.map(&:last)
608 if missing.any?
604 if missing.any?
609 options += target_class.where(:id => missing.map(&:to_i)).map {|o| [o.to_s, o.id.to_s]}
605 options += target_class.where(:id => missing.map(&:to_i)).map {|o| [o.to_s, o.id.to_s]}
610 #TODO: use #sort_by! when ruby1.8 support is dropped
606 options.sort_by!(&:first)
611 options = options.sort_by(&:first)
612 end
607 end
613 options
608 options
614 end
609 end
@@ -167,7 +167,7 module Redmine
167 where("child.id IN (?)", ids).
167 where("child.id IN (?)", ids).
168 order("#{Project.table_name}.lft ASC").
168 order("#{Project.table_name}.lft ASC").
169 uniq.
169 uniq.
170 all
170 to_a
171 else
171 else
172 @projects = []
172 @projects = []
173 end
173 end
@@ -193,7 +193,7 module Redmine
193 # * Checking the url target (project only)
193 # * Checking the url target (project only)
194 # * Checking the conditions of the item
194 # * Checking the conditions of the item
195 def allowed_node?(node, user, project)
195 def allowed_node?(node, user, project)
196 if project && user && !user.allowed_to?(node.url, project)
196 if node.url.is_a?(Hash) && project && user && !user.allowed_to?(node.url, project)
197 return false
197 return false
198 end
198 end
199 if node.condition && !node.condition.call(project)
199 if node.condition && !node.condition.call(project)
@@ -18,17 +18,13
18 require 'cgi'
18 require 'cgi'
19 require 'redmine/scm/adapters'
19 require 'redmine/scm/adapters'
20
20
21 if RUBY_VERSION < '1.9'
22 require 'iconv'
23 end
24
25 module Redmine
21 module Redmine
26 module Scm
22 module Scm
27 module Adapters
23 module Adapters
28 class AbstractAdapter #:nodoc:
24 class AbstractAdapter #:nodoc:
29
25
30 # raised if scm command exited with error, e.g. unknown revision.
26 # raised if scm command exited with error, e.g. unknown revision.
31 class ScmCommandAborted < CommandFailed; end
27 class ScmCommandAborted < ::Redmine::Scm::Adapters::CommandFailed; end
32
28
33 class << self
29 class << self
34 def client_command
30 def client_command
@@ -288,21 +284,12 module Redmine
288 def scm_iconv(to, from, str)
284 def scm_iconv(to, from, str)
289 return nil if str.nil?
285 return nil if str.nil?
290 return str if to == from
286 return str if to == from
291 if str.respond_to?(:force_encoding)
287 str.force_encoding(from)
292 str.force_encoding(from)
288 begin
293 begin
289 str.encode(to)
294 str.encode(to)
290 rescue Exception => err
295 rescue Exception => err
291 logger.error("failed to convert from #{from} to #{to}. #{err}")
296 logger.error("failed to convert from #{from} to #{to}. #{err}")
292 nil
297 nil
298 end
299 else
300 begin
301 Iconv.conv(to, from, str)
302 rescue Iconv::Failure => err
303 logger.error("failed to convert from #{from} to #{to}. #{err}")
304 nil
305 end
306 end
293 end
307 end
294 end
308
295
@@ -43,10 +43,7 module Redmine
43 end
43 end
44
44
45 def scm_command_version
45 def scm_command_version
46 scm_version = scm_version_from_command_line.dup
46 scm_version = scm_version_from_command_line.dup.force_encoding('ASCII-8BIT')
47 if scm_version.respond_to?(:force_encoding)
48 scm_version.force_encoding('ASCII-8BIT')
49 end
50 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)})
47 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)})
51 m[2].scan(%r{\d+}).collect(&:to_i)
48 m[2].scan(%r{\d+}).collect(&:to_i)
52 end
49 end
@@ -100,7 +97,7 module Redmine
100 prefix_utf8 = "#{url}/#{path}".gsub('\\', '/')
97 prefix_utf8 = "#{url}/#{path}".gsub('\\', '/')
101 logger.debug "PREFIX: #{prefix_utf8}"
98 logger.debug "PREFIX: #{prefix_utf8}"
102 prefix = scm_iconv(@path_encoding, 'UTF-8', prefix_utf8)
99 prefix = scm_iconv(@path_encoding, 'UTF-8', prefix_utf8)
103 prefix.force_encoding('ASCII-8BIT') if prefix.respond_to?(:force_encoding)
100 prefix.force_encoding('ASCII-8BIT')
104 re = %r{^V\s+(#{Regexp.escape(prefix)})?(\/?)([^\/]+)(\/?)\s+(\S+)\r?$}
101 re = %r{^V\s+(#{Regexp.escape(prefix)})?(\/?)([^\/]+)(\/?)\s+(\S+)\r?$}
105 io.each_line do |line|
102 io.each_line do |line|
106 next unless line =~ re
103 next unless line =~ re
@@ -43,10 +43,7 module Redmine
43 end
43 end
44
44
45 def scm_command_version
45 def scm_command_version
46 scm_version = scm_version_from_command_line.dup
46 scm_version = scm_version_from_command_line.dup.force_encoding('ASCII-8BIT')
47 if scm_version.respond_to?(:force_encoding)
48 scm_version.force_encoding('ASCII-8BIT')
49 end
50 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)}m)
47 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)}m)
51 m[2].scan(%r{\d+}).collect(&:to_i)
48 m[2].scan(%r{\d+}).collect(&:to_i)
52 end
49 end
@@ -94,7 +91,7 module Redmine
94 def entries(path=nil, identifier=nil, options={})
91 def entries(path=nil, identifier=nil, options={})
95 logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'"
92 logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'"
96 path_locale = scm_iconv(@path_encoding, 'UTF-8', path)
93 path_locale = scm_iconv(@path_encoding, 'UTF-8', path)
97 path_locale.force_encoding("ASCII-8BIT") if path_locale.respond_to?(:force_encoding)
94 path_locale.force_encoding("ASCII-8BIT")
98 entries = Entries.new
95 entries = Entries.new
99 cmd_args = %w|-q rls -e|
96 cmd_args = %w|-q rls -e|
100 cmd_args << "-D" << time_to_cvstime_rlog(identifier) if identifier
97 cmd_args << "-D" << time_to_cvstime_rlog(identifier) if identifier
@@ -171,6 +168,7 module Redmine
171 file_state = nil
168 file_state = nil
172 branch_map = nil
169 branch_map = nil
173 io.each_line() do |line|
170 io.each_line() do |line|
171 line = line.strip
174 if state != "revision" && /^#{ENDLOG}/ =~ line
172 if state != "revision" && /^#{ENDLOG}/ =~ line
175 commit_log = String.new
173 commit_log = String.new
176 revision = nil
174 revision = nil
@@ -43,10 +43,7 module Redmine
43 end
43 end
44
44
45 def darcs_binary_version
45 def darcs_binary_version
46 darcsversion = darcs_binary_version_from_command_line.dup
46 darcsversion = darcs_binary_version_from_command_line.dup.force_encoding('ASCII-8BIT')
47 if darcsversion.respond_to?(:force_encoding)
48 darcsversion.force_encoding('ASCII-8BIT')
49 end
50 if m = darcsversion.match(%r{\A(.*?)((\d+\.)+\d+)})
47 if m = darcsversion.match(%r{\A(.*?)((\d+\.)+\d+)})
51 m[2].scan(%r{\d+}).collect(&:to_i)
48 m[2].scan(%r{\d+}).collect(&:to_i)
52 end
49 end
@@ -47,10 +47,7 module Redmine
47 end
47 end
48
48
49 def scm_command_version
49 def scm_command_version
50 scm_version = scm_version_from_command_line.dup
50 scm_version = scm_version_from_command_line.dup.force_encoding('ASCII-8BIT')
51 if scm_version.respond_to?(:force_encoding)
52 scm_version.force_encoding('ASCII-8BIT')
53 end
54 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)})
51 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)})
55 m[2].scan(%r{\d+}).collect(&:to_i)
52 m[2].scan(%r{\d+}).collect(&:to_i)
56 end
53 end
@@ -145,10 +142,7 module Redmine
145 type = $1
142 type = $1
146 sha = $2
143 sha = $2
147 size = $3
144 size = $3
148 name = $4
145 name = $4.force_encoding(@path_encoding)
149 if name.respond_to?(:force_encoding)
150 name.force_encoding(@path_encoding)
151 end
152 full_path = p.empty? ? name : "#{p}/#{name}"
146 full_path = p.empty? ? name : "#{p}/#{name}"
153 n = scm_iconv('UTF-8', @path_encoding, name)
147 n = scm_iconv('UTF-8', @path_encoding, name)
154 full_p = scm_iconv('UTF-8', @path_encoding, full_path)
148 full_p = scm_iconv('UTF-8', @path_encoding, full_path)
@@ -54,10 +54,7 module Redmine
54 # The hg version is expressed either as a
54 # The hg version is expressed either as a
55 # release number (eg 0.9.5 or 1.0) or as a revision
55 # release number (eg 0.9.5 or 1.0) or as a revision
56 # id composed of 12 hexa characters.
56 # id composed of 12 hexa characters.
57 theversion = hgversion_from_command_line.dup
57 theversion = hgversion_from_command_line.dup.force_encoding('ASCII-8BIT')
58 if theversion.respond_to?(:force_encoding)
59 theversion.force_encoding('ASCII-8BIT')
60 end
61 if m = theversion.match(%r{\A(.*?)((\d+\.)+\d+)})
58 if m = theversion.match(%r{\A(.*?)((\d+\.)+\d+)})
62 m[2].scan(%r{\d+}).collect(&:to_i)
59 m[2].scan(%r{\d+}).collect(&:to_i)
63 end
60 end
@@ -130,10 +127,7 module Redmine
130 def summary
127 def summary
131 return @summary if @summary
128 return @summary if @summary
132 hg 'rhsummary' do |io|
129 hg 'rhsummary' do |io|
133 output = io.read
130 output = io.read.force_encoding('UTF-8')
134 if output.respond_to?(:force_encoding)
135 output.force_encoding('UTF-8')
136 end
137 begin
131 begin
138 @summary = parse_xml(output)['rhsummary']
132 @summary = parse_xml(output)['rhsummary']
139 rescue
133 rescue
@@ -146,10 +140,7 module Redmine
146 p1 = scm_iconv(@path_encoding, 'UTF-8', path)
140 p1 = scm_iconv(@path_encoding, 'UTF-8', path)
147 manifest = hg('rhmanifest', '-r', CGI.escape(hgrev(identifier)),
141 manifest = hg('rhmanifest', '-r', CGI.escape(hgrev(identifier)),
148 CGI.escape(without_leading_slash(p1.to_s))) do |io|
142 CGI.escape(without_leading_slash(p1.to_s))) do |io|
149 output = io.read
143 output = io.read.force_encoding('UTF-8')
150 if output.respond_to?(:force_encoding)
151 output.force_encoding('UTF-8')
152 end
153 begin
144 begin
154 parse_xml(output)['rhmanifest']['repository']['manifest']
145 parse_xml(output)['rhmanifest']['repository']['manifest']
155 rescue
146 rescue
@@ -193,10 +184,7 module Redmine
193 hg_args << '--limit' << options[:limit] if options[:limit]
184 hg_args << '--limit' << options[:limit] if options[:limit]
194 hg_args << hgtarget(path) unless path.blank?
185 hg_args << hgtarget(path) unless path.blank?
195 log = hg(*hg_args) do |io|
186 log = hg(*hg_args) do |io|
196 output = io.read
187 output = io.read.force_encoding('UTF-8')
197 if output.respond_to?(:force_encoding)
198 output.force_encoding('UTF-8')
199 end
200 begin
188 begin
201 # Mercurial < 1.5 does not support footer template for '</log>'
189 # Mercurial < 1.5 does not support footer template for '</log>'
202 parse_xml("#{output}</log>")['log']
190 parse_xml("#{output}</log>")['log']
@@ -278,7 +266,7 module Redmine
278 blame = Annotate.new
266 blame = Annotate.new
279 hg 'rhannotate', '-ncu', '-r', CGI.escape(hgrev(identifier)), hgtarget(p) do |io|
267 hg 'rhannotate', '-ncu', '-r', CGI.escape(hgrev(identifier)), hgtarget(p) do |io|
280 io.each_line do |line|
268 io.each_line do |line|
281 line.force_encoding('ASCII-8BIT') if line.respond_to?(:force_encoding)
269 line.force_encoding('ASCII-8BIT')
282 next unless line =~ %r{^([^:]+)\s(\d+)\s([0-9a-f]+):\s(.*)$}
270 next unless line =~ %r{^([^:]+)\s(\d+)\s([0-9a-f]+):\s(.*)$}
283 r = Revision.new(:author => $1.strip, :revision => $2, :scmid => $3,
271 r = Revision.new(:author => $1.strip, :revision => $2, :scmid => $3,
284 :identifier => $3)
272 :identifier => $3)
@@ -46,10 +46,7 module Redmine
46 end
46 end
47
47
48 def svn_binary_version
48 def svn_binary_version
49 scm_version = scm_version_from_command_line.dup
49 scm_version = scm_version_from_command_line.dup.force_encoding('ASCII-8BIT')
50 if scm_version.respond_to?(:force_encoding)
51 scm_version.force_encoding('ASCII-8BIT')
52 end
53 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)})
50 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)})
54 m[2].scan(%r{\d+}).collect(&:to_i)
51 m[2].scan(%r{\d+}).collect(&:to_i)
55 end
52 end
@@ -66,10 +63,7 module Redmine
66 cmd << credentials_string
63 cmd << credentials_string
67 info = nil
64 info = nil
68 shellout(cmd) do |io|
65 shellout(cmd) do |io|
69 output = io.read
66 output = io.read.force_encoding('UTF-8')
70 if output.respond_to?(:force_encoding)
71 output.force_encoding('UTF-8')
72 end
73 begin
67 begin
74 doc = parse_xml(output)
68 doc = parse_xml(output)
75 # root_url = doc.elements["info/entry/repository/root"].text
69 # root_url = doc.elements["info/entry/repository/root"].text
@@ -98,10 +92,7 module Redmine
98 cmd = "#{self.class.sq_bin} list --xml #{target(path)}@#{identifier}"
92 cmd = "#{self.class.sq_bin} list --xml #{target(path)}@#{identifier}"
99 cmd << credentials_string
93 cmd << credentials_string
100 shellout(cmd) do |io|
94 shellout(cmd) do |io|
101 output = io.read
95 output = io.read.force_encoding('UTF-8')
102 if output.respond_to?(:force_encoding)
103 output.force_encoding('UTF-8')
104 end
105 begin
96 begin
106 doc = parse_xml(output)
97 doc = parse_xml(output)
107 each_xml_element(doc['lists']['list'], 'entry') do |entry|
98 each_xml_element(doc['lists']['list'], 'entry') do |entry|
@@ -141,10 +132,7 module Redmine
141 cmd << credentials_string
132 cmd << credentials_string
142 properties = {}
133 properties = {}
143 shellout(cmd) do |io|
134 shellout(cmd) do |io|
144 output = io.read
135 output = io.read.force_encoding('UTF-8')
145 if output.respond_to?(:force_encoding)
146 output.force_encoding('UTF-8')
147 end
148 begin
136 begin
149 doc = parse_xml(output)
137 doc = parse_xml(output)
150 each_xml_element(doc['properties']['target'], 'property') do |property|
138 each_xml_element(doc['properties']['target'], 'property') do |property|
@@ -168,10 +156,7 module Redmine
168 cmd << " --limit #{options[:limit].to_i}" if options[:limit]
156 cmd << " --limit #{options[:limit].to_i}" if options[:limit]
169 cmd << ' ' + target(path)
157 cmd << ' ' + target(path)
170 shellout(cmd) do |io|
158 shellout(cmd) do |io|
171 output = io.read
159 output = io.read.force_encoding('UTF-8')
172 if output.respond_to?(:force_encoding)
173 output.force_encoding('UTF-8')
174 end
175 begin
160 begin
176 doc = parse_xml(output)
161 doc = parse_xml(output)
177 each_xml_element(doc['log'], 'logentry') do |logentry|
162 each_xml_element(doc['log'], 'logentry') do |logentry|
@@ -199,28 +199,10 module Redmine
199 while starting < max && line_left[starting] == line_right[starting]
199 while starting < max && line_left[starting] == line_right[starting]
200 starting += 1
200 starting += 1
201 end
201 end
202 if (! "".respond_to?(:force_encoding)) && starting < line_left.size
203 while line_left[starting].ord.between?(128, 191) && starting > 0
204 starting -= 1
205 end
206 end
207 ending = -1
202 ending = -1
208 while ending >= -(max - starting) && (line_left[ending] == line_right[ending])
203 while ending >= -(max - starting) && (line_left[ending] == line_right[ending])
209 ending -= 1
204 ending -= 1
210 end
205 end
211 if (! "".respond_to?(:force_encoding)) && ending > (-1 * line_left.size)
212 while line_left[ending].ord.between?(128, 255) && ending < -1
213 if line_left[ending].ord.between?(128, 191)
214 if line_left[ending + 1].ord.between?(128, 191)
215 ending += 1
216 else
217 break
218 end
219 else
220 ending += 1
221 end
222 end
223 end
224 unless starting == 0 && ending == -1
206 unless starting == 0 && ending == -1
225 [starting, ending]
207 [starting, ending]
226 end
208 end
@@ -279,7 +261,7 module Redmine
279
261
280 def line_to_html(line, offsets)
262 def line_to_html(line, offsets)
281 html = line_to_html_raw(line, offsets)
263 html = line_to_html_raw(line, offsets)
282 html.force_encoding('UTF-8') if html.respond_to?(:force_encoding)
264 html.force_encoding('UTF-8')
283 html
265 html
284 end
266 end
285
267
@@ -33,7 +33,7 namespace :ci do
33 else
33 else
34 Rake::Task["test"].invoke
34 Rake::Task["test"].invoke
35 end
35 end
36 # Rake::Task["test:ui"].invoke if RUBY_VERSION >= '1.9.3'
36 # Rake::Task["test:ui"].invoke
37 end
37 end
38
38
39 desc "Finish the build"
39 desc "Finish the build"
@@ -52,7 +52,7 file 'config/database.yml' do
52
52
53 case database
53 case database
54 when 'mysql'
54 when 'mysql'
55 dev_conf = {'adapter' => (RUBY_VERSION >= '1.9' ? 'mysql2' : 'mysql'),
55 dev_conf = {'adapter' => 'mysql2',
56 'database' => dev_db_name, 'host' => 'localhost',
56 'database' => dev_db_name, 'host' => 'localhost',
57 'encoding' => 'utf8'}
57 'encoding' => 'utf8'}
58 if ENV['RUN_ON_NOT_OFFICIAL']
58 if ENV['RUN_ON_NOT_OFFICIAL']
@@ -68,9 +68,6 namespace :locales do
68 desc <<-END_DESC
68 desc <<-END_DESC
69 Removes a translation string from all locale file (only works for top-level childless non-multiline keys, probably doesn\'t work on windows).
69 Removes a translation string from all locale file (only works for top-level childless non-multiline keys, probably doesn\'t work on windows).
70
70
71 This task does not work on Ruby 1.8.6.
72 You need to use Ruby 1.8.7 or later.
73
74 Options:
71 Options:
75 key=key_1,key_2 Comma-separated list of keys to delete
72 key=key_1,key_2 Comma-separated list of keys to delete
76 skip=en,de Comma-separated list of locale files to ignore (filename without extension)
73 skip=en,de Comma-separated list of locale files to ignore (filename without extension)
@@ -18,7 +18,6
18 desc 'Mantis migration script'
18 desc 'Mantis migration script'
19
19
20 require 'active_record'
20 require 'active_record'
21 require 'iconv' if RUBY_VERSION < '1.9'
22 require 'pp'
21 require 'pp'
23
22
24 namespace :redmine do
23 namespace :redmine do
@@ -452,12 +451,7 task :migrate_from_mantis => :environment do
452 end
451 end
453
452
454 def self.encode(text)
453 def self.encode(text)
455 if RUBY_VERSION < '1.9'
454 text.to_s.force_encoding(@charset).encode('UTF-8')
456 @ic ||= Iconv.new('UTF-8', @charset)
457 @ic.iconv text
458 else
459 text.to_s.force_encoding(@charset).encode('UTF-8')
460 end
461 end
455 end
462 end
456 end
463
457
@@ -16,7 +16,6
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'active_record'
18 require 'active_record'
19 require 'iconv' if RUBY_VERSION < '1.9'
20 require 'pp'
19 require 'pp'
21
20
22 namespace :redmine do
21 namespace :redmine do
@@ -155,7 +154,7 namespace :redmine do
155 attachment_type = read_attribute(:type)
154 attachment_type = read_attribute(:type)
156 #replace exotic characters with their hex representation to avoid invalid filenames
155 #replace exotic characters with their hex representation to avoid invalid filenames
157 trac_file = filename.gsub( /[^a-zA-Z0-9\-_\.!~*']/n ) do |x|
156 trac_file = filename.gsub( /[^a-zA-Z0-9\-_\.!~*']/n ) do |x|
158 codepoint = RUBY_VERSION < '1.9' ? x[0] : x.codepoints.to_a[0]
157 codepoint = x.codepoints.to_a[0]
159 sprintf('%%%02x', codepoint)
158 sprintf('%%%02x', codepoint)
160 end
159 end
161 "#{TracMigrate.trac_attachments_directory}/#{attachment_type}/#{id}/#{trac_file}"
160 "#{TracMigrate.trac_attachments_directory}/#{attachment_type}/#{id}/#{trac_file}"
@@ -715,12 +714,7 namespace :redmine do
715 end
714 end
716
715
717 def self.encode(text)
716 def self.encode(text)
718 if RUBY_VERSION < '1.9'
717 text.to_s.force_encoding(@charset).encode('UTF-8')
719 @ic ||= Iconv.new('UTF-8', @charset)
720 @ic.iconv text
721 else
722 text.to_s.force_encoding(@charset).encode('UTF-8')
723 end
724 end
718 end
725 end
719 end
726
720
@@ -1,6 +1,2
1 #!/usr/bin/env ruby
1 #!/usr/bin/env ruby
2
2 abort "script/about no longer exists, please use bin/about instead."
3 ENV["RAILS_ENV"] ||= "production"
4 require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
5 puts
6 puts Redmine::Info.environment
@@ -1,6 +1,2
1 #!/usr/bin/env ruby
1 #!/usr/bin/env ruby
2 # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
2 abort "script/rails no longer exists, please use bin/rails instead."
3
4 APP_PATH = File.expand_path('../../config/application', __FILE__)
5 require File.expand_path('../../config/boot', __FILE__)
6 require 'rails/commands'
@@ -72,8 +72,8 class AccountControllerTest < ActionController::TestCase
72 end
72 end
73
73
74 def test_login_with_suburi_should_redirect_to_back_url_param
74 def test_login_with_suburi_should_redirect_to_back_url_param
75 @relative_url_root = ApplicationController.relative_url_root
75 @relative_url_root = Redmine::Utils.relative_url_root
76 ApplicationController.relative_url_root = '/redmine'
76 Redmine::Utils.relative_url_root = '/redmine'
77
77
78 back_urls = [
78 back_urls = [
79 'http://test.host/redmine/issues/show/1',
79 'http://test.host/redmine/issues/show/1',
@@ -84,7 +84,7 class AccountControllerTest < ActionController::TestCase
84 assert_redirected_to back_url
84 assert_redirected_to back_url
85 end
85 end
86 ensure
86 ensure
87 ApplicationController.relative_url_root = @relative_url_root
87 Redmine::Utils.relative_url_root = @relative_url_root
88 end
88 end
89
89
90 def test_login_should_not_redirect_to_another_host
90 def test_login_should_not_redirect_to_another_host
@@ -99,8 +99,8 class AccountControllerTest < ActionController::TestCase
99 end
99 end
100
100
101 def test_login_with_suburi_should_not_redirect_to_another_suburi
101 def test_login_with_suburi_should_not_redirect_to_another_suburi
102 @relative_url_root = ApplicationController.relative_url_root
102 @relative_url_root = Redmine::Utils.relative_url_root
103 ApplicationController.relative_url_root = '/redmine'
103 Redmine::Utils.relative_url_root = '/redmine'
104
104
105 back_urls = [
105 back_urls = [
106 'http://test.host/',
106 'http://test.host/',
@@ -115,7 +115,7 class AccountControllerTest < ActionController::TestCase
115 assert_redirected_to '/my/page'
115 assert_redirected_to '/my/page'
116 end
116 end
117 ensure
117 ensure
118 ApplicationController.relative_url_root = @relative_url_root
118 Redmine::Utils.relative_url_root = @relative_url_root
119 end
119 end
120
120
121 def test_login_with_wrong_password
121 def test_login_with_wrong_password
@@ -127,11 +127,13 class ActivitiesControllerTest < ActionController::TestCase
127 end
127 end
128
128
129 def test_index_atom_feed_with_one_item_type
129 def test_index_atom_feed_with_one_item_type
130 get :index, :format => 'atom', :show_issues => '1'
130 with_settings :default_language => 'en' do
131 assert_response :success
131 get :index, :format => 'atom', :show_issues => '1'
132 assert_template 'common/feed'
132 assert_response :success
133
133 assert_template 'common/feed'
134 assert_select 'title', :text => /Issues/
134
135 assert_select 'title', :text => /Issues/
136 end
135 end
137 end
136
138
137 def test_index_should_show_private_notes_with_permission_only
139 def test_index_should_show_private_notes_with_permission_only
@@ -139,8 +139,7 class AttachmentsControllerTest < ActionController::TestCase
139 assert a.save
139 assert a.save
140 assert_equal 'japanese-utf-8.txt', a.filename
140 assert_equal 'japanese-utf-8.txt', a.filename
141
141
142 str_japanese = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"
142 str_japanese = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e".force_encoding('UTF-8')
143 str_japanese.force_encoding('UTF-8') if str_japanese.respond_to?(:force_encoding)
144
143
145 get :show, :id => a.id
144 get :show, :id => a.id
146 assert_response :success
145 assert_response :success
@@ -48,10 +48,10 class CalendarsControllerTest < ActionController::TestCase
48 end
48 end
49
49
50 def test_week_number_calculation
50 def test_week_number_calculation
51 Setting.start_of_week = 7
51 with_settings :start_of_week => 7 do
52
52 get :show, :month => '1', :year => '2010'
53 get :show, :month => '1', :year => '2010'
53 assert_response :success
54 assert_response :success
54 end
55
55
56 assert_select 'tr' do
56 assert_select 'tr' do
57 assert_select 'td.week-number', :text => '53'
57 assert_select 'td.week-number', :text => '53'
@@ -65,9 +65,10 class CalendarsControllerTest < ActionController::TestCase
65 assert_select 'td.even', :text => '9'
65 assert_select 'td.even', :text => '9'
66 end
66 end
67
67
68 Setting.start_of_week = 1
68 with_settings :start_of_week => 1 do
69 get :show, :month => '1', :year => '2010'
69 get :show, :month => '1', :year => '2010'
70 assert_response :success
70 assert_response :success
71 end
71
72
72 assert_select 'tr' do
73 assert_select 'tr' do
73 assert_select 'td.week-number', :text => '53'
74 assert_select 'td.week-number', :text => '53'
@@ -56,12 +56,14 class ContextMenusControllerTest < ActionController::TestCase
56 end
56 end
57
57
58 def test_context_menu_one_issue_by_anonymous
58 def test_context_menu_one_issue_by_anonymous
59 get :issues, :ids => [1]
59 with_settings :default_language => 'en' do
60 assert_response :success
60 get :issues, :ids => [1]
61 assert_template 'context_menus/issues'
61 assert_response :success
62 assert_tag :tag => 'a', :content => 'Delete',
62 assert_template 'context_menus/issues'
63 :attributes => { :href => '#',
63 assert_tag :tag => 'a', :content => 'Delete',
64 :class => 'icon-del disabled' }
64 :attributes => { :href => '#',
65 :class => 'icon-del disabled' }
66 end
65 end
67 end
66
68
67 def test_context_menu_multiple_issues_of_same_project
69 def test_context_menu_multiple_issues_of_same_project
@@ -112,7 +112,7 class CustomFieldsControllerTest < ActionController::TestCase
112 end
112 end
113
113
114 def test_new_js
114 def test_new_js
115 get :new, :type => 'IssueCustomField', :custom_field => {:field_format => 'list'}, :format => 'js'
115 xhr :get, :new, :type => 'IssueCustomField', :custom_field => {:field_format => 'list'}, :format => 'js'
116 assert_response :success
116 assert_response :success
117 assert_template 'new'
117 assert_template 'new'
118 assert_equal 'text/javascript', response.content_type
118 assert_equal 'text/javascript', response.content_type
@@ -201,7 +201,7 class GroupsControllerTest < ActionController::TestCase
201 end
201 end
202
202
203 def test_autocomplete_for_user
203 def test_autocomplete_for_user
204 get :autocomplete_for_user, :id => 10, :q => 'smi', :format => 'js'
204 xhr :get, :autocomplete_for_user, :id => 10, :q => 'smi', :format => 'js'
205 assert_response :success
205 assert_response :success
206 assert_include 'John Smith', response.body
206 assert_include 'John Smith', response.body
207 end
207 end
@@ -474,12 +474,8 class IssuesControllerTest < ActionController::TestCase
474
474
475 def test_index_csv_big_5
475 def test_index_csv_big_5
476 with_settings :default_language => "zh-TW" do
476 with_settings :default_language => "zh-TW" do
477 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88"
477 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88".force_encoding('UTF-8')
478 str_big5 = "\xa4@\xa4\xeb"
478 str_big5 = "\xa4@\xa4\xeb".force_encoding('Big5')
479 if str_utf8.respond_to?(:force_encoding)
480 str_utf8.force_encoding('UTF-8')
481 str_big5.force_encoding('Big5')
482 end
483 issue = Issue.generate!(:subject => str_utf8)
479 issue = Issue.generate!(:subject => str_utf8)
484
480
485 get :index, :project_id => 1,
481 get :index, :project_id => 1,
@@ -488,10 +484,7 class IssuesControllerTest < ActionController::TestCase
488 :format => 'csv'
484 :format => 'csv'
489 assert_equal 'text/csv; header=present', @response.content_type
485 assert_equal 'text/csv; header=present', @response.content_type
490 lines = @response.body.chomp.split("\n")
486 lines = @response.body.chomp.split("\n")
491 s1 = "\xaa\xac\xbaA"
487 s1 = "\xaa\xac\xbaA".force_encoding('Big5')
492 if str_utf8.respond_to?(:force_encoding)
493 s1.force_encoding('Big5')
494 end
495 assert_include s1, lines[0]
488 assert_include s1, lines[0]
496 assert_include str_big5, lines[1]
489 assert_include str_big5, lines[1]
497 end
490 end
@@ -499,10 +492,7 class IssuesControllerTest < ActionController::TestCase
499
492
500 def test_index_csv_cannot_convert_should_be_replaced_big_5
493 def test_index_csv_cannot_convert_should_be_replaced_big_5
501 with_settings :default_language => "zh-TW" do
494 with_settings :default_language => "zh-TW" do
502 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85"
495 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85".force_encoding('UTF-8')
503 if str_utf8.respond_to?(:force_encoding)
504 str_utf8.force_encoding('UTF-8')
505 end
506 issue = Issue.generate!(:subject => str_utf8)
496 issue = Issue.generate!(:subject => str_utf8)
507
497
508 get :index, :project_id => 1,
498 get :index, :project_id => 1,
@@ -513,21 +503,11 class IssuesControllerTest < ActionController::TestCase
513 :set_filter => 1
503 :set_filter => 1
514 assert_equal 'text/csv; header=present', @response.content_type
504 assert_equal 'text/csv; header=present', @response.content_type
515 lines = @response.body.chomp.split("\n")
505 lines = @response.body.chomp.split("\n")
516 s1 = "\xaa\xac\xbaA" # status
506 s1 = "\xaa\xac\xbaA".force_encoding('Big5') # status
517 if str_utf8.respond_to?(:force_encoding)
518 s1.force_encoding('Big5')
519 end
520 assert lines[0].include?(s1)
507 assert lines[0].include?(s1)
521 s2 = lines[1].split(",")[2]
508 s2 = lines[1].split(",")[2]
522 if s1.respond_to?(:force_encoding)
509 s3 = "\xa5H?".force_encoding('Big5') # subject
523 s3 = "\xa5H?" # subject
510 assert_equal s3, s2
524 s3.force_encoding('Big5')
525 assert_equal s3, s2
526 elsif RUBY_PLATFORM == 'java'
527 assert_equal "??", s2
528 else
529 assert_equal "\xa5H???", s2
530 end
531 end
511 end
532 end
512 end
533
513
@@ -3120,20 +3100,15 class IssuesControllerTest < ActionController::TestCase
3120 def test_put_update_with_attachment_that_fails_to_save
3100 def test_put_update_with_attachment_that_fails_to_save
3121 set_tmp_attachments_directory
3101 set_tmp_attachments_directory
3122
3102
3123 # Delete all fixtured journals, a race condition can occur causing the wrong
3124 # journal to get fetched in the next find.
3125 Journal.delete_all
3126
3127 # Mock out the unsaved attachment
3128 Attachment.any_instance.stubs(:create).returns(Attachment.new)
3129
3130 # anonymous user
3103 # anonymous user
3131 put :update,
3104 with_settings :attachment_max_size => 0 do
3132 :id => 1,
3105 put :update,
3133 :issue => {:notes => ''},
3106 :id => 1,
3134 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
3107 :issue => {:notes => ''},
3135 assert_redirected_to :action => 'show', :id => '1'
3108 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
3136 assert_equal '1 file(s) could not be saved.', flash[:warning]
3109 assert_redirected_to :action => 'show', :id => '1'
3110 assert_equal '1 file(s) could not be saved.', flash[:warning]
3111 end
3137 end
3112 end
3138
3113
3139 def test_put_update_with_no_change
3114 def test_put_update_with_no_change
@@ -3379,7 +3354,7 class IssuesControllerTest < ActionController::TestCase
3379 post :bulk_edit, :ids => [1, 2, 6], :issue => {:project_id => 1}
3354 post :bulk_edit, :ids => [1, 2, 6], :issue => {:project_id => 1}
3380 assert_response :success
3355 assert_response :success
3381 assert_template 'bulk_edit'
3356 assert_template 'bulk_edit'
3382 assert_equal Project.find(1).shared_versions.open.all.sort, assigns(:versions).sort
3357 assert_equal Project.find(1).shared_versions.open.to_a.sort, assigns(:versions).sort
3383
3358
3384 assert_select 'select[name=?]', 'issue[fixed_version_id]' do
3359 assert_select 'select[name=?]', 'issue[fixed_version_id]' do
3385 assert_select 'option', :text => '2.0'
3360 assert_select 'option', :text => '2.0'
@@ -199,8 +199,8 class IssuesCustomFieldsVisibilityTest < ActionController::TestCase
199 p1 = Project.generate!
199 p1 = Project.generate!
200 p2 = Project.generate!
200 p2 = Project.generate!
201 user = User.generate!
201 user = User.generate!
202 User.add_to_project(user, p1, Role.where(:id => [1, 3]).all)
202 User.add_to_project(user, p1, Role.where(:id => [1, 3]).to_a)
203 User.add_to_project(user, p2, Role.where(:id => 3).all)
203 User.add_to_project(user, p2, Role.where(:id => 3).to_a)
204 Issue.generate!(:project => p1, :tracker_id => 1, :custom_field_values => {@field2.id => 'ValueA'})
204 Issue.generate!(:project => p1, :tracker_id => 1, :custom_field_values => {@field2.id => 'ValueA'})
205 Issue.generate!(:project => p2, :tracker_id => 1, :custom_field_values => {@field2.id => 'ValueB'})
205 Issue.generate!(:project => p2, :tracker_id => 1, :custom_field_values => {@field2.id => 'ValueB'})
206 Issue.generate!(:project => p1, :tracker_id => 1, :custom_field_values => {@field2.id => 'ValueC'})
206 Issue.generate!(:project => p1, :tracker_id => 1, :custom_field_values => {@field2.id => 'ValueC'})
@@ -104,7 +104,7 class MembersControllerTest < ActionController::TestCase
104 end
104 end
105
105
106 def test_autocomplete
106 def test_autocomplete
107 get :autocomplete, :project_id => 1, :q => 'mis', :format => 'js'
107 xhr :get, :autocomplete, :project_id => 1, :q => 'mis', :format => 'js'
108 assert_response :success
108 assert_response :success
109 assert_include 'User Misc', response.body
109 assert_include 'User Misc', response.body
110 end
110 end
@@ -59,8 +59,8 class MessagesControllerTest < ActionController::TestCase
59 assert_template 'show'
59 assert_template 'show'
60 replies = assigns(:replies)
60 replies = assigns(:replies)
61 assert_not_nil replies
61 assert_not_nil replies
62 assert !replies.include?(message.children.order('id').first)
62 assert_not_include message.children.reorder('id').first, replies
63 assert replies.include?(message.children.order('id').last)
63 assert_include message.children.reorder('id').last, replies
64 end
64 end
65
65
66 def test_show_with_reply_permission
66 def test_show_with_reply_permission
@@ -167,7 +167,7 class MyControllerTest < ActionController::TestCase
167 :new_password_confirmation => 'secret1234'
167 :new_password_confirmation => 'secret1234'
168 assert_response :success
168 assert_response :success
169 assert_template 'password'
169 assert_template 'password'
170 assert_error_tag :content => /Password doesn&#x27;t match confirmation/
170 assert_error_tag :content => /Password doesn.*t match confirmation/
171
171
172 # wrong password
172 # wrong password
173 post :password, :password => 'wrongpassword',
173 post :password, :password => 'wrongpassword',
@@ -26,7 +26,7 class RepositoriesBazaarControllerTest < ActionController::TestCase
26 REPOSITORY_PATH = Rails.root.join('tmp/test/bazaar_repository').to_s
26 REPOSITORY_PATH = Rails.root.join('tmp/test/bazaar_repository').to_s
27 REPOSITORY_PATH_TRUNK = File.join(REPOSITORY_PATH, "trunk")
27 REPOSITORY_PATH_TRUNK = File.join(REPOSITORY_PATH, "trunk")
28 PRJ_ID = 3
28 PRJ_ID = 3
29 CHAR_1_UTF8_HEX = "\xc3\x9c"
29 CHAR_1_UTF8_HEX = "\xc3\x9c".dup.force_encoding('UTF-8')
30
30
31 def setup
31 def setup
32 User.current = nil
32 User.current = nil
@@ -36,10 +36,6 class RepositoriesBazaarControllerTest < ActionController::TestCase
36 :url => REPOSITORY_PATH_TRUNK,
36 :url => REPOSITORY_PATH_TRUNK,
37 :log_encoding => 'UTF-8')
37 :log_encoding => 'UTF-8')
38 assert @repository
38 assert @repository
39 @char_1_utf8 = CHAR_1_UTF8_HEX.dup
40 if @char_1_utf8.respond_to?(:force_encoding)
41 @char_1_utf8.force_encoding('UTF-8')
42 end
43 end
39 end
44
40
45 if File.directory?(REPOSITORY_PATH)
41 if File.directory?(REPOSITORY_PATH)
@@ -176,31 +172,29 class RepositoriesBazaarControllerTest < ActionController::TestCase
176 end
172 end
177 end
173 end
178
174
179 if REPOSITORY_PATH.respond_to?(:force_encoding)
175 def test_annotate_author_non_ascii
180 def test_annotate_author_non_ascii
176 log_encoding = nil
181 log_encoding = nil
177 if Encoding.locale_charmap == "UTF-8" ||
182 if Encoding.locale_charmap == "UTF-8" ||
178 Encoding.locale_charmap == "ISO-8859-1"
183 Encoding.locale_charmap == "ISO-8859-1"
179 log_encoding = Encoding.locale_charmap
184 log_encoding = Encoding.locale_charmap
180 end
185 end
181 unless log_encoding.nil?
186 unless log_encoding.nil?
182 repository = Repository::Bazaar.create(
187 repository = Repository::Bazaar.create(
183 :project => @project,
188 :project => @project,
184 :url => File.join(REPOSITORY_PATH, "author_non_ascii"),
189 :url => File.join(REPOSITORY_PATH, "author_non_ascii"),
185 :identifier => 'author_non_ascii',
190 :identifier => 'author_non_ascii',
186 :log_encoding => log_encoding)
191 :log_encoding => log_encoding)
187 assert repository
192 assert repository
188 get :annotate, :id => PRJ_ID, :repository_id => 'author_non_ascii',
193 get :annotate, :id => PRJ_ID, :repository_id => 'author_non_ascii',
189 :path => repository_path_hash(['author-non-ascii-test.txt'])[:param]
194 :path => repository_path_hash(['author-non-ascii-test.txt'])[:param]
190 assert_response :success
195 assert_response :success
191 assert_template 'annotate'
196 assert_template 'annotate'
192 assert_select "th.line-num", :text => '1' do
197 assert_select "th.line-num", :text => '1' do
193 assert_select "+ td.revision" do
198 assert_select "+ td.revision" do
194 assert_select "a", :text => '2'
199 assert_select "a", :text => '2'
195 assert_select "+ td.author", :text => "test #{CHAR_1_UTF8_HEX}" do
200 assert_select "+ td.author", :text => "test #{@char_1_utf8}" do
196 assert_select "+ td",
201 assert_select "+ td",
197 :text => "author non ASCII test"
202 :text => "author non ASCII test"
203 end
204 end
198 end
205 end
199 end
206 end
200 end
@@ -27,8 +27,7 class RepositoriesFilesystemControllerTest < ActionController::TestCase
27 PRJ_ID = 3
27 PRJ_ID = 3
28
28
29 def setup
29 def setup
30 @ruby19_non_utf8_pass =
30 @ruby19_non_utf8_pass = Encoding.default_external.to_s != 'UTF-8'
31 (RUBY_VERSION >= '1.9' && Encoding.default_external.to_s != 'UTF-8')
32 User.current = nil
31 User.current = nil
33 Setting.enabled_scm << 'Filesystem' unless Setting.enabled_scm.include?('Filesystem')
32 Setting.enabled_scm << 'Filesystem' unless Setting.enabled_scm.include?('Filesystem')
34 @project = Project.find(PRJ_ID)
33 @project = Project.find(PRJ_ID)
@@ -94,12 +93,11 class RepositoriesFilesystemControllerTest < ActionController::TestCase
94 :attributes => { :class => 'line-num' },
93 :attributes => { :class => 'line-num' },
95 :sibling => { :tag => 'td', :content => /japanese/ }
94 :sibling => { :tag => 'td', :content => /japanese/ }
96 if @ruby19_non_utf8_pass
95 if @ruby19_non_utf8_pass
97 puts "TODO: show repository file contents test fails in Ruby 1.9 " +
96 puts "TODO: show repository file contents test fails " +
98 "and Encoding.default_external is not UTF-8. " +
97 "when Encoding.default_external is not UTF-8. " +
99 "Current value is '#{Encoding.default_external.to_s}'"
98 "Current value is '#{Encoding.default_external.to_s}'"
100 else
99 else
101 str_japanese = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"
100 str_japanese = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e".force_encoding('UTF-8')
102 str_japanese.force_encoding('UTF-8') if str_japanese.respond_to?(:force_encoding)
103 assert_tag :tag => 'th',
101 assert_tag :tag => 'th',
104 :content => '3',
102 :content => '3',
105 :attributes => { :class => 'line-num' },
103 :attributes => { :class => 'line-num' },
@@ -109,7 +107,7 class RepositoriesFilesystemControllerTest < ActionController::TestCase
109 end
107 end
110
108
111 def test_show_utf16
109 def test_show_utf16
112 enc = (RUBY_VERSION == "1.9.2" ? 'UTF-16LE' : 'UTF-16')
110 enc = 'UTF-16'
113 with_settings :repositories_encodings => enc do
111 with_settings :repositories_encodings => enc do
114 get :entry, :id => PRJ_ID,
112 get :entry, :id => PRJ_ID,
115 :path => repository_path_hash(['japanese', 'utf-16.txt'])[:param]
113 :path => repository_path_hash(['japanese', 'utf-16.txt'])[:param]
@@ -26,8 +26,8 class RepositoriesGitControllerTest < ActionController::TestCase
26 REPOSITORY_PATH = Rails.root.join('tmp/test/git_repository').to_s
26 REPOSITORY_PATH = Rails.root.join('tmp/test/git_repository').to_s
27 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
27 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
28 PRJ_ID = 3
28 PRJ_ID = 3
29 CHAR_1_HEX = "\xc3\x9c"
29 CHAR_1_HEX = "\xc3\x9c".force_encoding('UTF-8')
30 FELIX_HEX = "Felix Sch\xC3\xA4fer"
30 FELIX_HEX = "Felix Sch\xC3\xA4fer".force_encoding('UTF-8')
31 NUM_REV = 28
31 NUM_REV = 28
32
32
33 ## Git, Mercurial and CVS path encodings are binary.
33 ## Git, Mercurial and CVS path encodings are binary.
@@ -39,8 +39,7 class RepositoriesGitControllerTest < ActionController::TestCase
39 JRUBY_SKIP_STR = "TODO: This test fails in JRuby"
39 JRUBY_SKIP_STR = "TODO: This test fails in JRuby"
40
40
41 def setup
41 def setup
42 @ruby19_non_utf8_pass =
42 @ruby19_non_utf8_pass = Encoding.default_external.to_s != 'UTF-8'
43 (RUBY_VERSION >= '1.9' && Encoding.default_external.to_s != 'UTF-8')
44
43
45 User.current = nil
44 User.current = nil
46 @project = Project.find(PRJ_ID)
45 @project = Project.find(PRJ_ID)
@@ -50,12 +49,6 class RepositoriesGitControllerTest < ActionController::TestCase
50 :path_encoding => 'ISO-8859-1'
49 :path_encoding => 'ISO-8859-1'
51 )
50 )
52 assert @repository
51 assert @repository
53 @char_1 = CHAR_1_HEX.dup
54 @felix_utf8 = FELIX_HEX.dup
55 if @char_1.respond_to?(:force_encoding)
56 @char_1.force_encoding('UTF-8')
57 @felix_utf8.force_encoding('UTF-8')
58 end
59 end
52 end
60
53
61 def test_create_and_update
54 def test_create_and_update
@@ -231,7 +224,7 class RepositoriesGitControllerTest < ActionController::TestCase
231 with_settings :repositories_encodings => 'UTF-8,ISO-8859-1' do
224 with_settings :repositories_encodings => 'UTF-8,ISO-8859-1' do
232 ['57ca437c', '57ca437c0acbbcb749821fdf3726a1367056d364'].each do |r1|
225 ['57ca437c', '57ca437c0acbbcb749821fdf3726a1367056d364'].each do |r1|
233 get :entry, :id => PRJ_ID,
226 get :entry, :id => PRJ_ID,
234 :path => repository_path_hash(['latin-1-dir', "test-#{@char_1}.txt"])[:param],
227 :path => repository_path_hash(['latin-1-dir', "test-#{CHAR_1_HEX}.txt"])[:param],
235 :rev => r1
228 :rev => r1
236 assert_response :success
229 assert_response :success
237 assert_template 'entry'
230 assert_template 'entry'
@@ -239,7 +232,7 class RepositoriesGitControllerTest < ActionController::TestCase
239 :content => '1',
232 :content => '1',
240 :attributes => { :class => 'line-num' },
233 :attributes => { :class => 'line-num' },
241 :sibling => { :tag => 'td',
234 :sibling => { :tag => 'td',
242 :content => /test-#{@char_1}.txt/ }
235 :content => /test-#{CHAR_1_HEX}.txt/ }
243 end
236 end
244 end
237 end
245 end
238 end
@@ -419,14 +412,14 class RepositoriesGitControllerTest < ActionController::TestCase
419 :descendant => {
412 :descendant => {
420 :tag => 'th',
413 :tag => 'th',
421 :attributes => { :class => 'filename' } ,
414 :attributes => { :class => 'filename' } ,
422 :content => /latin-1-dir\/test-#{@char_1}.txt/ ,
415 :content => /latin-1-dir\/test-#{CHAR_1_HEX}.txt/ ,
423 },
416 },
424 :sibling => {
417 :sibling => {
425 :tag => 'tbody',
418 :tag => 'tbody',
426 :descendant => {
419 :descendant => {
427 :tag => 'td',
420 :tag => 'td',
428 :attributes => { :class => /diff_in/ },
421 :attributes => { :class => /diff_in/ },
429 :content => /test-#{@char_1}.txt/
422 :content => /test-#{CHAR_1_HEX}.txt/
430 }
423 }
431 }
424 }
432 end
425 end
@@ -498,11 +491,13 class RepositoriesGitControllerTest < ActionController::TestCase
498 end
491 end
499
492
500 def test_annotate_binary_file
493 def test_annotate_binary_file
501 get :annotate, :id => PRJ_ID,
494 with_settings :default_language => 'en' do
502 :path => repository_path_hash(['images', 'edit.png'])[:param]
495 get :annotate, :id => PRJ_ID,
503 assert_response 500
496 :path => repository_path_hash(['images', 'edit.png'])[:param]
504 assert_tag :tag => 'p', :attributes => { :id => /errorExplanation/ },
497 assert_response 500
505 :content => /cannot be annotated/
498 assert_tag :tag => 'p', :attributes => { :id => /errorExplanation/ },
499 :content => /cannot be annotated/
500 end
506 end
501 end
507
502
508 def test_annotate_error_when_too_big
503 def test_annotate_error_when_too_big
@@ -533,14 +528,14 class RepositoriesGitControllerTest < ActionController::TestCase
533 with_settings :repositories_encodings => 'UTF-8,ISO-8859-1' do
528 with_settings :repositories_encodings => 'UTF-8,ISO-8859-1' do
534 ['57ca437c', '57ca437c0acbbcb749821fdf3726a1367056d364'].each do |r1|
529 ['57ca437c', '57ca437c0acbbcb749821fdf3726a1367056d364'].each do |r1|
535 get :annotate, :id => PRJ_ID,
530 get :annotate, :id => PRJ_ID,
536 :path => repository_path_hash(['latin-1-dir', "test-#{@char_1}.txt"])[:param],
531 :path => repository_path_hash(['latin-1-dir', "test-#{CHAR_1_HEX}.txt"])[:param],
537 :rev => r1
532 :rev => r1
538 assert_select "th.line-num", :text => '1' do
533 assert_select "th.line-num", :text => '1' do
539 assert_select "+ td.revision" do
534 assert_select "+ td.revision" do
540 assert_select "a", :text => '57ca437c'
535 assert_select "a", :text => '57ca437c'
541 assert_select "+ td.author", :text => "jsmith" do
536 assert_select "+ td.author", :text => "jsmith" do
542 assert_select "+ td",
537 assert_select "+ td",
543 :text => "test-#{@char_1}.txt"
538 :text => "test-#{CHAR_1_HEX}.txt"
544 end
539 end
545 end
540 end
546 end
541 end
@@ -557,7 +552,7 class RepositoriesGitControllerTest < ActionController::TestCase
557 assert_select "th.line-num", :text => '1' do
552 assert_select "th.line-num", :text => '1' do
558 assert_select "+ td.revision" do
553 assert_select "+ td.revision" do
559 assert_select "a", :text => '83ca5fd5'
554 assert_select "a", :text => '83ca5fd5'
560 assert_select "+ td.author", :text => @felix_utf8 do
555 assert_select "+ td.author", :text => FELIX_HEX do
561 assert_select "+ td",
556 assert_select "+ td",
562 :text => "And this is a file with a leading and trailing space..."
557 :text => "And this is a file with a leading and trailing space..."
563 end
558 end
@@ -643,8 +638,8 class RepositoriesGitControllerTest < ActionController::TestCase
643 private
638 private
644
639
645 def puts_ruby19_non_utf8_pass
640 def puts_ruby19_non_utf8_pass
646 puts "TODO: This test fails in Ruby 1.9 " +
641 puts "TODO: This test fails " +
647 "and Encoding.default_external is not UTF-8. " +
642 "when Encoding.default_external is not UTF-8. " +
648 "Current value is '#{Encoding.default_external.to_s}'"
643 "Current value is '#{Encoding.default_external.to_s}'"
649 end
644 end
650 else
645 else
@@ -28,8 +28,7 class RepositoriesMercurialControllerTest < ActionController::TestCase
28 PRJ_ID = 3
28 PRJ_ID = 3
29 NUM_REV = 34
29 NUM_REV = 34
30
30
31 ruby19_non_utf8_pass =
31 ruby19_non_utf8_pass = Encoding.default_external.to_s != 'UTF-8'
32 (RUBY_VERSION >= '1.9' && Encoding.default_external.to_s != 'UTF-8')
33
32
34 def setup
33 def setup
35 User.current = nil
34 User.current = nil
@@ -41,21 +40,15 class RepositoriesMercurialControllerTest < ActionController::TestCase
41 )
40 )
42 assert @repository
41 assert @repository
43 @diff_c_support = true
42 @diff_c_support = true
44 @char_1 = CHAR_1_HEX.dup
43 @char_1 = CHAR_1_HEX.dup.force_encoding('UTF-8')
45 @tag_char_1 = "tag-#{CHAR_1_HEX}-00"
44 @tag_char_1 = "tag-#{CHAR_1_HEX}-00".force_encoding('UTF-8')
46 @branch_char_0 = "branch-#{CHAR_1_HEX}-00"
45 @branch_char_0 = "branch-#{CHAR_1_HEX}-00".force_encoding('UTF-8')
47 @branch_char_1 = "branch-#{CHAR_1_HEX}-01"
46 @branch_char_1 = "branch-#{CHAR_1_HEX}-01".force_encoding('UTF-8')
48 if @char_1.respond_to?(:force_encoding)
49 @char_1.force_encoding('UTF-8')
50 @tag_char_1.force_encoding('UTF-8')
51 @branch_char_0.force_encoding('UTF-8')
52 @branch_char_1.force_encoding('UTF-8')
53 end
54 end
47 end
55
48
56 if ruby19_non_utf8_pass
49 if ruby19_non_utf8_pass
57 puts "TODO: Mercurial functional test fails in Ruby 1.9 " +
50 puts "TODO: Mercurial functional test fails " +
58 "and Encoding.default_external is not UTF-8. " +
51 "when Encoding.default_external is not UTF-8. " +
59 "Current value is '#{Encoding.default_external.to_s}'"
52 "Current value is '#{Encoding.default_external.to_s}'"
60 def test_fake; assert true end
53 def test_fake; assert true end
61 elsif File.directory?(REPOSITORY_PATH)
54 elsif File.directory?(REPOSITORY_PATH)
@@ -31,7 +31,7 class RolesControllerTest < ActionController::TestCase
31 assert_template 'index'
31 assert_template 'index'
32
32
33 assert_not_nil assigns(:roles)
33 assert_not_nil assigns(:roles)
34 assert_equal Role.order('builtin, position').all, assigns(:roles)
34 assert_equal Role.order('builtin, position').to_a, assigns(:roles)
35
35
36 assert_tag :tag => 'a', :attributes => { :href => '/roles/1/edit' },
36 assert_tag :tag => 'a', :attributes => { :href => '/roles/1/edit' },
37 :content => 'Manager'
37 :content => 'Manager'
@@ -160,7 +160,7 class RolesControllerTest < ActionController::TestCase
160 assert_template 'permissions'
160 assert_template 'permissions'
161
161
162 assert_not_nil assigns(:roles)
162 assert_not_nil assigns(:roles)
163 assert_equal Role.order('builtin, position').all, assigns(:roles)
163 assert_equal Role.order('builtin, position').to_a, assigns(:roles)
164
164
165 assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
165 assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
166 :name => 'permissions[3][]',
166 :name => 'permissions[3][]',
@@ -40,7 +40,9 class SearchControllerTest < ActionController::TestCase
40 end
40 end
41
41
42 def test_search_all_projects
42 def test_search_all_projects
43 get :index, :q => 'recipe subproject commit', :all_words => ''
43 with_settings :default_language => 'en' do
44 get :index, :q => 'recipe subproject commit', :all_words => ''
45 end
44 assert_response :success
46 assert_response :success
45 assert_template 'index'
47 assert_template 'index'
46
48
@@ -240,12 +240,8 class TimeEntryReportsControllerTest < ActionController::TestCase
240
240
241 def test_csv_big_5
241 def test_csv_big_5
242 Setting.default_language = "zh-TW"
242 Setting.default_language = "zh-TW"
243 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88"
243 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88".force_encoding('UTF-8')
244 str_big5 = "\xa4@\xa4\xeb"
244 str_big5 = "\xa4@\xa4\xeb".force_encoding('Big5')
245 if str_utf8.respond_to?(:force_encoding)
246 str_utf8.force_encoding('UTF-8')
247 str_big5.force_encoding('Big5')
248 end
249 user = User.find_by_id(3)
245 user = User.find_by_id(3)
250 user.firstname = str_utf8
246 user.firstname = str_utf8
251 user.lastname = "test-lastname"
247 user.lastname = "test-lastname"
@@ -270,21 +266,14 class TimeEntryReportsControllerTest < ActionController::TestCase
270 assert_equal 'text/csv; header=present', @response.content_type
266 assert_equal 'text/csv; header=present', @response.content_type
271 lines = @response.body.chomp.split("\n")
267 lines = @response.body.chomp.split("\n")
272 # Headers
268 # Headers
273 s1 = "\xa5\xce\xa4\xe1,2011-11-11,\xa4u\xae\xc9\xc1`\xadp"
269 s1 = "\xa5\xce\xa4\xe1,2011-11-11,\xa4u\xae\xc9\xc1`\xadp".force_encoding('Big5')
274 s2 = "\xa4u\xae\xc9\xc1`\xadp"
270 s2 = "\xa4u\xae\xc9\xc1`\xadp".force_encoding('Big5')
275 if s1.respond_to?(:force_encoding)
276 s1.force_encoding('Big5')
277 s2.force_encoding('Big5')
278 end
279 assert_equal s1, lines.first
271 assert_equal s1, lines.first
280 # Total row
272 # Total row
281 assert_equal "#{str_big5} #{user.lastname},7.30,7.30", lines[1]
273 assert_equal "#{str_big5} #{user.lastname},7.30,7.30", lines[1]
282 assert_equal "#{s2},7.30,7.30", lines[2]
274 assert_equal "#{s2},7.30,7.30", lines[2]
283
275
284 str_tw = "Traditional Chinese (\xe7\xb9\x81\xe9\xab\x94\xe4\xb8\xad\xe6\x96\x87)"
276 str_tw = "Traditional Chinese (\xe7\xb9\x81\xe9\xab\x94\xe4\xb8\xad\xe6\x96\x87)".force_encoding('UTF-8')
285 if str_tw.respond_to?(:force_encoding)
286 str_tw.force_encoding('UTF-8')
287 end
288 assert_equal str_tw, l(:general_lang_name)
277 assert_equal str_tw, l(:general_lang_name)
289 assert_equal 'Big5', l(:general_csv_encoding)
278 assert_equal 'Big5', l(:general_csv_encoding)
290 assert_equal ',', l(:general_csv_separator)
279 assert_equal ',', l(:general_csv_separator)
@@ -293,10 +282,7 class TimeEntryReportsControllerTest < ActionController::TestCase
293
282
294 def test_csv_cannot_convert_should_be_replaced_big_5
283 def test_csv_cannot_convert_should_be_replaced_big_5
295 Setting.default_language = "zh-TW"
284 Setting.default_language = "zh-TW"
296 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85"
285 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85".force_encoding('UTF-8')
297 if str_utf8.respond_to?(:force_encoding)
298 str_utf8.force_encoding('UTF-8')
299 end
300 user = User.find_by_id(3)
286 user = User.find_by_id(3)
301 user.firstname = str_utf8
287 user.firstname = str_utf8
302 user.lastname = "test-lastname"
288 user.lastname = "test-lastname"
@@ -321,21 +307,10 class TimeEntryReportsControllerTest < ActionController::TestCase
321 assert_equal 'text/csv; header=present', @response.content_type
307 assert_equal 'text/csv; header=present', @response.content_type
322 lines = @response.body.chomp.split("\n")
308 lines = @response.body.chomp.split("\n")
323 # Headers
309 # Headers
324 s1 = "\xa5\xce\xa4\xe1,2011-11-11,\xa4u\xae\xc9\xc1`\xadp"
310 s1 = "\xa5\xce\xa4\xe1,2011-11-11,\xa4u\xae\xc9\xc1`\xadp".force_encoding('Big5')
325 if s1.respond_to?(:force_encoding)
326 s1.force_encoding('Big5')
327 end
328 assert_equal s1, lines.first
311 assert_equal s1, lines.first
329 # Total row
312 # Total row
330 s2 = ""
313 s2 = "\xa5H?".force_encoding('Big5')
331 if s2.respond_to?(:force_encoding)
332 s2 = "\xa5H?"
333 s2.force_encoding('Big5')
334 elsif RUBY_PLATFORM == 'java'
335 s2 = "??"
336 else
337 s2 = "\xa5H???"
338 end
339 assert_equal "#{s2} #{user.lastname},7.30,7.30", lines[1]
314 assert_equal "#{s2} #{user.lastname},7.30,7.30", lines[1]
340 end
315 end
341
316
@@ -362,21 +337,14 class TimeEntryReportsControllerTest < ActionController::TestCase
362 assert_equal 'text/csv; header=present', @response.content_type
337 assert_equal 'text/csv; header=present', @response.content_type
363 lines = @response.body.chomp.split("\n")
338 lines = @response.body.chomp.split("\n")
364 # Headers
339 # Headers
365 s1 = "Utilisateur;2011-11-11;Temps total"
340 s1 = "Utilisateur;2011-11-11;Temps total".force_encoding('ISO-8859-1')
366 s2 = "Temps total"
341 s2 = "Temps total".force_encoding('ISO-8859-1')
367 if s1.respond_to?(:force_encoding)
368 s1.force_encoding('ISO-8859-1')
369 s2.force_encoding('ISO-8859-1')
370 end
371 assert_equal s1, lines.first
342 assert_equal s1, lines.first
372 # Total row
343 # Total row
373 assert_equal "#{user.firstname} #{user.lastname};7,30;7,30", lines[1]
344 assert_equal "#{user.firstname} #{user.lastname};7,30;7,30", lines[1]
374 assert_equal "#{s2};7,30;7,30", lines[2]
345 assert_equal "#{s2};7,30;7,30", lines[2]
375
346
376 str_fr = "Fran\xc3\xa7ais"
347 str_fr = "Fran\xc3\xa7ais".force_encoding('UTF-8')
377 if str_fr.respond_to?(:force_encoding)
378 str_fr.force_encoding('UTF-8')
379 end
380 assert_equal str_fr, l(:general_lang_name)
348 assert_equal str_fr, l(:general_lang_name)
381 assert_equal 'ISO-8859-1', l(:general_csv_encoding)
349 assert_equal 'ISO-8859-1', l(:general_csv_encoding)
382 assert_equal ';', l(:general_csv_separator)
350 assert_equal ';', l(:general_csv_separator)
@@ -702,16 +702,18 class TimelogControllerTest < ActionController::TestCase
702 end
702 end
703
703
704 def test_index_csv_all_projects
704 def test_index_csv_all_projects
705 Setting.date_format = '%m/%d/%Y'
705 with_settings :date_format => '%m/%d/%Y' do
706 get :index, :format => 'csv'
706 get :index, :format => 'csv'
707 assert_response :success
707 assert_response :success
708 assert_equal 'text/csv; header=present', response.content_type
708 assert_equal 'text/csv; header=present', response.content_type
709 end
709 end
710 end
710
711
711 def test_index_csv
712 def test_index_csv
712 Setting.date_format = '%m/%d/%Y'
713 with_settings :date_format => '%m/%d/%Y' do
713 get :index, :project_id => 1, :format => 'csv'
714 get :index, :project_id => 1, :format => 'csv'
714 assert_response :success
715 assert_response :success
715 assert_equal 'text/csv; header=present', response.content_type
716 assert_equal 'text/csv; header=present', response.content_type
717 end
716 end
718 end
717 end
719 end
@@ -95,8 +95,8 class TimelogCustomFieldsVisibilityTest < ActionController::TestCase
95 p1 = Project.generate!
95 p1 = Project.generate!
96 p2 = Project.generate!
96 p2 = Project.generate!
97 user = User.generate!
97 user = User.generate!
98 User.add_to_project(user, p1, Role.where(:id => [1, 3]).all)
98 User.add_to_project(user, p1, Role.where(:id => [1, 3]).to_a)
99 User.add_to_project(user, p2, Role.where(:id => 3).all)
99 User.add_to_project(user, p2, Role.where(:id => 3).to_a)
100 TimeEntry.generate!(
100 TimeEntry.generate!(
101 :issue => Issue.generate!(:project => p1, :tracker_id => 1,
101 :issue => Issue.generate!(:project => p1, :tracker_id => 1,
102 :custom_field_values => {@field2.id => 'ValueA'}))
102 :custom_field_values => {@field2.id => 'ValueA'}))
@@ -108,9 +108,9 class TimelogCustomFieldsVisibilityTest < ActionController::TestCase
108 :custom_field_values => {@field2.id => 'ValueC'}))
108 :custom_field_values => {@field2.id => 'ValueC'}))
109 @request.session[:user_id] = user.id
109 @request.session[:user_id] = user.id
110 get :index, :c => ["hours", "issue.cf_#{@field2.id}"]
110 get :index, :c => ["hours", "issue.cf_#{@field2.id}"]
111 assert_select 'td', :text => 'ValueA'
111 assert_select 'td', {:text => 'ValueA'}, "ValueA not found in:\n#{response.body}"
112 assert_select 'td', :text => 'ValueB', :count => 0
112 assert_select 'td', :text => 'ValueB', :count => 0
113 assert_select 'td', :text => 'ValueC'
113 assert_select 'td', {:text => 'ValueC'}, "ValueC not found in:\n#{response.body}"
114
114
115 get :index, :set_filter => '1', "issue.cf_#{@field2.id}" => '*'
115 get :index, :set_filter => '1', "issue.cf_#{@field2.id}" => '*'
116 assert_equal %w(ValueA ValueC), assigns(:entries).map{|i| i.issue.custom_field_value(@field2)}.sort
116 assert_equal %w(ValueA ValueC), assigns(:entries).map{|i| i.issue.custom_field_value(@field2)}.sort
@@ -97,9 +97,7 class WelcomeControllerTest < ActionController::TestCase
97 @request.session[:user_id] = 2
97 @request.session[:user_id] = 2
98
98
99 get :index
99 get :index
100 assert_tag 'script',
100 assert_select 'script', :text => %r{warnLeavingUnsaved}
101 :attributes => {:type => "text/javascript"},
102 :content => %r{warnLeavingUnsaved}
103 end
101 end
104
102
105 def test_warn_on_leaving_unsaved_turn_off
103 def test_warn_on_leaving_unsaved_turn_off
@@ -58,7 +58,9 class WikiControllerTest < ActionController::TestCase
58 end
58 end
59
59
60 def test_show_old_version
60 def test_show_old_version
61 get :show, :project_id => 'ecookbook', :id => 'CookBook_documentation', :version => '2'
61 with_settings :default_language => 'en' do
62 get :show, :project_id => 'ecookbook', :id => 'CookBook_documentation', :version => '2'
63 end
62 assert_response :success
64 assert_response :success
63 assert_template 'show'
65 assert_template 'show'
64
66
@@ -89,7 +91,9 class WikiControllerTest < ActionController::TestCase
89 end
91 end
90
92
91 def test_show_first_version
93 def test_show_first_version
92 get :show, :project_id => 'ecookbook', :id => 'CookBook_documentation', :version => '1'
94 with_settings :default_language => 'en' do
95 get :show, :project_id => 'ecookbook', :id => 'CookBook_documentation', :version => '1'
96 end
93 assert_response :success
97 assert_response :success
94 assert_template 'show'
98 assert_template 'show'
95
99
@@ -248,7 +248,7 class WorkflowsControllerTest < ActionController::TestCase
248
248
249 get :permissions, :role_id => 1, :tracker_id => 2, :used_statuses_only => '0'
249 get :permissions, :role_id => 1, :tracker_id => 2, :used_statuses_only => '0'
250 assert_response :success
250 assert_response :success
251 assert_equal IssueStatus.sorted.all, assigns(:statuses)
251 assert_equal IssueStatus.sorted.to_a, assigns(:statuses)
252 end
252 end
253
253
254 def test_post_permissions
254 def test_post_permissions
@@ -17,13 +17,7
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 begin
20 class AccountTest < ActionDispatch::IntegrationTest
21 require 'mocha/setup'
22 rescue
23 # Won't run some tests
24 end
25
26 class AccountTest < ActionController::IntegrationTest
27 fixtures :users, :roles
21 fixtures :users, :roles
28
22
29 def test_login
23 def test_login
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class AdminTest < ActionController::IntegrationTest
20 class AdminTest < ActionDispatch::IntegrationTest
21 fixtures :projects, :trackers, :issue_statuses, :issues,
21 fixtures :projects, :trackers, :issue_statuses, :issues,
22 :enumerations, :users, :issue_categories,
22 :enumerations, :users, :issue_categories,
23 :projects_trackers,
23 :projects_trackers,
@@ -41,11 +41,8 class Redmine::ApiTest::AuthenticationTest < Redmine::ApiTest::Base
41 end
41 end
42
42
43 def test_invalid_utf8_credentials_should_not_trigger_an_error
43 def test_invalid_utf8_credentials_should_not_trigger_an_error
44 invalid_utf8 = "\x82"
44 invalid_utf8 = "\x82".force_encoding('UTF-8')
45 if invalid_utf8.respond_to?(:force_encoding)
45 assert !invalid_utf8.valid_encoding?
46 invalid_utf8.force_encoding('UTF-8')
47 assert !invalid_utf8.valid_encoding?
48 end
49 assert_nothing_raised do
46 assert_nothing_raised do
50 get '/users/current.xml', {}, credentials(invalid_utf8, "foo")
47 get '/users/current.xml', {}, credentials(invalid_utf8, "foo")
51 end
48 end
@@ -165,7 +165,7 class Redmine::ApiTest::MembershipsTest < Redmine::ApiTest::Base
165
165
166 assert_response :unprocessable_entity
166 assert_response :unprocessable_entity
167 assert_equal 'application/xml', @response.content_type
167 assert_equal 'application/xml', @response.content_type
168 assert_tag 'errors', :child => {:tag => 'error', :content => /member_roles is invalid/}
168 assert_tag 'errors', :child => {:tag => 'error', :content => /role can't be empty/i}
169 end
169 end
170
170
171 test "DELETE /memberships/:id.xml should destroy the membership" do
171 test "DELETE /memberships/:id.xml should destroy the membership" do
@@ -195,12 +195,12 class Redmine::ApiTest::ProjectsTest < Redmine::ApiTest::Base
195 end
195 end
196
196
197 test "POST /projects.xml with valid parameters should create the project" do
197 test "POST /projects.xml with valid parameters should create the project" do
198 Setting.default_projects_modules = ['issue_tracking', 'repository']
198 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
199
199 assert_difference('Project.count') do
200 assert_difference('Project.count') do
200 post '/projects.xml',
201 post '/projects.xml',
201 {:project => {:name => 'API test', :identifier => 'api-test'}},
202 {:project => {:name => 'API test', :identifier => 'api-test'}},
202 credentials('admin')
203 credentials('admin')
203 end
204 end
204 end
205
205
206 project = Project.order('id DESC').first
206 project = Project.order('id DESC').first
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class ApplicationTest < ActionController::IntegrationTest
20 class ApplicationTest < ActionDispatch::IntegrationTest
21 include Redmine::I18n
21 include Redmine::I18n
22
22
23 fixtures :projects, :trackers, :issue_statuses, :issues,
23 fixtures :projects, :trackers, :issue_statuses, :issues,
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class AttachmentsTest < ActionController::IntegrationTest
20 class AttachmentsTest < ActionDispatch::IntegrationTest
21 fixtures :projects, :enabled_modules,
21 fixtures :projects, :enabled_modules,
22 :users, :roles, :members, :member_roles,
22 :users, :roles, :members, :member_roles,
23 :trackers, :projects_trackers,
23 :trackers, :projects_trackers,
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class FeedsTest < ActionController::IntegrationTest
20 class FeedsTest < ActionDispatch::IntegrationTest
21 fixtures :projects, :trackers, :issue_statuses, :issues,
21 fixtures :projects, :trackers, :issue_statuses, :issues,
22 :enumerations, :users, :issue_categories,
22 :enumerations, :users, :issue_categories,
23 :projects_trackers, :enabled_modules,
23 :projects_trackers, :enabled_modules,
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class IssuesTest < ActionController::IntegrationTest
20 class IssuesTest < ActionDispatch::IntegrationTest
21 fixtures :projects,
21 fixtures :projects,
22 :users,
22 :users,
23 :roles,
23 :roles,
@@ -132,27 +132,27 class IssuesTest < ActionController::IntegrationTest
132 end
132 end
133
133
134 def test_pagination_links_on_index
134 def test_pagination_links_on_index
135 Setting.per_page_options = '2'
135 with_settings :per_page_options => '2' do
136 get '/projects/ecookbook/issues'
136 get '/projects/ecookbook/issues'
137
138 assert_tag :a, :content => '2',
139 :attributes => { :href => '/projects/ecookbook/issues?page=2' }
140
137
138 assert_tag :a, :content => '2',
139 :attributes => { :href => '/projects/ecookbook/issues?page=2' }
140 end
141 end
141 end
142
142
143 def test_pagination_links_on_index_without_project_id_in_url
143 def test_pagination_links_on_index_without_project_id_in_url
144 Setting.per_page_options = '2'
144 with_settings :per_page_options => '2' do
145 get '/issues', :project_id => 'ecookbook'
145 get '/issues', :project_id => 'ecookbook'
146
146
147 assert_tag :a, :content => '2',
147 assert_tag :a, :content => '2',
148 :attributes => { :href => '/projects/ecookbook/issues?page=2' }
148 :attributes => { :href => '/projects/ecookbook/issues?page=2' }
149
149 end
150 end
150 end
151
151
152 def test_issue_with_user_custom_field
152 def test_issue_with_user_custom_field
153 @field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true, :trackers => Tracker.all)
153 @field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true, :trackers => Tracker.all)
154 Role.anonymous.add_permission! :add_issues, :edit_issues
154 Role.anonymous.add_permission! :add_issues, :edit_issues
155 users = Project.find(1).users
155 users = Project.find(1).users.uniq.sort
156 tester = users.first
156 tester = users.first
157
157
158 # Issue form
158 # Issue form
@@ -205,8 +205,8 class IssuesTest < ActionController::IntegrationTest
205 :issue => {
205 :issue => {
206 :custom_field_values => {@field.id.to_s => new_tester.id.to_s}
206 :custom_field_values => {@field.id.to_s => new_tester.id.to_s}
207 }
207 }
208 assert_redirected_to "/issues/#{issue.id}"
208 end
209 end
209 assert_response 302
210
210
211 # Issue view
211 # Issue view
212 follow_redirect!
212 follow_redirect!
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class LayoutTest < ActionController::IntegrationTest
20 class LayoutTest < ActionDispatch::IntegrationTest
21 fixtures :projects, :trackers, :issue_statuses, :issues,
21 fixtures :projects, :trackers, :issue_statuses, :issues,
22 :enumerations, :users, :issue_categories,
22 :enumerations, :users, :issue_categories,
23 :projects_trackers,
23 :projects_trackers,
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../../test_helper', __FILE__)
18 require File.expand_path('../../../../test_helper', __FILE__)
19
19
20 class HookTest < ActionController::IntegrationTest
20 class HookTest < ActionDispatch::IntegrationTest
21 fixtures :users, :roles, :projects, :members, :member_roles
21 fixtures :users, :roles, :projects, :members, :member_roles
22
22
23 # Hooks that are manually registered later
23 # Hooks that are manually registered later
@@ -34,6 +34,8 class HookTest < ActionController::IntegrationTest
34 end
34 end
35 end
35 end
36
36
37 Redmine::Hook.clear_listeners
38
37 class ContentForInsideHook < Redmine::Hook::ViewListener
39 class ContentForInsideHook < Redmine::Hook::ViewListener
38 render_on :view_welcome_index_left, :inline => <<-VIEW
40 render_on :view_welcome_index_left, :inline => <<-VIEW
39 <% content_for :header_tags do %>
41 <% content_for :header_tags do %>
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../../test_helper', __FILE__)
18 require File.expand_path('../../../../test_helper', __FILE__)
19
19
20 class MenuManagerTest < ActionController::IntegrationTest
20 class MenuManagerTest < ActionDispatch::IntegrationTest
21 include Redmine::I18n
21 include Redmine::I18n
22
22
23 fixtures :projects, :trackers, :issue_statuses, :issues,
23 fixtures :projects, :trackers, :issue_statuses, :issues,
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../../test_helper', __FILE__)
18 require File.expand_path('../../../../test_helper', __FILE__)
19
19
20 class ThemesTest < ActionController::IntegrationTest
20 class ThemesTest < ActionDispatch::IntegrationTest
21
21
22 def setup
22 def setup
23 @theme = Redmine::Themes.themes.last
23 @theme = Redmine::Themes.themes.last
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class ProjectsTest < ActionController::IntegrationTest
20 class ProjectsTest < ActionDispatch::IntegrationTest
21 fixtures :projects, :users, :members, :enabled_modules
21 fixtures :projects, :users, :members, :enabled_modules
22
22
23 def test_archive_project
23 def test_archive_project
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class RepositoriesGitTest < ActionController::IntegrationTest
20 class RepositoriesGitTest < ActionDispatch::IntegrationTest
21 fixtures :projects, :users, :roles, :members, :member_roles,
21 fixtures :projects, :users, :roles, :members, :member_roles,
22 :repositories, :enabled_modules
22 :repositories, :enabled_modules
23
23
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingAccountTest < ActionController::IntegrationTest
20 class RoutingAccountTest < ActionDispatch::IntegrationTest
21 def test_account
21 def test_account
22 ["get", "post"].each do |method|
22 ["get", "post"].each do |method|
23 assert_routing(
23 assert_routing(
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingActivitiesTest < ActionController::IntegrationTest
20 class RoutingActivitiesTest < ActionDispatch::IntegrationTest
21 def test_activities
21 def test_activities
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/activity" },
23 { :method => 'get', :path => "/activity" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingAdminTest < ActionController::IntegrationTest
20 class RoutingAdminTest < ActionDispatch::IntegrationTest
21 def test_administration_panel
21 def test_administration_panel
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/admin" },
23 { :method => 'get', :path => "/admin" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingAttachmentsTest < ActionController::IntegrationTest
20 class RoutingAttachmentsTest < ActionDispatch::IntegrationTest
21 def test_attachments
21 def test_attachments
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/attachments/1" },
23 { :method => 'get', :path => "/attachments/1" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingAuthSourcesTest < ActionController::IntegrationTest
20 class RoutingAuthSourcesTest < ActionDispatch::IntegrationTest
21 def test_auth_sources
21 def test_auth_sources
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/auth_sources" },
23 { :method => 'get', :path => "/auth_sources" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingAutoCompletesTest < ActionController::IntegrationTest
20 class RoutingAutoCompletesTest < ActionDispatch::IntegrationTest
21 def test_auto_completes
21 def test_auto_completes
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/issues/auto_complete" },
23 { :method => 'get', :path => "/issues/auto_complete" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingBoardsTest < ActionController::IntegrationTest
20 class RoutingBoardsTest < ActionDispatch::IntegrationTest
21 def test_boards
21 def test_boards
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/projects/world_domination/boards" },
23 { :method => 'get', :path => "/projects/world_domination/boards" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingCalendarsTest < ActionController::IntegrationTest
20 class RoutingCalendarsTest < ActionDispatch::IntegrationTest
21 def test_calendars
21 def test_calendars
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/issues/calendar" },
23 { :method => 'get', :path => "/issues/calendar" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingCommentsTest < ActionController::IntegrationTest
20 class RoutingCommentsTest < ActionDispatch::IntegrationTest
21 def test_comments
21 def test_comments
22 assert_routing(
22 assert_routing(
23 { :method => 'post', :path => "/news/567/comments" },
23 { :method => 'post', :path => "/news/567/comments" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingContextMenusTest < ActionController::IntegrationTest
20 class RoutingContextMenusTest < ActionDispatch::IntegrationTest
21 def test_context_menus_time_entries
21 def test_context_menus_time_entries
22 ["get", "post"].each do |method|
22 ["get", "post"].each do |method|
23 assert_routing(
23 assert_routing(
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingCustomFieldsTest < ActionController::IntegrationTest
20 class RoutingCustomFieldsTest < ActionDispatch::IntegrationTest
21 def test_custom_fields
21 def test_custom_fields
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/custom_fields" },
23 { :method => 'get', :path => "/custom_fields" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingDocumentsTest < ActionController::IntegrationTest
20 class RoutingDocumentsTest < ActionDispatch::IntegrationTest
21 def test_documents_scoped_under_project
21 def test_documents_scoped_under_project
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/projects/567/documents" },
23 { :method => 'get', :path => "/projects/567/documents" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingEnumerationsTest < ActionController::IntegrationTest
20 class RoutingEnumerationsTest < ActionDispatch::IntegrationTest
21 def test_enumerations
21 def test_enumerations
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/enumerations" },
23 { :method => 'get', :path => "/enumerations" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingFilesTest < ActionController::IntegrationTest
20 class RoutingFilesTest < ActionDispatch::IntegrationTest
21 def test_files
21 def test_files
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/projects/33/files" },
23 { :method => 'get', :path => "/projects/33/files" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingGanttsTest < ActionController::IntegrationTest
20 class RoutingGanttsTest < ActionDispatch::IntegrationTest
21 def test_gantts
21 def test_gantts
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/issues/gantt" },
23 { :method => 'get', :path => "/issues/gantt" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingGroupsTest < ActionController::IntegrationTest
20 class RoutingGroupsTest < ActionDispatch::IntegrationTest
21 def test_groups_resources
21 def test_groups_resources
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/groups" },
23 { :method => 'get', :path => "/groups" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingIssueCategoriesTest < ActionController::IntegrationTest
20 class RoutingIssueCategoriesTest < ActionDispatch::IntegrationTest
21 def test_issue_categories_scoped_under_project
21 def test_issue_categories_scoped_under_project
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/projects/foo/issue_categories" },
23 { :method => 'get', :path => "/projects/foo/issue_categories" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingIssueRelationsTest < ActionController::IntegrationTest
20 class RoutingIssueRelationsTest < ActionDispatch::IntegrationTest
21 def test_issue_relations
21 def test_issue_relations
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/issues/1/relations" },
23 { :method => 'get', :path => "/issues/1/relations" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingIssueStatusesTest < ActionController::IntegrationTest
20 class RoutingIssueStatusesTest < ActionDispatch::IntegrationTest
21 def test_issue_statuses
21 def test_issue_statuses
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/issue_statuses" },
23 { :method => 'get', :path => "/issue_statuses" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingIssuesTest < ActionController::IntegrationTest
20 class RoutingIssuesTest < ActionDispatch::IntegrationTest
21 def test_issues_rest_actions
21 def test_issues_rest_actions
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/issues" },
23 { :method => 'get', :path => "/issues" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingJournalsTest < ActionController::IntegrationTest
20 class RoutingJournalsTest < ActionDispatch::IntegrationTest
21 def test_journals
21 def test_journals
22 assert_routing(
22 assert_routing(
23 { :method => 'post', :path => "/issues/1/quoted" },
23 { :method => 'post', :path => "/issues/1/quoted" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingMailHandlerTest < ActionController::IntegrationTest
20 class RoutingMailHandlerTest < ActionDispatch::IntegrationTest
21 def test_mail_handler
21 def test_mail_handler
22 assert_routing(
22 assert_routing(
23 { :method => "post", :path => "/mail_handler" },
23 { :method => "post", :path => "/mail_handler" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingMembersTest < ActionController::IntegrationTest
20 class RoutingMembersTest < ActionDispatch::IntegrationTest
21 def test_members
21 def test_members
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/projects/5234/memberships.xml" },
23 { :method => 'get', :path => "/projects/5234/memberships.xml" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingMessagesTest < ActionController::IntegrationTest
20 class RoutingMessagesTest < ActionDispatch::IntegrationTest
21 def test_messages
21 def test_messages
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/boards/22/topics/2" },
23 { :method => 'get', :path => "/boards/22/topics/2" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingMyTest < ActionController::IntegrationTest
20 class RoutingMyTest < ActionDispatch::IntegrationTest
21 def test_my
21 def test_my
22 ["get", "post"].each do |method|
22 ["get", "post"].each do |method|
23 assert_routing(
23 assert_routing(
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingNewsTest < ActionController::IntegrationTest
20 class RoutingNewsTest < ActionDispatch::IntegrationTest
21 def test_news_index
21 def test_news_index
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/news" },
23 { :method => 'get', :path => "/news" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingPreviewsTest < ActionController::IntegrationTest
20 class RoutingPreviewsTest < ActionDispatch::IntegrationTest
21 def test_previews
21 def test_previews
22 ["get", "post", "put"].each do |method|
22 ["get", "post", "put"].each do |method|
23 assert_routing(
23 assert_routing(
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingProjectEnumerationsTest < ActionController::IntegrationTest
20 class RoutingProjectEnumerationsTest < ActionDispatch::IntegrationTest
21 def test_project_enumerations
21 def test_project_enumerations
22 assert_routing(
22 assert_routing(
23 { :method => 'put', :path => "/projects/64/enumerations" },
23 { :method => 'put', :path => "/projects/64/enumerations" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingProjectsTest < ActionController::IntegrationTest
20 class RoutingProjectsTest < ActionDispatch::IntegrationTest
21 def test_projects
21 def test_projects
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/projects" },
23 { :method => 'get', :path => "/projects" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingQueriesTest < ActionController::IntegrationTest
20 class RoutingQueriesTest < ActionDispatch::IntegrationTest
21 def test_queries
21 def test_queries
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/queries.xml" },
23 { :method => 'get', :path => "/queries.xml" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingReportsTest < ActionController::IntegrationTest
20 class RoutingReportsTest < ActionDispatch::IntegrationTest
21 def test_reports
21 def test_reports
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/projects/567/issues/report" },
23 { :method => 'get', :path => "/projects/567/issues/report" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingRepositoriesTest < ActionController::IntegrationTest
20 class RoutingRepositoriesTest < ActionDispatch::IntegrationTest
21 def setup
21 def setup
22 @path_hash = repository_path_hash(%w[path to file.c])
22 @path_hash = repository_path_hash(%w[path to file.c])
23 assert_equal "path/to/file.c", @path_hash[:path]
23 assert_equal "path/to/file.c", @path_hash[:path]
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingRolesTest < ActionController::IntegrationTest
20 class RoutingRolesTest < ActionDispatch::IntegrationTest
21 def test_roles
21 def test_roles
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/roles" },
23 { :method => 'get', :path => "/roles" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingSearchTest < ActionController::IntegrationTest
20 class RoutingSearchTest < ActionDispatch::IntegrationTest
21 def test_search
21 def test_search
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/search" },
23 { :method => 'get', :path => "/search" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingSettingsTest < ActionController::IntegrationTest
20 class RoutingSettingsTest < ActionDispatch::IntegrationTest
21 def test_settings
21 def test_settings
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/settings" },
23 { :method => 'get', :path => "/settings" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingSysTest < ActionController::IntegrationTest
20 class RoutingSysTest < ActionDispatch::IntegrationTest
21 def test_sys
21 def test_sys
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/sys/projects" },
23 { :method => 'get', :path => "/sys/projects" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingTimelogsTest < ActionController::IntegrationTest
20 class RoutingTimelogsTest < ActionDispatch::IntegrationTest
21 def test_timelogs_global
21 def test_timelogs_global
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/time_entries" },
23 { :method => 'get', :path => "/time_entries" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingTrackersTest < ActionController::IntegrationTest
20 class RoutingTrackersTest < ActionDispatch::IntegrationTest
21 def test_trackers
21 def test_trackers
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/trackers" },
23 { :method => 'get', :path => "/trackers" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingUsersTest < ActionController::IntegrationTest
20 class RoutingUsersTest < ActionDispatch::IntegrationTest
21 def test_users
21 def test_users
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/users" },
23 { :method => 'get', :path => "/users" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingVersionsTest < ActionController::IntegrationTest
20 class RoutingVersionsTest < ActionDispatch::IntegrationTest
21 def test_roadmap
21 def test_roadmap
22 # /projects/foo/versions is /projects/foo/roadmap
22 # /projects/foo/versions is /projects/foo/roadmap
23 assert_routing(
23 assert_routing(
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingWatchersTest < ActionController::IntegrationTest
20 class RoutingWatchersTest < ActionDispatch::IntegrationTest
21 def test_watchers
21 def test_watchers
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/watchers/new" },
23 { :method => 'get', :path => "/watchers/new" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingWelcomeTest < ActionController::IntegrationTest
20 class RoutingWelcomeTest < ActionDispatch::IntegrationTest
21 def test_welcome
21 def test_welcome
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/" },
23 { :method => 'get', :path => "/" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingWikiTest < ActionController::IntegrationTest
20 class RoutingWikiTest < ActionDispatch::IntegrationTest
21 def test_wiki_matching
21 def test_wiki_matching
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/projects/567/wiki" },
23 { :method => 'get', :path => "/projects/567/wiki" },
@@ -54,7 +54,7 class RoutingWikiTest < ActionController::IntegrationTest
54 :id => 'CookBook_documentation', :version => '2' }
54 :id => 'CookBook_documentation', :version => '2' }
55 )
55 )
56 # Make sure we don't route wiki page sub-uris to let plugins handle them
56 # Make sure we don't route wiki page sub-uris to let plugins handle them
57 assert_raise(ActionController::RoutingError) do
57 assert_raise(Minitest::Assertion) do
58 assert_recognizes({}, {:method => 'get', :path => "/projects/1/wiki/CookBook_documentation/whatever"})
58 assert_recognizes({}, {:method => 'get', :path => "/projects/1/wiki/CookBook_documentation/whatever"})
59 end
59 end
60 end
60 end
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingWikisTest < ActionController::IntegrationTest
20 class RoutingWikisTest < ActionDispatch::IntegrationTest
21 def test_wikis_plural_admin_setup
21 def test_wikis_plural_admin_setup
22 ["get", "post"].each do |method|
22 ["get", "post"].each do |method|
23 assert_routing(
23 assert_routing(
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingWorkflowsTest < ActionController::IntegrationTest
20 class RoutingWorkflowsTest < ActionDispatch::IntegrationTest
21 def test_workflows
21 def test_workflows
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/workflows" },
23 { :method => 'get', :path => "/workflows" },
@@ -17,7 +17,7
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class UsersTest < ActionController::IntegrationTest
20 class UsersTest < ActionDispatch::IntegrationTest
21 fixtures :users
21 fixtures :users
22
22
23 def test_destroy_should_not_accept_get_requests
23 def test_destroy_should_not_accept_get_requests
@@ -14,7 +14,7 module ObjectHelpers
14
14
15 def User.add_to_project(user, project, roles=nil)
15 def User.add_to_project(user, project, roles=nil)
16 roles = Role.find(1) if roles.nil?
16 roles = Role.find(1) if roles.nil?
17 roles = [roles] unless roles.is_a?(Array)
17 roles = [roles] if roles.is_a?(Role)
18 Member.create!(:principal => user, :project => project, :roles => roles)
18 Member.create!(:principal => user, :project => project, :roles => roles)
19 end
19 end
20
20
@@ -178,14 +178,6 module ObjectHelpers
178 changeset.save!
178 changeset.save!
179 changeset
179 changeset
180 end
180 end
181
182 def Query.generate!(attributes={})
183 query = new(attributes)
184 query.name = "Generated query" if query.name.blank?
185 query.user ||= User.find(1)
186 query.save!
187 query
188 end
189 end
181 end
190
182
191 module IssueObjectHelpers
183 module IssueObjectHelpers
@@ -26,17 +26,25 include ObjectHelpers
26
26
27 require 'awesome_nested_set/version'
27 require 'awesome_nested_set/version'
28
28
29 class ActionView::TestCase
30 helper :application
31 include ApplicationHelper
32 end
33
29 class ActiveSupport::TestCase
34 class ActiveSupport::TestCase
30 include ActionDispatch::TestProcess
35 include ActionDispatch::TestProcess
36 include Shoulda::Context::Assertions
37 include Shoulda::Context::InstanceMethods
38 extend Shoulda::Context::ClassMethods
31
39
32 self.use_transactional_fixtures = true
40 self.use_transactional_fixtures = true
33 self.use_instantiated_fixtures = false
41 self.use_instantiated_fixtures = false
34
42
35 ESCAPED_CANT = 'can&#x27;t'
43 #ESCAPED_CANT = 'can&#x27;t'
36 ESCAPED_UCANT = 'Can&#x27;t'
44 #ESCAPED_UCANT = 'Can&#x27;t'
37 # Rails 4.0.2
45 # Rails 4.0.2
38 #ESCAPED_CANT = 'can&#39;t'
46 ESCAPED_CANT = 'can&#39;t'
39 #ESCAPED_UCANT = 'Can&#39;t'
47 ESCAPED_UCANT = 'Can&#39;t'
40
48
41 def log_user(login, password)
49 def log_user(login, password)
42 User.anonymous
50 User.anonymous
@@ -147,7 +155,9 class ActiveSupport::TestCase
147
155
148 # Returns the path to the test +vendor+ repository
156 # Returns the path to the test +vendor+ repository
149 def self.repository_path(vendor)
157 def self.repository_path(vendor)
150 Rails.root.join("tmp/test/#{vendor.downcase}_repository").to_s
158 path = Rails.root.join("tmp/test/#{vendor.downcase}_repository").to_s
159 # Unlike ruby, JRuby returns Rails.root with backslashes under Windows
160 path.tr("\\", "/")
151 end
161 end
152
162
153 # Returns the url of the subversion test repository
163 # Returns the url of the subversion test repository
@@ -388,8 +388,7 class ChangesetTest < ActiveSupport::TestCase
388 def test_comments_should_be_converted_to_utf8
388 def test_comments_should_be_converted_to_utf8
389 proj = Project.find(3)
389 proj = Project.find(3)
390 # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
390 # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
391 str = "Texte encod\xe9 en ISO-8859-1."
391 str = "Texte encod\xe9 en ISO-8859-1.".force_encoding("ASCII-8BIT")
392 str.force_encoding("ASCII-8BIT") if str.respond_to?(:force_encoding)
393 r = Repository::Bazaar.create!(
392 r = Repository::Bazaar.create!(
394 :project => proj,
393 :project => proj,
395 :url => '/tmp/test/bazaar',
394 :url => '/tmp/test/bazaar',
@@ -401,18 +400,15 class ChangesetTest < ActiveSupport::TestCase
401 :scmid => '12345',
400 :scmid => '12345',
402 :comments => str)
401 :comments => str)
403 assert( c.save )
402 assert( c.save )
404 str_utf8 = "Texte encod\xc3\xa9 en ISO-8859-1."
403 str_utf8 = "Texte encod\xc3\xa9 en ISO-8859-1.".force_encoding("UTF-8")
405 str_utf8.force_encoding("UTF-8") if str_utf8.respond_to?(:force_encoding)
406 assert_equal str_utf8, c.comments
404 assert_equal str_utf8, c.comments
407 end
405 end
408
406
409 def test_invalid_utf8_sequences_in_comments_should_be_replaced_latin1
407 def test_invalid_utf8_sequences_in_comments_should_be_replaced_latin1
410 proj = Project.find(3)
408 proj = Project.find(3)
411 # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
409 # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
412 str1 = "Texte encod\xe9 en ISO-8859-1."
410 str1 = "Texte encod\xe9 en ISO-8859-1.".force_encoding("UTF-8")
413 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
411 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test".force_encoding("ASCII-8BIT")
414 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
415 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
416 r = Repository::Bazaar.create!(
412 r = Repository::Bazaar.create!(
417 :project => proj,
413 :project => proj,
418 :url => '/tmp/test/bazaar',
414 :url => '/tmp/test/bazaar',
@@ -431,10 +427,7 class ChangesetTest < ActiveSupport::TestCase
431
427
432 def test_invalid_utf8_sequences_in_comments_should_be_replaced_ja_jis
428 def test_invalid_utf8_sequences_in_comments_should_be_replaced_ja_jis
433 proj = Project.find(3)
429 proj = Project.find(3)
434 str = "test\xb5\xfetest\xb5\xfe"
430 str = "test\xb5\xfetest\xb5\xfe".force_encoding('ASCII-8BIT')
435 if str.respond_to?(:force_encoding)
436 str.force_encoding('ASCII-8BIT')
437 end
438 r = Repository::Bazaar.create!(
431 r = Repository::Bazaar.create!(
439 :project => proj,
432 :project => proj,
440 :url => '/tmp/test/bazaar',
433 :url => '/tmp/test/bazaar',
@@ -453,14 +446,12 class ChangesetTest < ActiveSupport::TestCase
453 s1 = "\xC2\x80"
446 s1 = "\xC2\x80"
454 s2 = "\xc3\x82\xc2\x80"
447 s2 = "\xc3\x82\xc2\x80"
455 s4 = s2.dup
448 s4 = s2.dup
456 if s1.respond_to?(:force_encoding)
449 s3 = s1.dup
457 s3 = s1.dup
450 s1.force_encoding('ASCII-8BIT')
458 s1.force_encoding('ASCII-8BIT')
451 s2.force_encoding('ASCII-8BIT')
459 s2.force_encoding('ASCII-8BIT')
452 s3.force_encoding('ISO-8859-1')
460 s3.force_encoding('ISO-8859-1')
453 s4.force_encoding('UTF-8')
461 s4.force_encoding('UTF-8')
454 assert_equal s3.encode('UTF-8'), s4
462 assert_equal s3.encode('UTF-8'), s4
463 end
464 proj = Project.find(3)
455 proj = Project.find(3)
465 r = Repository::Bazaar.create!(
456 r = Repository::Bazaar.create!(
466 :project => proj,
457 :project => proj,
@@ -478,10 +469,8 class ChangesetTest < ActiveSupport::TestCase
478
469
479 def test_invalid_utf8_sequences_in_paths_should_be_replaced
470 def test_invalid_utf8_sequences_in_paths_should_be_replaced
480 proj = Project.find(3)
471 proj = Project.find(3)
481 str1 = "Texte encod\xe9 en ISO-8859-1"
472 str1 = "Texte encod\xe9 en ISO-8859-1".force_encoding("UTF-8")
482 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
473 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test".force_encoding("ASCII-8BIT")
483 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
484 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
485 r = Repository::Bazaar.create!(
474 r = Repository::Bazaar.create!(
486 :project => proj,
475 :project => proj,
487 :url => '/tmp/test/bazaar',
476 :url => '/tmp/test/bazaar',
@@ -521,9 +510,7 class ChangesetTest < ActiveSupport::TestCase
521 assert( c.save )
510 assert( c.save )
522 assert_equal "", c.comments
511 assert_equal "", c.comments
523 assert_equal nil, c.committer
512 assert_equal nil, c.committer
524 if c.comments.respond_to?(:force_encoding)
513 assert_equal "UTF-8", c.comments.encoding.to_s
525 assert_equal "UTF-8", c.comments.encoding.to_s
526 end
527 end
514 end
528
515
529 def test_comments_empty
516 def test_comments_empty
@@ -542,10 +529,8 class ChangesetTest < ActiveSupport::TestCase
542 assert( c.save )
529 assert( c.save )
543 assert_equal "", c.comments
530 assert_equal "", c.comments
544 assert_equal "", c.committer
531 assert_equal "", c.committer
545 if c.comments.respond_to?(:force_encoding)
532 assert_equal "UTF-8", c.comments.encoding.to_s
546 assert_equal "UTF-8", c.comments.encoding.to_s
533 assert_equal "UTF-8", c.committer.encoding.to_s
547 assert_equal "UTF-8", c.committer.encoding.to_s
548 end
549 end
534 end
550
535
551 def test_comments_should_accept_more_than_64k
536 def test_comments_should_accept_more_than_64k
@@ -95,14 +95,12 class CustomFieldTest < ActiveSupport::TestCase
95 assert_equal ["One value", "And another one"], field.possible_values
95 assert_equal ["One value", "And another one"], field.possible_values
96 end
96 end
97
97
98 if "string".respond_to?(:encoding)
98 def test_possible_values_stored_as_binary_should_be_utf8_encoded
99 def test_possible_values_stored_as_binary_should_be_utf8_encoded
99 field = CustomField.find(11)
100 field = CustomField.find(11)
100 assert_kind_of Array, field.possible_values
101 assert_kind_of Array, field.possible_values
101 assert field.possible_values.size > 0
102 assert field.possible_values.size > 0
102 field.possible_values.each do |value|
103 field.possible_values.each do |value|
103 assert_equal "UTF-8", value.encoding.name
104 assert_equal "UTF-8", value.encoding.name
105 end
106 end
104 end
107 end
105 end
108
106
@@ -22,7 +22,7 class EnabledModuleTest < ActiveSupport::TestCase
22
22
23 def test_enabling_wiki_should_create_a_wiki
23 def test_enabling_wiki_should_create_a_wiki
24 CustomField.delete_all
24 CustomField.delete_all
25 project = Project.create!(:name => 'Project with wiki', :identifier => 'wikiproject')
25 project = Project.create!(:name => 'Project with wiki', :identifier => 'wikiproject', :enabled_module_names => [])
26 assert_nil project.wiki
26 assert_nil project.wiki
27 project.enabled_module_names = ['wiki']
27 project.enabled_module_names = ['wiki']
28 project.reload
28 project.reload
@@ -52,8 +52,7 class GroupTest < ActiveSupport::TestCase
52
52
53 def test_blank_name_error_message_fr
53 def test_blank_name_error_message_fr
54 set_language_if_valid 'fr'
54 set_language_if_valid 'fr'
55 str = "Nom doit \xc3\xaatre renseign\xc3\xa9(e)"
55 str = "Nom doit \xc3\xaatre renseign\xc3\xa9(e)".force_encoding('UTF-8')
56 str.force_encoding('UTF-8') if str.respond_to?(:force_encoding)
57 g = Group.new
56 g = Group.new
58 assert !g.save
57 assert !g.save
59 assert_include str, g.errors.full_messages
58 assert_include str, g.errors.full_messages
@@ -133,20 +132,4 class GroupTest < ActiveSupport::TestCase
133
132
134 assert_equal nil, Issue.find(1).assigned_to_id
133 assert_equal nil, Issue.find(1).assigned_to_id
135 end
134 end
136
137 def test_builtin_id_with_anonymous_user_should_return_anonymous_group
138 assert_equal 13, Group.builtin_id(User.anonymous)
139 end
140
141 def test_builtin_id_with_anonymous_role_should_return_anonymous_group
142 assert_equal 13, Group.builtin_id(Role.anonymous)
143 end
144
145 def test_builtin_id_with_user_should_return_non_member_group
146 assert_equal 12, Group.builtin_id(User.find(1))
147 end
148
149 def test_builtin_id_with_non_member_role_should_return_non_member_group
150 assert_equal 12, Group.builtin_id(Role.non_member)
151 end
152 end
135 end
@@ -35,10 +35,7 class ApplicationHelperTest < ActionView::TestCase
35 def setup
35 def setup
36 super
36 super
37 set_tmp_attachments_directory
37 set_tmp_attachments_directory
38 @russian_test = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82"
38 @russian_test = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82".force_encoding('UTF-8')
39 if @russian_test.respond_to?(:force_encoding)
40 @russian_test.force_encoding('UTF-8')
41 end
42 end
39 end
43
40
44 test "#link_to_if_authorized for authorized user should allow using the :controller and :action for the target link" do
41 test "#link_to_if_authorized for authorized user should allow using the :controller and :action for the target link" do
@@ -99,16 +96,12 class ApplicationHelperTest < ActionView::TestCase
99 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
96 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
100 end
97 end
101
98
102 if 'ruby'.respond_to?(:encoding)
99 def test_auto_links_with_non_ascii_characters
103 def test_auto_links_with_non_ascii_characters
100 to_test = {
104 to_test = {
101 "http://foo.bar/#{@russian_test}" =>
105 "http://foo.bar/#{@russian_test}" =>
102 %|<a class="external" href="http://foo.bar/#{@russian_test}">http://foo.bar/#{@russian_test}</a>|
106 %|<a class="external" href="http://foo.bar/#{@russian_test}">http://foo.bar/#{@russian_test}</a>|
103 }
107 }
104 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
108 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
109 end
110 else
111 puts 'Skipping test_auto_links_with_non_ascii_characters, unsupported ruby version'
112 end
105 end
113
106
114 def test_auto_mailto
107 def test_auto_mailto
@@ -254,16 +247,12 RAW
254 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
247 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
255 end
248 end
256
249
257 if 'ruby'.respond_to?(:encoding)
250 def test_textile_external_links_with_non_ascii_characters
258 def test_textile_external_links_with_non_ascii_characters
251 to_test = {
259 to_test = {
252 %|This is a "link":http://foo.bar/#{@russian_test}| =>
260 %|This is a "link":http://foo.bar/#{@russian_test}| =>
253 %|This is a <a href="http://foo.bar/#{@russian_test}" class="external">link</a>|
261 %|This is a <a href="http://foo.bar/#{@russian_test}" class="external">link</a>|
254 }
262 }
255 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
263 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
264 end
265 else
266 puts 'Skipping test_textile_external_links_with_non_ascii_characters, unsupported ruby version'
267 end
256 end
268
257
269 def test_redmine_links
258 def test_redmine_links
@@ -1336,13 +1325,8 RAW
1336 project = Project.find(1)
1325 project = Project.find(1)
1337 assert_equal %(<a href="/projects/ecookbook">eCookbook</a>),
1326 assert_equal %(<a href="/projects/ecookbook">eCookbook</a>),
1338 link_to_project(project)
1327 link_to_project(project)
1339 assert_equal %(<a href="/projects/ecookbook/settings">eCookbook</a>),
1340 link_to_project(project, :action => 'settings')
1341 assert_equal %(<a href="http://test.host/projects/ecookbook?jump=blah">eCookbook</a>),
1328 assert_equal %(<a href="http://test.host/projects/ecookbook?jump=blah">eCookbook</a>),
1342 link_to_project(project, {:only_path => false, :jump => 'blah'})
1329 link_to_project(project, {:only_path => false, :jump => 'blah'})
1343 result = link_to("eCookbook", "/projects/ecookbook/settings", :class => "project")
1344 assert_equal result,
1345 link_to_project(project, {:action => 'settings'}, :class => "project")
1346 end
1330 end
1347
1331
1348 def test_link_to_project_settings
1332 def test_link_to_project_settings
@@ -1433,7 +1417,7 RAW
1433
1417
1434 def test_raw_json_should_escape_closing_tags
1418 def test_raw_json_should_escape_closing_tags
1435 s = raw_json(["<foo>bar</foo>"])
1419 s = raw_json(["<foo>bar</foo>"])
1436 assert_equal '["<foo>bar<\/foo>"]', s
1420 assert_include '\/foo', s
1437 end
1421 end
1438
1422
1439 def test_raw_json_should_be_html_safe
1423 def test_raw_json_should_be_html_safe
@@ -1508,8 +1492,7 RAW
1508 end
1492 end
1509
1493
1510 def test_truncate_single_line_non_ascii
1494 def test_truncate_single_line_non_ascii
1511 ja = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"
1495 ja = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e".force_encoding('UTF-8')
1512 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
1513 result = truncate_single_line_raw("#{ja}\n#{ja}\n#{ja}", 10)
1496 result = truncate_single_line_raw("#{ja}\n#{ja}\n#{ja}", 10)
1514 assert_equal "#{ja} #{ja}...", result
1497 assert_equal "#{ja} #{ja}...", result
1515 assert !result.html_safe?
1498 assert !result.html_safe?
@@ -42,23 +42,16 class ProjectsHelperTest < ActionView::TestCase
42 def test_link_to_version_within_project
42 def test_link_to_version_within_project
43 @project = Project.find(2)
43 @project = Project.find(2)
44 User.current = User.find(1)
44 User.current = User.find(1)
45 assert_equal '<a href="/versions/5" title="07/01/2006">Alpha</a>', link_to_version(Version.find(5))
45 assert_equal '<a href="/versions/5">Alpha</a>', link_to_version(Version.find(5))
46 end
46 end
47
47
48 def test_link_to_version
48 def test_link_to_version
49 User.current = User.find(1)
49 User.current = User.find(1)
50 assert_equal '<a href="/versions/5" title="07/01/2006">Alpha</a>', link_to_version(Version.find(5))
50 assert_equal '<a href="/versions/5">OnlineStore - Alpha</a>', link_to_version(Version.find(5))
51 end
52
53 def test_link_to_version_without_effective_date
54 User.current = User.find(1)
55 version = Version.find(5)
56 version.effective_date = nil
57 assert_equal '<a href="/versions/5">Alpha</a>', link_to_version(version)
58 end
51 end
59
52
60 def test_link_to_private_version
53 def test_link_to_private_version
61 assert_equal 'Alpha', link_to_version(Version.find(5))
54 assert_equal 'OnlineStore - Alpha', link_to_version(Version.find(5))
62 end
55 end
63
56
64 def test_link_to_version_invalid_version
57 def test_link_to_version_invalid_version
@@ -71,20 +64,11 class ProjectsHelperTest < ActionView::TestCase
71 end
64 end
72
65
73 def test_format_version_name
66 def test_format_version_name
74 assert_equal "0.1", format_version_name(Version.find(1))
67 assert_equal "eCookbook - 0.1", format_version_name(Version.find(1))
75 end
76
77 def test_format_version_name_for_shared_version_within_project_should_not_display_project_name
78 @project = Project.find(1)
79 version = Version.find(1)
80 version.sharing = 'system'
81 assert_equal "0.1", format_version_name(version)
82 end
68 end
83
69
84 def test_format_version_name_for_shared_version_should_display_project_name
70 def test_format_version_name_for_system_version
85 version = Version.find(1)
71 assert_equal "OnlineStore - Systemwide visible version", format_version_name(Version.find(7))
86 version.sharing = 'system'
87 assert_equal "eCookbook - 0.1", format_version_name(version)
88 end
72 end
89
73
90 def test_version_options_for_select_with_no_versions
74 def test_version_options_for_select_with_no_versions
@@ -47,21 +47,19 class PatchesTest < ActiveSupport::TestCase
47 end
47 end
48
48
49 # https://github.com/rails/rails/pull/14198/files
49 # https://github.com/rails/rails/pull/14198/files
50 if RUBY_VERSION >= "1.9"
50 def test_indifferent_select
51 def test_indifferent_select
51 hash = ActiveSupport::HashWithIndifferentAccess.new(@symbols).select { |_ ,v| v == 1 }
52 hash = ActiveSupport::HashWithIndifferentAccess.new(@symbols).select { |_ ,v| v == 1 }
52 assert_equal({ 'a' => 1 }, hash)
53 assert_equal({ 'a' => 1 }, hash)
53 assert_instance_of ((Rails::VERSION::MAJOR < 4 && RUBY_VERSION < "2.1") ?
54 assert_instance_of ((Rails::VERSION::MAJOR < 4 && RUBY_VERSION < "2.1") ?
54 Hash : ActiveSupport::HashWithIndifferentAccess),
55 Hash : ActiveSupport::HashWithIndifferentAccess),
55 hash
56 hash
56 end
57 end
58
57
59 def test_indifferent_select_bang
58 def test_indifferent_select_bang
60 indifferent_strings = ActiveSupport::HashWithIndifferentAccess.new(@symbols)
59 indifferent_strings = ActiveSupport::HashWithIndifferentAccess.new(@symbols)
61 indifferent_strings.select! { |_, v| v == 1 }
60 indifferent_strings.select! { |_, v| v == 1 }
62 assert_equal({ 'a' => 1 }, indifferent_strings)
61 assert_equal({ 'a' => 1 }, indifferent_strings)
63 assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings
62 assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings
64 end
65 end
63 end
66
64
67 def test_indifferent_reject
65 def test_indifferent_reject
@@ -77,15 +75,13 class PatchesTest < ActiveSupport::TestCase
77 assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings
75 assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings
78 end
76 end
79
77
80 if RUBY_VERSION >= "1.9"
78 def test_select
81 def test_select
79 assert_equal @keys, @ordered_hash.select { true }.map(&:first)
82 assert_equal @keys, @ordered_hash.select { true }.map(&:first)
80 new_ordered_hash = @ordered_hash.select { true }
83 new_ordered_hash = @ordered_hash.select { true }
81 assert_equal @keys, new_ordered_hash.map(&:first)
84 assert_equal @keys, new_ordered_hash.map(&:first)
82 assert_instance_of ((Rails::VERSION::MAJOR < 4 && RUBY_VERSION < "2.1") ?
85 assert_instance_of ((Rails::VERSION::MAJOR < 4 && RUBY_VERSION < "2.1") ?
83 Hash : ActiveSupport::OrderedHash),
86 Hash : ActiveSupport::OrderedHash),
84 new_ordered_hash
87 new_ordered_hash
88 end
89 end
85 end
90
86
91 def test_reject
87 def test_reject
@@ -416,7 +416,7 class IssueNestedSetTest < ActiveSupport::TestCase
416 c.reload
416 c.reload
417
417
418 assert_equal 5, c.issues.count
418 assert_equal 5, c.issues.count
419 ic1, ic2, ic3, ic4, ic5 = c.issues.order('subject').all
419 ic1, ic2, ic3, ic4, ic5 = c.issues.order('subject').to_a
420 assert ic1.root?
420 assert ic1.root?
421 assert_equal ic1, ic2.parent
421 assert_equal ic1, ic2.parent
422 assert_equal ic1, ic3.parent
422 assert_equal ic1, ic3.parent
@@ -83,7 +83,7 class IssuePriorityTest < ActiveSupport::TestCase
83 IssuePriority.clear_position_names
83 IssuePriority.clear_position_names
84
84
85 IssuePriority.compute_position_names
85 IssuePriority.compute_position_names
86 assert_equal %w(lowest default high3 high2 highest), IssuePriority.active.all.sort.map(&:position_name)
86 assert_equal %w(lowest default high3 high2 highest), IssuePriority.active.to_a.sort.map(&:position_name)
87 end
87 end
88
88
89 def test_compute_position_names_without_default_priority_should_split_priorities
89 def test_compute_position_names_without_default_priority_should_split_priorities
@@ -91,16 +91,16 class IssuePriorityTest < ActiveSupport::TestCase
91 IssuePriority.update_all :is_default => false
91 IssuePriority.update_all :is_default => false
92
92
93 IssuePriority.compute_position_names
93 IssuePriority.compute_position_names
94 assert_equal %w(lowest low2 default high2 highest), IssuePriority.active.all.sort.map(&:position_name)
94 assert_equal %w(lowest low2 default high2 highest), IssuePriority.active.to_a.sort.map(&:position_name)
95 end
95 end
96
96
97 def test_adding_a_priority_should_update_position_names
97 def test_adding_a_priority_should_update_position_names
98 priority = IssuePriority.create!(:name => 'New')
98 priority = IssuePriority.create!(:name => 'New')
99 assert_equal %w(lowest default high4 high3 high2 highest), IssuePriority.active.all.sort.map(&:position_name)
99 assert_equal %w(lowest default high4 high3 high2 highest), IssuePriority.active.to_a.sort.map(&:position_name)
100 end
100 end
101
101
102 def test_destroying_a_priority_should_update_position_names
102 def test_destroying_a_priority_should_update_position_names
103 IssuePriority.find_by_position_name('highest').destroy
103 IssuePriority.find_by_position_name('highest').destroy
104 assert_equal %w(lowest default high2 highest), IssuePriority.active.all.sort.map(&:position_name)
104 assert_equal %w(lowest default high2 highest), IssuePriority.active.to_a.sort.map(&:position_name)
105 end
105 end
106 end
106 end
@@ -112,7 +112,7 class IssueStatusTest < ActiveSupport::TestCase
112 end
112 end
113
113
114 def test_sorted_scope
114 def test_sorted_scope
115 assert_equal IssueStatus.all.sort, IssueStatus.sorted.all
115 assert_equal IssueStatus.all.sort, IssueStatus.sorted.to_a
116 end
116 end
117
117
118 def test_named_scope
118 def test_named_scope
@@ -204,7 +204,7 class IssueTest < ActiveSupport::TestCase
204
204
205 def test_visible_scope_for_anonymous
205 def test_visible_scope_for_anonymous
206 # Anonymous user should see issues of public projects only
206 # Anonymous user should see issues of public projects only
207 issues = Issue.visible(User.anonymous).all
207 issues = Issue.visible(User.anonymous).to_a
208 assert issues.any?
208 assert issues.any?
209 assert_nil issues.detect {|issue| !issue.project.is_public?}
209 assert_nil issues.detect {|issue| !issue.project.is_public?}
210 assert_nil issues.detect {|issue| issue.is_private?}
210 assert_nil issues.detect {|issue| issue.is_private?}
@@ -214,7 +214,7 class IssueTest < ActiveSupport::TestCase
214 def test_visible_scope_for_anonymous_without_view_issues_permissions
214 def test_visible_scope_for_anonymous_without_view_issues_permissions
215 # Anonymous user should not see issues without permission
215 # Anonymous user should not see issues without permission
216 Role.anonymous.remove_permission!(:view_issues)
216 Role.anonymous.remove_permission!(:view_issues)
217 issues = Issue.visible(User.anonymous).all
217 issues = Issue.visible(User.anonymous).to_a
218 assert issues.empty?
218 assert issues.empty?
219 assert_visibility_match User.anonymous, issues
219 assert_visibility_match User.anonymous, issues
220 end
220 end
@@ -247,7 +247,7 class IssueTest < ActiveSupport::TestCase
247 user = User.find(9)
247 user = User.find(9)
248 assert user.projects.empty?
248 assert user.projects.empty?
249 # Non member user should see issues of public projects only
249 # Non member user should see issues of public projects only
250 issues = Issue.visible(user).all
250 issues = Issue.visible(user).to_a
251 assert issues.any?
251 assert issues.any?
252 assert_nil issues.detect {|issue| !issue.project.is_public?}
252 assert_nil issues.detect {|issue| !issue.project.is_public?}
253 assert_nil issues.detect {|issue| issue.is_private?}
253 assert_nil issues.detect {|issue| issue.is_private?}
@@ -259,7 +259,7 class IssueTest < ActiveSupport::TestCase
259 Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 9, :subject => 'Issue by non member')
259 Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 9, :subject => 'Issue by non member')
260 user = User.find(9)
260 user = User.find(9)
261
261
262 issues = Issue.visible(user).all
262 issues = Issue.visible(user).to_a
263 assert issues.any?
263 assert issues.any?
264 assert_nil issues.detect {|issue| issue.author != user}
264 assert_nil issues.detect {|issue| issue.author != user}
265 assert_visibility_match user, issues
265 assert_visibility_match user, issues
@@ -270,7 +270,7 class IssueTest < ActiveSupport::TestCase
270 Role.non_member.remove_permission!(:view_issues)
270 Role.non_member.remove_permission!(:view_issues)
271 user = User.find(9)
271 user = User.find(9)
272 assert user.projects.empty?
272 assert user.projects.empty?
273 issues = Issue.visible(user).all
273 issues = Issue.visible(user).to_a
274 assert issues.empty?
274 assert issues.empty?
275 assert_visibility_match user, issues
275 assert_visibility_match user, issues
276 end
276 end
@@ -291,7 +291,7 class IssueTest < ActiveSupport::TestCase
291 # User should see issues of projects for which user has view_issues permissions only
291 # User should see issues of projects for which user has view_issues permissions only
292 Role.non_member.remove_permission!(:view_issues)
292 Role.non_member.remove_permission!(:view_issues)
293 Member.create!(:principal => user, :project_id => 3, :role_ids => [2])
293 Member.create!(:principal => user, :project_id => 3, :role_ids => [2])
294 issues = Issue.visible(user).all
294 issues = Issue.visible(user).to_a
295 assert issues.any?
295 assert issues.any?
296 assert_nil issues.detect {|issue| issue.project_id != 3}
296 assert_nil issues.detect {|issue| issue.project_id != 3}
297 assert_nil issues.detect {|issue| issue.is_private?}
297 assert_nil issues.detect {|issue| issue.is_private?}
@@ -311,12 +311,12 class IssueTest < ActiveSupport::TestCase
311 :is_private => true)
311 :is_private => true)
312
312
313 Role.find(2).update_attribute :issues_visibility, 'default'
313 Role.find(2).update_attribute :issues_visibility, 'default'
314 issues = Issue.visible(User.find(8)).all
314 issues = Issue.visible(User.find(8)).to_a
315 assert issues.any?
315 assert issues.any?
316 assert issues.include?(issue)
316 assert issues.include?(issue)
317
317
318 Role.find(2).update_attribute :issues_visibility, 'own'
318 Role.find(2).update_attribute :issues_visibility, 'own'
319 issues = Issue.visible(User.find(8)).all
319 issues = Issue.visible(User.find(8)).to_a
320 assert issues.any?
320 assert issues.any?
321 assert issues.include?(issue)
321 assert issues.include?(issue)
322 end
322 end
@@ -325,7 +325,7 class IssueTest < ActiveSupport::TestCase
325 user = User.find(1)
325 user = User.find(1)
326 user.members.each(&:destroy)
326 user.members.each(&:destroy)
327 assert user.projects.empty?
327 assert user.projects.empty?
328 issues = Issue.visible(user).all
328 issues = Issue.visible(user).to_a
329 assert issues.any?
329 assert issues.any?
330 # Admin should see issues on private projects that admin does not belong to
330 # Admin should see issues on private projects that admin does not belong to
331 assert issues.detect {|issue| !issue.project.is_public?}
331 assert issues.detect {|issue| !issue.project.is_public?}
@@ -336,7 +336,7 class IssueTest < ActiveSupport::TestCase
336
336
337 def test_visible_scope_with_project
337 def test_visible_scope_with_project
338 project = Project.find(1)
338 project = Project.find(1)
339 issues = Issue.visible(User.find(2), :project => project).all
339 issues = Issue.visible(User.find(2), :project => project).to_a
340 projects = issues.collect(&:project).uniq
340 projects = issues.collect(&:project).uniq
341 assert_equal 1, projects.size
341 assert_equal 1, projects.size
342 assert_equal project, projects.first
342 assert_equal project, projects.first
@@ -344,7 +344,7 class IssueTest < ActiveSupport::TestCase
344
344
345 def test_visible_scope_with_project_and_subprojects
345 def test_visible_scope_with_project_and_subprojects
346 project = Project.find(1)
346 project = Project.find(1)
347 issues = Issue.visible(User.find(2), :project => project, :with_subprojects => true).all
347 issues = Issue.visible(User.find(2), :project => project, :with_subprojects => true).to_a
348 projects = issues.collect(&:project).uniq
348 projects = issues.collect(&:project).uniq
349 assert projects.size > 1
349 assert projects.size > 1
350 assert_equal [], projects.select {|p| !p.is_or_is_descendant_of?(project)}
350 assert_equal [], projects.select {|p| !p.is_or_is_descendant_of?(project)}
@@ -370,13 +370,20 class IssueTest < ActiveSupport::TestCase
370 assert_equal 2, parent.descendants.visible(user).collect{|i| i}.size
370 assert_equal 2, parent.descendants.visible(user).collect{|i| i}.size
371 end
371 end
372
372
373 def test_visible_scope_with_unsaved_user_should_not_raise_an_error
374 user = User.new
375 assert_nothing_raised do
376 Issue.visible(user).to_a
377 end
378 end
379
373 def test_open_scope
380 def test_open_scope
374 issues = Issue.open.all
381 issues = Issue.open.to_a
375 assert_nil issues.detect(&:closed?)
382 assert_nil issues.detect(&:closed?)
376 end
383 end
377
384
378 def test_open_scope_with_arg
385 def test_open_scope_with_arg
379 issues = Issue.open(false).all
386 issues = Issue.open(false).to_a
380 assert_equal issues, issues.select(&:closed?)
387 assert_equal issues, issues.select(&:closed?)
381 end
388 end
382
389
@@ -1289,7 +1296,7 class IssueTest < ActiveSupport::TestCase
1289 end
1296 end
1290
1297
1291 test "#copy should not create a journal" do
1298 test "#copy should not create a journal" do
1292 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
1299 copy = Issue.find(1).copy({:project_id => 3, :tracker_id => 2, :assigned_to_id => 3}, :link => false)
1293 copy.save!
1300 copy.save!
1294 assert_equal 0, copy.reload.journals.size
1301 assert_equal 0, copy.reload.journals.size
1295 end
1302 end
@@ -1325,7 +1332,7 class IssueTest < ActiveSupport::TestCase
1325 test "#copy should create a journal with notes" do
1332 test "#copy should create a journal with notes" do
1326 date = Date.today
1333 date = Date.today
1327 notes = "Notes added when copying"
1334 notes = "Notes added when copying"
1328 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :start_date => date)
1335 copy = Issue.find(1).copy({:project_id => 3, :tracker_id => 2, :start_date => date}, :link => false)
1329 copy.init_journal(User.current, notes)
1336 copy.init_journal(User.current, notes)
1330 copy.save!
1337 copy.save!
1331
1338
@@ -1619,13 +1626,15 class IssueTest < ActiveSupport::TestCase
1619 issue2.reload
1626 issue2.reload
1620 assert_equal Date.parse('2012-10-18'), issue2.start_date
1627 assert_equal Date.parse('2012-10-18'), issue2.start_date
1621
1628
1622 child = Issue.new(:parent_issue_id => issue2.id, :start_date => '2012-10-16',
1629 with_settings :date_format => '%m/%d/%Y' do
1623 :project_id => 1, :tracker_id => 1, :status_id => 1, :subject => 'Child', :author_id => 1)
1630 child = Issue.new(:parent_issue_id => issue2.id, :start_date => '2012-10-16',
1624 assert !child.valid?
1631 :project_id => 1, :tracker_id => 1, :status_id => 1, :subject => 'Child', :author_id => 1)
1625 assert_include 'Start date cannot be earlier than 10/18/2012 because of preceding issues', child.errors.full_messages
1632 assert !child.valid?
1626 assert_equal Date.parse('2012-10-18'), child.soonest_start
1633 assert_include 'Start date cannot be earlier than 10/18/2012 because of preceding issues', child.errors.full_messages
1627 child.start_date = '2012-10-18'
1634 assert_equal Date.parse('2012-10-18'), child.soonest_start
1628 assert child.save
1635 child.start_date = '2012-10-18'
1636 assert child.save
1637 end
1629 end
1638 end
1630
1639
1631 def test_setting_parent_to_a_dependent_issue_should_not_validate
1640 def test_setting_parent_to_a_dependent_issue_should_not_validate
@@ -27,6 +27,7 class JournalTest < ActiveSupport::TestCase
27
27
28 def setup
28 def setup
29 @journal = Journal.find 1
29 @journal = Journal.find 1
30 User.current = nil
30 end
31 end
31
32
32 def test_journalized_is_an_issue
33 def test_journalized_is_an_issue
@@ -119,12 +120,12 class JournalTest < ActiveSupport::TestCase
119
120
120 def test_visible_scope_for_anonymous
121 def test_visible_scope_for_anonymous
121 # Anonymous user should see issues of public projects only
122 # Anonymous user should see issues of public projects only
122 journals = Journal.visible(User.anonymous).all
123 journals = Journal.visible(User.anonymous).to_a
123 assert journals.any?
124 assert journals.any?
124 assert_nil journals.detect {|journal| !journal.issue.project.is_public?}
125 assert_nil journals.detect {|journal| !journal.issue.project.is_public?}
125 # Anonymous user should not see issues without permission
126 # Anonymous user should not see issues without permission
126 Role.anonymous.remove_permission!(:view_issues)
127 Role.anonymous.remove_permission!(:view_issues)
127 journals = Journal.visible(User.anonymous).all
128 journals = Journal.visible(User.anonymous).to_a
128 assert journals.empty?
129 assert journals.empty?
129 end
130 end
130
131
@@ -132,18 +133,18 class JournalTest < ActiveSupport::TestCase
132 user = User.find(9)
133 user = User.find(9)
133 assert user.projects.empty?
134 assert user.projects.empty?
134 # Non member user should see issues of public projects only
135 # Non member user should see issues of public projects only
135 journals = Journal.visible(user).all
136 journals = Journal.visible(user).to_a
136 assert journals.any?
137 assert journals.any?
137 assert_nil journals.detect {|journal| !journal.issue.project.is_public?}
138 assert_nil journals.detect {|journal| !journal.issue.project.is_public?}
138 # Non member user should not see issues without permission
139 # Non member user should not see issues without permission
139 Role.non_member.remove_permission!(:view_issues)
140 Role.non_member.remove_permission!(:view_issues)
140 user.reload
141 user.reload
141 journals = Journal.visible(user).all
142 journals = Journal.visible(user).to_a
142 assert journals.empty?
143 assert journals.empty?
143 # User should see issues of projects for which user has view_issues permissions only
144 # User should see issues of projects for which user has view_issues permissions only
144 Member.create!(:principal => user, :project_id => 1, :role_ids => [1])
145 Member.create!(:principal => user, :project_id => 1, :role_ids => [1])
145 user.reload
146 user.reload
146 journals = Journal.visible(user).all
147 journals = Journal.visible(user).to_a
147 assert journals.any?
148 assert journals.any?
148 assert_nil journals.detect {|journal| journal.issue.project_id != 1}
149 assert_nil journals.detect {|journal| journal.issue.project_id != 1}
149 end
150 end
@@ -152,7 +153,7 class JournalTest < ActiveSupport::TestCase
152 user = User.find(1)
153 user = User.find(1)
153 user.members.each(&:destroy)
154 user.members.each(&:destroy)
154 assert user.projects.empty?
155 assert user.projects.empty?
155 journals = Journal.visible(user).all
156 journals = Journal.visible(user).to_a
156 assert journals.any?
157 assert journals.any?
157 # Admin should see issues on private projects that admin does not belong to
158 # Admin should see issues on private projects that admin does not belong to
158 assert journals.detect {|journal| !journal.issue.project.is_public?}
159 assert journals.detect {|journal| !journal.issue.project.is_public?}
@@ -21,14 +21,9 class Redmine::CodesetUtilTest < ActiveSupport::TestCase
21
21
22 def test_to_utf8_by_setting_from_latin1
22 def test_to_utf8_by_setting_from_latin1
23 with_settings :repositories_encodings => 'UTF-8,ISO-8859-1' do
23 with_settings :repositories_encodings => 'UTF-8,ISO-8859-1' do
24 s1 = "Texte encod\xc3\xa9"
24 s1 = "Texte encod\xc3\xa9".force_encoding("UTF-8")
25 s2 = "Texte encod\xe9"
25 s2 = "Texte encod\xe9".force_encoding("ASCII-8BIT")
26 s3 = s2.dup
26 s3 = s2.dup.force_encoding("UTF-8")
27 if s1.respond_to?(:force_encoding)
28 s1.force_encoding("UTF-8")
29 s2.force_encoding("ASCII-8BIT")
30 s3.force_encoding("UTF-8")
31 end
32 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s2)
27 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s2)
33 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s3)
28 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s3)
34 end
29 end
@@ -36,14 +31,9 class Redmine::CodesetUtilTest < ActiveSupport::TestCase
36
31
37 def test_to_utf8_by_setting_from_euc_jp
32 def test_to_utf8_by_setting_from_euc_jp
38 with_settings :repositories_encodings => 'UTF-8,EUC-JP' do
33 with_settings :repositories_encodings => 'UTF-8,EUC-JP' do
39 s1 = "\xe3\x83\xac\xe3\x83\x83\xe3\x83\x89\xe3\x83\x9e\xe3\x82\xa4\xe3\x83\xb3"
34 s1 = "\xe3\x83\xac\xe3\x83\x83\xe3\x83\x89\xe3\x83\x9e\xe3\x82\xa4\xe3\x83\xb3".force_encoding("UTF-8")
40 s2 = "\xa5\xec\xa5\xc3\xa5\xc9\xa5\xde\xa5\xa4\xa5\xf3"
35 s2 = "\xa5\xec\xa5\xc3\xa5\xc9\xa5\xde\xa5\xa4\xa5\xf3".force_encoding("ASCII-8BIT")
41 s3 = s2.dup
36 s3 = s2.dup.force_encoding("UTF-8")
42 if s1.respond_to?(:force_encoding)
43 s1.force_encoding("UTF-8")
44 s2.force_encoding("ASCII-8BIT")
45 s3.force_encoding("UTF-8")
46 end
47 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s2)
37 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s2)
48 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s3)
38 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s3)
49 end
39 end
@@ -51,14 +41,9 class Redmine::CodesetUtilTest < ActiveSupport::TestCase
51
41
52 def test_to_utf8_by_setting_should_be_converted_all_latin1
42 def test_to_utf8_by_setting_should_be_converted_all_latin1
53 with_settings :repositories_encodings => 'ISO-8859-1' do
43 with_settings :repositories_encodings => 'ISO-8859-1' do
54 s1 = "\xc3\x82\xc2\x80"
44 s1 = "\xc3\x82\xc2\x80".force_encoding("UTF-8")
55 s2 = "\xC2\x80"
45 s2 = "\xC2\x80".force_encoding("ASCII-8BIT")
56 s3 = s2.dup
46 s3 = s2.dup.force_encoding("UTF-8")
57 if s1.respond_to?(:force_encoding)
58 s1.force_encoding("UTF-8")
59 s2.force_encoding("ASCII-8BIT")
60 s3.force_encoding("UTF-8")
61 end
62 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s2)
47 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s2)
63 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s3)
48 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s3)
64 end
49 end
@@ -70,45 +55,33 class Redmine::CodesetUtilTest < ActiveSupport::TestCase
70 end
55 end
71
56
72 def test_to_utf8_by_setting_returns_ascii_as_utf8
57 def test_to_utf8_by_setting_returns_ascii_as_utf8
73 s1 = "ASCII"
58 s1 = "ASCII".force_encoding("UTF-8")
74 s2 = s1.dup
59 s2 = s1.dup.force_encoding("ISO-8859-1")
75 if s1.respond_to?(:force_encoding)
76 s1.force_encoding("UTF-8")
77 s2.force_encoding("ISO-8859-1")
78 end
79 str1 = Redmine::CodesetUtil.to_utf8_by_setting(s1)
60 str1 = Redmine::CodesetUtil.to_utf8_by_setting(s1)
80 str2 = Redmine::CodesetUtil.to_utf8_by_setting(s2)
61 str2 = Redmine::CodesetUtil.to_utf8_by_setting(s2)
81 assert_equal s1, str1
62 assert_equal s1, str1
82 assert_equal s1, str2
63 assert_equal s1, str2
83 if s1.respond_to?(:force_encoding)
64 assert_equal "UTF-8", str1.encoding.to_s
84 assert_equal "UTF-8", str1.encoding.to_s
65 assert_equal "UTF-8", str2.encoding.to_s
85 assert_equal "UTF-8", str2.encoding.to_s
86 end
87 end
66 end
88
67
89 def test_to_utf8_by_setting_invalid_utf8_sequences_should_be_stripped
68 def test_to_utf8_by_setting_invalid_utf8_sequences_should_be_stripped
90 with_settings :repositories_encodings => '' do
69 with_settings :repositories_encodings => '' do
91 # s1 = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
70 # s1 = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
92 s1 = "Texte encod\xe9 en ISO-8859-1."
71 s1 = "Texte encod\xe9 en ISO-8859-1.".force_encoding("ASCII-8BIT")
93 s1.force_encoding("ASCII-8BIT") if s1.respond_to?(:force_encoding)
94 str = Redmine::CodesetUtil.to_utf8_by_setting(s1)
72 str = Redmine::CodesetUtil.to_utf8_by_setting(s1)
95 if str.respond_to?(:force_encoding)
73 assert str.valid_encoding?
96 assert str.valid_encoding?
74 assert_equal "UTF-8", str.encoding.to_s
97 assert_equal "UTF-8", str.encoding.to_s
98 end
99 assert_equal "Texte encod? en ISO-8859-1.", str
75 assert_equal "Texte encod? en ISO-8859-1.", str
100 end
76 end
101 end
77 end
102
78
103 def test_to_utf8_by_setting_invalid_utf8_sequences_should_be_stripped_ja_jis
79 def test_to_utf8_by_setting_invalid_utf8_sequences_should_be_stripped_ja_jis
104 with_settings :repositories_encodings => 'ISO-2022-JP' do
80 with_settings :repositories_encodings => 'ISO-2022-JP' do
105 s1 = "test\xb5\xfetest\xb5\xfe"
81 s1 = "test\xb5\xfetest\xb5\xfe".force_encoding("ASCII-8BIT")
106 s1.force_encoding("ASCII-8BIT") if s1.respond_to?(:force_encoding)
107 str = Redmine::CodesetUtil.to_utf8_by_setting(s1)
82 str = Redmine::CodesetUtil.to_utf8_by_setting(s1)
108 if str.respond_to?(:force_encoding)
83 assert str.valid_encoding?
109 assert str.valid_encoding?
84 assert_equal "UTF-8", str.encoding.to_s
110 assert_equal "UTF-8", str.encoding.to_s
111 end
112 assert_equal "test??test??", str
85 assert_equal "test??test??", str
113 end
86 end
114 end
87 end
@@ -27,11 +27,10 class PdfTest < ActiveSupport::TestCase
27 end
27 end
28
28
29 def test_rdm_pdf_iconv_cannot_convert_ja_cp932
29 def test_rdm_pdf_iconv_cannot_convert_ja_cp932
30 encoding = ( RUBY_PLATFORM == 'java' ? "SJIS" : "CP932" )
31 utf8_txt_1 = "\xe7\x8b\x80\xe6\x85\x8b"
30 utf8_txt_1 = "\xe7\x8b\x80\xe6\x85\x8b"
32 utf8_txt_2 = "\xe7\x8b\x80\xe6\x85\x8b\xe7\x8b\x80"
31 utf8_txt_2 = "\xe7\x8b\x80\xe6\x85\x8b\xe7\x8b\x80"
33 utf8_txt_3 = "\xe7\x8b\x80\xe7\x8b\x80\xe6\x85\x8b\xe7\x8b\x80"
32 utf8_txt_3 = "\xe7\x8b\x80\xe7\x8b\x80\xe6\x85\x8b\xe7\x8b\x80"
34 if utf8_txt_1.respond_to?(:force_encoding)
33 ["CP932", "SJIS"].each do |encoding|
35 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_1, encoding)
34 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_1, encoding)
36 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_2, encoding)
35 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_2, encoding)
37 txt_3 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_3, encoding)
36 txt_3 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_3, encoding)
@@ -41,88 +40,65 class PdfTest < ActiveSupport::TestCase
41 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
40 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
42 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
41 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
43 assert_equal "ASCII-8BIT", txt_3.encoding.to_s
42 assert_equal "ASCII-8BIT", txt_3.encoding.to_s
44 elsif RUBY_PLATFORM == 'java'
45 assert_equal "??",
46 Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_1, encoding)
47 assert_equal "???",
48 Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_2, encoding)
49 assert_equal "????",
50 Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_3, encoding)
51 else
52 assert_equal "???\x91\xd4",
53 Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_1, encoding)
54 assert_equal "???\x91\xd4???",
55 Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_2, encoding)
56 assert_equal "??????\x91\xd4???",
57 Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_3, encoding)
58 end
43 end
59 end
44 end
60
45
61 def test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_en
46 def test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_en
62 str1 = "Texte encod\xe9 en ISO-8859-1"
47 str1 = "Texte encod\xe9 en ISO-8859-1".force_encoding("UTF-8")
63 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
48 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test".force_encoding("ASCII-8BIT")
64 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
65 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
66 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str1, 'UTF-8')
49 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str1, 'UTF-8')
67 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str2, 'UTF-8')
50 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str2, 'UTF-8')
68 if txt_1.respond_to?(:force_encoding)
51 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
69 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
52 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
70 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
71 end
72 assert_equal "Texte encod? en ISO-8859-1", txt_1
53 assert_equal "Texte encod? en ISO-8859-1", txt_1
73 assert_equal "?a?b?c?d?e test", txt_2
54 assert_equal "?a?b?c?d?e test", txt_2
74 end
55 end
75
56
76 def test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_ja
57 def test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_ja
77 str1 = "Texte encod\xe9 en ISO-8859-1"
58 str1 = "Texte encod\xe9 en ISO-8859-1".force_encoding("UTF-8")
78 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
59 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test".force_encoding("ASCII-8BIT")
79 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
80 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
81 encoding = ( RUBY_PLATFORM == 'java' ? "SJIS" : "CP932" )
60 encoding = ( RUBY_PLATFORM == 'java' ? "SJIS" : "CP932" )
82 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str1, encoding)
61 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str1, encoding)
83 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str2, encoding)
62 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str2, encoding)
84 if txt_1.respond_to?(:force_encoding)
63 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
85 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
64 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
86 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
87 end
88 assert_equal "Texte encod? en ISO-8859-1", txt_1
65 assert_equal "Texte encod? en ISO-8859-1", txt_1
89 assert_equal "?a?b?c?d?e test", txt_2
66 assert_equal "?a?b?c?d?e test", txt_2
90 end
67 end
91
68
92 def test_attach
69 def test_attach
93 set_fixtures_attachments_directory
70 ["CP932", "SJIS"].each do |encoding|
71 set_fixtures_attachments_directory
94
72
95 str2 = "\x83e\x83X\x83g"
73 str2 = "\x83e\x83X\x83g".force_encoding("ASCII-8BIT")
96 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
97 encoding = ( RUBY_PLATFORM == 'java' ? "SJIS" : "CP932" )
98
74
99 a1 = Attachment.find(17)
75 a1 = Attachment.find(17)
100 a2 = Attachment.find(19)
76 a2 = Attachment.find(19)
77 User.current = User.find(1)
78 assert a1.readable?
79 assert a1.visible?
80 assert a2.readable?
81 assert a2.visible?
101
82
102 User.current = User.find(1)
83 aa1 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "Testfile.PNG", "UTF-8")
103 assert a1.readable?
84 assert_not_nil aa1
104 assert a1.visible?
85 assert_equal 17, aa1.id
105 assert a2.readable?
106 assert a2.visible?
107
86
108 aa1 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "Testfile.PNG", "UTF-8")
87 aa2 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "test#{str2}.png", encoding)
109 assert_not_nil aa1
88 assert_not_nil aa2
110 assert_equal 17, aa1.id
89 assert_equal 19, aa2.id
111 aa2 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "test#{str2}.png", encoding)
112 assert_not_nil aa2
113 assert_equal 19, aa2.id
114
90
115 User.current = nil
91 User.current = nil
116 assert a1.readable?
92 assert a1.readable?
117 assert (! a1.visible?)
93 assert (! a1.visible?)
118 assert a2.readable?
94 assert a2.readable?
119 assert (! a2.visible?)
95 assert (! a2.visible?)
96 aa1 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "Testfile.PNG", "UTF-8")
97 assert_equal nil, aa1
98 aa2 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "test#{str2}.png", encoding)
99 assert_equal nil, aa2
120
100
121 aa1 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "Testfile.PNG", "UTF-8")
101 set_tmp_attachments_directory
122 assert_equal nil, aa1
102 end
123 aa2 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "test#{str2}.png", encoding)
124 assert_equal nil, aa2
125
126 set_tmp_attachments_directory
127 end
103 end
128 end
104 end
@@ -66,6 +66,7 class Redmine::Hook::ManagerTest < ActionView::TestCase
66
66
67 def setup
67 def setup
68 @hook_module = Redmine::Hook
68 @hook_module = Redmine::Hook
69 @hook_module.clear_listeners
69 end
70 end
70
71
71 def teardown
72 def teardown
@@ -32,47 +32,50 class Redmine::I18nTest < ActiveSupport::TestCase
32 def test_date_format_default
32 def test_date_format_default
33 set_language_if_valid 'en'
33 set_language_if_valid 'en'
34 today = Date.today
34 today = Date.today
35 Setting.date_format = ''
35 with_settings :date_format => '' do
36 assert_equal I18n.l(today), format_date(today)
36 assert_equal I18n.l(today), format_date(today)
37 end
37 end
38 end
38
39
39 def test_date_format
40 def test_date_format
40 set_language_if_valid 'en'
41 set_language_if_valid 'en'
41 today = Date.today
42 today = Date.today
42 Setting.date_format = '%d %m %Y'
43 with_settings :date_format => '%d %m %Y' do
43 assert_equal today.strftime('%d %m %Y'), format_date(today)
44 assert_equal today.strftime('%d %m %Y'), format_date(today)
45 end
44 end
46 end
45
47
46 def test_date_format_default_with_user_locale
48 def test_date_format_default_with_user_locale
47 set_language_if_valid 'es'
49 set_language_if_valid 'es'
48 today = now = Time.parse('2011-02-20 14:00:00')
50 today = now = Time.parse('2011-02-20 14:00:00')
49 Setting.date_format = '%d %B %Y'
51 with_settings :date_format => '%d %B %Y' do
50 User.current.language = 'fr'
52 User.current.language = 'fr'
51 s1 = "20 f\xc3\xa9vrier 2011"
53 s1 = "20 f\xc3\xa9vrier 2011".force_encoding("UTF-8")
52 s1.force_encoding("UTF-8") if s1.respond_to?(:force_encoding)
54 assert_equal s1, format_date(today)
53 assert_equal s1, format_date(today)
55 User.current.language = nil
54 User.current.language = nil
56 assert_equal '20 Febrero 2011', format_date(today)
55 assert_equal '20 Febrero 2011', format_date(today)
57 end
56 end
58 end
57
59
58 def test_date_and_time_for_each_language
60 def test_date_and_time_for_each_language
59 Setting.date_format = ''
61 with_settings :date_format => '' do
60 valid_languages.each do |lang|
62 valid_languages.each do |lang|
61 set_language_if_valid lang
63 set_language_if_valid lang
62 assert_nothing_raised "#{lang} failure" do
64 assert_nothing_raised "#{lang} failure" do
63 format_date(Date.today)
65 format_date(Date.today)
64 format_time(Time.now)
66 format_time(Time.now)
65 format_time(Time.now, false)
67 format_time(Time.now, false)
66 assert_not_equal 'default', ::I18n.l(Date.today, :format => :default),
68 assert_not_equal 'default', ::I18n.l(Date.today, :format => :default),
67 "date.formats.default missing in #{lang}"
69 "date.formats.default missing in #{lang}"
68 assert_not_equal 'time', ::I18n.l(Time.now, :format => :time),
70 assert_not_equal 'time', ::I18n.l(Time.now, :format => :time),
69 "time.formats.time missing in #{lang}"
71 "time.formats.time missing in #{lang}"
72 end
73 assert l('date.day_names').is_a?(Array)
74 assert_equal 7, l('date.day_names').size
75
76 assert l('date.month_names').is_a?(Array)
77 assert_equal 13, l('date.month_names').size
70 end
78 end
71 assert l('date.day_names').is_a?(Array)
72 assert_equal 7, l('date.day_names').size
73
74 assert l('date.month_names').is_a?(Array)
75 assert_equal 13, l('date.month_names').size
76 end
79 end
77 end
80 end
78
81
@@ -134,10 +137,10 class Redmine::I18nTest < ActiveSupport::TestCase
134 def test_utc_time_format
137 def test_utc_time_format
135 set_language_if_valid 'en'
138 set_language_if_valid 'en'
136 now = Time.now
139 now = Time.now
137 Setting.date_format = '%d %m %Y'
140 with_settings :date_format => '%d %m %Y', :time_format => '%H %M' do
138 Setting.time_format = '%H %M'
141 assert_equal now.strftime('%d %m %Y %H %M'), format_time(now.utc)
139 assert_equal now.strftime('%d %m %Y %H %M'), format_time(now.utc)
142 assert_equal now.strftime('%H %M'), format_time(now.utc, false)
140 assert_equal now.strftime('%H %M'), format_time(now.utc, false)
143 end
141 end
144 end
142
145
143 def test_number_to_human_size_for_each_language
146 def test_number_to_human_size_for_each_language
@@ -175,8 +178,7 class Redmine::I18nTest < ActiveSupport::TestCase
175 set_language_if_valid 'bs'
178 set_language_if_valid 'bs'
176 assert_equal "KM -1000,20", number_to_currency(-1000.2)
179 assert_equal "KM -1000,20", number_to_currency(-1000.2)
177 set_language_if_valid 'de'
180 set_language_if_valid 'de'
178 euro_sign = "\xe2\x82\xac"
181 euro_sign = "\xe2\x82\xac".force_encoding('UTF-8')
179 euro_sign.force_encoding('UTF-8') if euro_sign.respond_to?(:force_encoding)
180 assert_equal "-1000,20 #{euro_sign}", number_to_currency(-1000.2)
182 assert_equal "-1000,20 #{euro_sign}", number_to_currency(-1000.2)
181 end
183 end
182
184
@@ -193,8 +195,7 class Redmine::I18nTest < ActiveSupport::TestCase
193 assert_nil options.detect {|option| option.size != 2}
195 assert_nil options.detect {|option| option.size != 2}
194 assert_nil options.detect {|option| !option.first.is_a?(String) || !option.last.is_a?(String)}
196 assert_nil options.detect {|option| !option.first.is_a?(String) || !option.last.is_a?(String)}
195 assert_include ["English", "en"], options
197 assert_include ["English", "en"], options
196 ja = "Japanese (\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e)"
198 ja = "Japanese (\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e)".force_encoding('UTF-8')
197 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
198 assert_include [ja, "ja"], options
199 assert_include [ja, "ja"], options
199 end
200 end
200
201
@@ -238,30 +239,21 class Redmine::I18nTest < ActiveSupport::TestCase
238
239
239 def test_utf8
240 def test_utf8
240 set_language_if_valid 'ja'
241 set_language_if_valid 'ja'
241 str_ja_yes = "\xe3\x81\xaf\xe3\x81\x84"
242 str_ja_yes = "\xe3\x81\xaf\xe3\x81\x84".force_encoding('UTF-8')
242 i18n_ja_yes = l(:general_text_Yes)
243 i18n_ja_yes = l(:general_text_Yes)
243 if str_ja_yes.respond_to?(:force_encoding)
244 str_ja_yes.force_encoding('UTF-8')
245 assert_equal "UTF-8", i18n_ja_yes.encoding.to_s
246 end
247 assert_equal str_ja_yes, i18n_ja_yes
244 assert_equal str_ja_yes, i18n_ja_yes
245 assert_equal "UTF-8", i18n_ja_yes.encoding.to_s
248 end
246 end
249
247
250 def test_traditional_chinese_locale
248 def test_traditional_chinese_locale
251 set_language_if_valid 'zh-TW'
249 set_language_if_valid 'zh-TW'
252 str_tw = "Traditional Chinese (\xe7\xb9\x81\xe9\xab\x94\xe4\xb8\xad\xe6\x96\x87)"
250 str_tw = "Traditional Chinese (\xe7\xb9\x81\xe9\xab\x94\xe4\xb8\xad\xe6\x96\x87)".force_encoding('UTF-8')
253 if str_tw.respond_to?(:force_encoding)
254 str_tw.force_encoding('UTF-8')
255 end
256 assert_equal str_tw, l(:general_lang_name)
251 assert_equal str_tw, l(:general_lang_name)
257 end
252 end
258
253
259 def test_french_locale
254 def test_french_locale
260 set_language_if_valid 'fr'
255 set_language_if_valid 'fr'
261 str_fr = "Fran\xc3\xa7ais"
256 str_fr = "Fran\xc3\xa7ais".force_encoding('UTF-8')
262 if str_fr.respond_to?(:force_encoding)
263 str_fr.force_encoding('UTF-8')
264 end
265 assert_equal str_fr, l(:general_lang_name)
257 assert_equal str_fr, l(:general_lang_name)
266 end
258 end
267 end
259 end
@@ -16,210 +16,203
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../../../../../test_helper', __FILE__)
18 require File.expand_path('../../../../../../test_helper', __FILE__)
19 begin
20 require 'mocha/setup'
21
22 class BazaarAdapterTest < ActiveSupport::TestCase
23 REPOSITORY_PATH = Rails.root.join('tmp/test/bazaar_repository').to_s
24 REPOSITORY_PATH.gsub!(/\/+/, '/')
25
26 if File.directory?(REPOSITORY_PATH)
27 def setup
28 @adapter = Redmine::Scm::Adapters::BazaarAdapter.new(
29 File.join(REPOSITORY_PATH, "trunk")
30 )
31 end
32
19
33 def test_scm_version
20 class BazaarAdapterTest < ActiveSupport::TestCase
34 to_test = { "Bazaar (bzr) 2.1.2\n" => [2,1,2],
21 REPOSITORY_PATH = Rails.root.join('tmp/test/bazaar_repository').to_s
35 "2.1.1\n1.7\n1.8" => [2,1,1],
22 REPOSITORY_PATH.gsub!(/\/+/, '/')
36 "2.0.1\r\n1.8.1\r\n1.9.1" => [2,0,1]}
37 to_test.each do |s, v|
38 test_scm_version_for(s, v)
39 end
40 end
41
23
42 def test_cat
24 if File.directory?(REPOSITORY_PATH)
43 cat = @adapter.cat('directory/document.txt')
25 def setup
44 assert cat =~ /Write the contents of a file as of a given revision to standard output/
26 @adapter = Redmine::Scm::Adapters::BazaarAdapter.new(
45 end
27 File.join(REPOSITORY_PATH, "trunk")
28 )
29 end
46
30
47 def test_cat_path_invalid
31 def test_scm_version
48 assert_nil @adapter.cat('invalid')
32 to_test = { "Bazaar (bzr) 2.1.2\n" => [2,1,2],
33 "2.1.1\n1.7\n1.8" => [2,1,1],
34 "2.0.1\r\n1.8.1\r\n1.9.1" => [2,0,1]}
35 to_test.each do |s, v|
36 test_scm_version_for(s, v)
49 end
37 end
38 end
50
39
51 def test_cat_revision_invalid
40 def test_cat
52 assert_nil @adapter.cat('doc-mkdir.txt', '12345678')
41 cat = @adapter.cat('directory/document.txt')
53 end
42 assert cat =~ /Write the contents of a file as of a given revision to standard output/
43 end
54
44
55 def test_diff
45 def test_cat_path_invalid
56 diff1 = @adapter.diff('doc-mkdir.txt', 3, 2)
46 assert_nil @adapter.cat('invalid')
57 assert_equal 21, diff1.size
47 end
58 buf = diff1[14].gsub(/\r\n|\r|\n/, "")
59 assert_equal "-Display more information.", buf
60 end
61
48
62 def test_diff_path_invalid
49 def test_cat_revision_invalid
63 assert_equal [], @adapter.diff('invalid', 1)
50 assert_nil @adapter.cat('doc-mkdir.txt', '12345678')
64 end
51 end
65
52
66 def test_diff_revision_invalid
53 def test_diff
67 assert_equal [], @adapter.diff(nil, 12345678)
54 diff1 = @adapter.diff('doc-mkdir.txt', 3, 2)
68 assert_equal [], @adapter.diff(nil, 12345678, 87654321)
55 assert_equal 21, diff1.size
69 end
56 buf = diff1[14].gsub(/\r\n|\r|\n/, "")
57 assert_equal "-Display more information.", buf
58 end
70
59
71 def test_annotate
60 def test_diff_path_invalid
72 annotate = @adapter.annotate('doc-mkdir.txt')
61 assert_equal [], @adapter.diff('invalid', 1)
73 assert_equal 17, annotate.lines.size
62 end
74 assert_equal '1', annotate.revisions[0].identifier
75 assert_equal 'jsmith@', annotate.revisions[0].author
76 assert_equal 'mkdir', annotate.lines[0]
77 end
78
63
79 def test_annotate_path_invalid
64 def test_diff_revision_invalid
80 assert_nil @adapter.annotate('invalid')
65 assert_equal [], @adapter.diff(nil, 12345678)
81 end
66 assert_equal [], @adapter.diff(nil, 12345678, 87654321)
67 end
82
68
83 def test_annotate_revision_invalid
69 def test_annotate
84 assert_nil @adapter.annotate('doc-mkdir.txt', '12345678')
70 annotate = @adapter.annotate('doc-mkdir.txt')
85 end
71 assert_equal 17, annotate.lines.size
72 assert_equal '1', annotate.revisions[0].identifier
73 assert_equal 'jsmith@', annotate.revisions[0].author
74 assert_equal 'mkdir', annotate.lines[0]
75 end
86
76
87 def test_branch_conf_path
77 def test_annotate_path_invalid
88 p = "c:\\test\\test\\"
78 assert_nil @adapter.annotate('invalid')
89 bcp = Redmine::Scm::Adapters::BazaarAdapter.branch_conf_path(p)
79 end
90 assert_equal File.join("c:\\test\\test", ".bzr", "branch", "branch.conf"), bcp
91 p = "c:\\test\\test\\.bzr"
92 bcp = Redmine::Scm::Adapters::BazaarAdapter.branch_conf_path(p)
93 assert_equal File.join("c:\\test\\test", ".bzr", "branch", "branch.conf"), bcp
94 p = "c:\\test\\test\\.bzr\\"
95 bcp = Redmine::Scm::Adapters::BazaarAdapter.branch_conf_path(p)
96 assert_equal File.join("c:\\test\\test", ".bzr", "branch", "branch.conf"), bcp
97 p = "c:\\test\\test"
98 bcp = Redmine::Scm::Adapters::BazaarAdapter.branch_conf_path(p)
99 assert_equal File.join("c:\\test\\test", ".bzr", "branch", "branch.conf"), bcp
100 p = "\\\\server\\test\\test\\"
101 bcp = Redmine::Scm::Adapters::BazaarAdapter.branch_conf_path(p)
102 assert_equal File.join("\\\\server\\test\\test", ".bzr", "branch", "branch.conf"), bcp
103 end
104
80
105 def test_append_revisions_only_true
81 def test_annotate_revision_invalid
106 assert_equal true, @adapter.append_revisions_only
82 assert_nil @adapter.annotate('doc-mkdir.txt', '12345678')
107 end
83 end
108
84
109 def test_append_revisions_only_false
85 def test_branch_conf_path
110 adpt = Redmine::Scm::Adapters::BazaarAdapter.new(
86 p = "c:\\test\\test\\"
111 File.join(REPOSITORY_PATH, "empty-branch")
87 bcp = Redmine::Scm::Adapters::BazaarAdapter.branch_conf_path(p)
112 )
88 assert_equal File.join("c:\\test\\test", ".bzr", "branch", "branch.conf"), bcp
113 assert_equal false, adpt.append_revisions_only
89 p = "c:\\test\\test\\.bzr"
114 end
90 bcp = Redmine::Scm::Adapters::BazaarAdapter.branch_conf_path(p)
91 assert_equal File.join("c:\\test\\test", ".bzr", "branch", "branch.conf"), bcp
92 p = "c:\\test\\test\\.bzr\\"
93 bcp = Redmine::Scm::Adapters::BazaarAdapter.branch_conf_path(p)
94 assert_equal File.join("c:\\test\\test", ".bzr", "branch", "branch.conf"), bcp
95 p = "c:\\test\\test"
96 bcp = Redmine::Scm::Adapters::BazaarAdapter.branch_conf_path(p)
97 assert_equal File.join("c:\\test\\test", ".bzr", "branch", "branch.conf"), bcp
98 p = "\\\\server\\test\\test\\"
99 bcp = Redmine::Scm::Adapters::BazaarAdapter.branch_conf_path(p)
100 assert_equal File.join("\\\\server\\test\\test", ".bzr", "branch", "branch.conf"), bcp
101 end
115
102
116 def test_append_revisions_only_shared_repo
103 def test_append_revisions_only_true
117 adpt = Redmine::Scm::Adapters::BazaarAdapter.new(
104 assert_equal true, @adapter.append_revisions_only
118 REPOSITORY_PATH
105 end
119 )
120 assert_equal false, adpt.append_revisions_only
121 end
122
106
123 def test_info_not_nil
107 def test_append_revisions_only_false
124 assert_not_nil @adapter.info
108 adpt = Redmine::Scm::Adapters::BazaarAdapter.new(
125 end
109 File.join(REPOSITORY_PATH, "empty-branch")
110 )
111 assert_equal false, adpt.append_revisions_only
112 end
126
113
127 def test_info_nil
114 def test_append_revisions_only_shared_repo
128 adpt = Redmine::Scm::Adapters::BazaarAdapter.new(
115 adpt = Redmine::Scm::Adapters::BazaarAdapter.new(
129 "/invalid/invalid/"
116 REPOSITORY_PATH
130 )
117 )
131 assert_nil adpt.info
118 assert_equal false, adpt.append_revisions_only
132 end
119 end
133
120
134 def test_info
121 def test_info_not_nil
135 info = @adapter.info
122 assert_not_nil @adapter.info
136 assert_equal 4, info.lastrev.identifier.to_i
123 end
137 end
138
124
139 def test_info_emtpy
125 def test_info_nil
140 adpt = Redmine::Scm::Adapters::BazaarAdapter.new(
126 adpt = Redmine::Scm::Adapters::BazaarAdapter.new(
141 File.join(REPOSITORY_PATH, "empty-branch")
127 "/invalid/invalid/"
142 )
128 )
143 assert_equal 0, adpt.info.lastrev.identifier.to_i
129 assert_nil adpt.info
144 end
130 end
145
131
146 def test_entries_path_invalid
132 def test_info
147 assert_equal [], @adapter.entries('invalid')
133 info = @adapter.info
148 end
134 assert_equal 4, info.lastrev.identifier.to_i
135 end
149
136
150 def test_entries_revision_invalid
137 def test_info_emtpy
151 assert_nil @adapter.entries(nil, 12345678)
138 adpt = Redmine::Scm::Adapters::BazaarAdapter.new(
152 end
139 File.join(REPOSITORY_PATH, "empty-branch")
140 )
141 assert_equal 0, adpt.info.lastrev.identifier.to_i
142 end
153
143
154 def test_revisions
144 def test_entries_path_invalid
155 revisions = @adapter.revisions(nil, 4, 2)
145 assert_equal [], @adapter.entries('invalid')
156 assert_equal 3, revisions.size
146 end
157 assert_equal 2, revisions[2].identifier
158 assert_equal 'jsmith@foo.bar-20071203175224-v0eog5d5wrgdrshg', revisions[2].scmid
159 assert_equal 4, revisions[0].identifier
160 assert_equal 'jsmith@foo.bar-20071203175422-t40bf8li5zz0c4cg', revisions[0].scmid
161 assert_equal 2, revisions[0].paths.size
162 assert_equal 'D', revisions[0].paths[0][:action]
163 assert_equal '/doc-deleted.txt', revisions[0].paths[0][:path]
164 assert_equal 'docdeleted.txt-20071203175320-iwwj561ojuubs3gt-1', revisions[0].paths[0][:revision]
165 assert_equal 'M', revisions[0].paths[1][:action]
166 assert_equal '/directory/doc-ls.txt', revisions[0].paths[1][:path]
167 assert_equal 'docls.txt-20071203175005-a3hyc3mn0shl7cgu-1', revisions[0].paths[1][:revision]
168 end
169
147
170 def test_revisions_path_invalid
148 def test_entries_revision_invalid
171 assert_nil @adapter.revisions('invalid')
149 assert_nil @adapter.entries(nil, 12345678)
172 end
150 end
173
151
174 def test_revisions_revision_invalid
152 def test_revisions
175 assert_nil @adapter.revisions(nil, 12345678)
153 revisions = @adapter.revisions(nil, 4, 2)
176 assert_nil @adapter.revisions(nil, 12345678, 87654321)
154 assert_equal 3, revisions.size
177 end
155 assert_equal 2, revisions[2].identifier
156 assert_equal 'jsmith@foo.bar-20071203175224-v0eog5d5wrgdrshg', revisions[2].scmid
157 assert_equal 4, revisions[0].identifier
158 assert_equal 'jsmith@foo.bar-20071203175422-t40bf8li5zz0c4cg', revisions[0].scmid
159 assert_equal 2, revisions[0].paths.size
160 assert_equal 'D', revisions[0].paths[0][:action]
161 assert_equal '/doc-deleted.txt', revisions[0].paths[0][:path]
162 assert_equal 'docdeleted.txt-20071203175320-iwwj561ojuubs3gt-1', revisions[0].paths[0][:revision]
163 assert_equal 'M', revisions[0].paths[1][:action]
164 assert_equal '/directory/doc-ls.txt', revisions[0].paths[1][:path]
165 assert_equal 'docls.txt-20071203175005-a3hyc3mn0shl7cgu-1', revisions[0].paths[1][:revision]
166 end
178
167
179 def test_entry
168 def test_revisions_path_invalid
180 entry = @adapter.entry()
169 assert_nil @adapter.revisions('invalid')
181 assert_equal "", entry.path
170 end
182 assert_equal "dir", entry.kind
171
183 entry = @adapter.entry('')
172 def test_revisions_revision_invalid
184 assert_equal "", entry.path
173 assert_nil @adapter.revisions(nil, 12345678)
174 assert_nil @adapter.revisions(nil, 12345678, 87654321)
175 end
176
177 def test_entry
178 entry = @adapter.entry()
179 assert_equal "", entry.path
180 assert_equal "dir", entry.kind
181 entry = @adapter.entry('')
182 assert_equal "", entry.path
183 assert_equal "dir", entry.kind
184 assert_nil @adapter.entry('invalid')
185 assert_nil @adapter.entry('/invalid')
186 assert_nil @adapter.entry('/invalid/')
187 assert_nil @adapter.entry('invalid/invalid')
188 assert_nil @adapter.entry('invalid/invalid/')
189 assert_nil @adapter.entry('/invalid/invalid')
190 assert_nil @adapter.entry('/invalid/invalid/')
191 ["doc-ls.txt", "/doc-ls.txt"].each do |path|
192 entry = @adapter.entry(path, 2)
193 assert_equal "doc-ls.txt", entry.path
194 assert_equal "file", entry.kind
195 end
196 ["directory", "/directory", "/directory/"].each do |path|
197 entry = @adapter.entry(path, 2)
198 assert_equal "directory", entry.path
185 assert_equal "dir", entry.kind
199 assert_equal "dir", entry.kind
186 assert_nil @adapter.entry('invalid')
187 assert_nil @adapter.entry('/invalid')
188 assert_nil @adapter.entry('/invalid/')
189 assert_nil @adapter.entry('invalid/invalid')
190 assert_nil @adapter.entry('invalid/invalid/')
191 assert_nil @adapter.entry('/invalid/invalid')
192 assert_nil @adapter.entry('/invalid/invalid/')
193 ["doc-ls.txt", "/doc-ls.txt"].each do |path|
194 entry = @adapter.entry(path, 2)
195 assert_equal "doc-ls.txt", entry.path
196 assert_equal "file", entry.kind
197 end
198 ["directory", "/directory", "/directory/"].each do |path|
199 entry = @adapter.entry(path, 2)
200 assert_equal "directory", entry.path
201 assert_equal "dir", entry.kind
202 end
203 ["directory/document.txt", "/directory/document.txt"].each do |path|
204 entry = @adapter.entry(path, 2)
205 assert_equal "directory/document.txt", entry.path
206 assert_equal "file", entry.kind
207 end
208 end
200 end
201 ["directory/document.txt", "/directory/document.txt"].each do |path|
202 entry = @adapter.entry(path, 2)
203 assert_equal "directory/document.txt", entry.path
204 assert_equal "file", entry.kind
205 end
206 end
209
207
210 private
208 private
211
209
212 def test_scm_version_for(scm_command_version, version)
210 def test_scm_version_for(scm_command_version, version)
213 @adapter.class.expects(:scm_version_from_command_line).returns(scm_command_version)
211 @adapter.class.expects(:scm_version_from_command_line).returns(scm_command_version)
214 assert_equal version, @adapter.class.scm_command_version
212 assert_equal version, @adapter.class.scm_command_version
215 end
216 else
217 puts "Bazaar test repository NOT FOUND. Skipping unit tests !!!"
218 def test_fake; assert true end
219 end
213 end
220 end
214 else
221 rescue LoadError
215 puts "Bazaar test repository NOT FOUND. Skipping unit tests !!!"
222 class BazaarMochaFake < ActiveSupport::TestCase
216 def test_fake; assert true end
223 def test_fake; assert(false, "Requires mocha to run those tests") end
224 end
217 end
225 end
218 end
@@ -16,100 +16,91
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../../../../../test_helper', __FILE__)
18 require File.expand_path('../../../../../../test_helper', __FILE__)
19 begin
20 require 'mocha/setup'
21
19
22 class CvsAdapterTest < ActiveSupport::TestCase
20 class CvsAdapterTest < ActiveSupport::TestCase
23 REPOSITORY_PATH = Rails.root.join('tmp/test/cvs_repository').to_s
21 REPOSITORY_PATH = Rails.root.join('tmp/test/cvs_repository').to_s
24 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
22 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
25 MODULE_NAME = 'test'
23 MODULE_NAME = 'test'
26
24
27 if File.directory?(REPOSITORY_PATH)
25 if File.directory?(REPOSITORY_PATH)
28 def setup
26 def setup
29 @adapter = Redmine::Scm::Adapters::CvsAdapter.new(MODULE_NAME, REPOSITORY_PATH)
27 @adapter = Redmine::Scm::Adapters::CvsAdapter.new(MODULE_NAME, REPOSITORY_PATH)
30 end
28 end
31
29
32 def test_scm_version
30 def test_scm_version
33 to_test = { "\nConcurrent Versions System (CVS) 1.12.13 (client/server)\n" => [1,12,13],
31 to_test = { "\nConcurrent Versions System (CVS) 1.12.13 (client/server)\n" => [1,12,13],
34 "\r\n1.12.12\r\n1.12.11" => [1,12,12],
32 "\r\n1.12.12\r\n1.12.11" => [1,12,12],
35 "1.12.11\r\n1.12.10\r\n" => [1,12,11]}
33 "1.12.11\r\n1.12.10\r\n" => [1,12,11]}
36 to_test.each do |s, v|
34 to_test.each do |s, v|
37 test_scm_version_for(s, v)
35 test_scm_version_for(s, v)
38 end
39 end
36 end
37 end
40
38
41 def test_revisions_all
39 def test_revisions_all
42 cnt = 0
40 cnt = 0
43 @adapter.revisions('', nil, nil, :log_encoding => 'UTF-8') do |revision|
41 @adapter.revisions('', nil, nil, :log_encoding => 'UTF-8') do |revision|
44 cnt += 1
42 cnt += 1
45 end
46 assert_equal 16, cnt
47 end
43 end
44 assert_equal 16, cnt
45 end
48
46
49 def test_revisions_from_rev3
47 def test_revisions_from_rev3
50 rev3_committed_on = Time.gm(2007, 12, 13, 16, 27, 22)
48 rev3_committed_on = Time.gm(2007, 12, 13, 16, 27, 22)
51 cnt = 0
49 cnt = 0
52 @adapter.revisions('', rev3_committed_on, nil, :log_encoding => 'UTF-8') do |revision|
50 @adapter.revisions('', rev3_committed_on, nil, :log_encoding => 'UTF-8') do |revision|
53 cnt += 1
51 cnt += 1
54 end
55 assert_equal 4, cnt
56 end
52 end
53 assert_equal 4, cnt
54 end
57
55
58 def test_entries_rev3
56 def test_entries_rev3
59 rev3_committed_on = Time.gm(2007, 12, 13, 16, 27, 22)
57 rev3_committed_on = Time.gm(2007, 12, 13, 16, 27, 22)
60 entries = @adapter.entries('sources', rev3_committed_on)
58 entries = @adapter.entries('sources', rev3_committed_on)
61 assert_equal 2, entries.size
59 assert_equal 2, entries.size
62 assert_equal entries[0].name, "watchers_controller.rb"
60 assert_equal entries[0].name, "watchers_controller.rb"
63 assert_equal entries[0].lastrev.time, Time.gm(2007, 12, 13, 16, 27, 22)
61 assert_equal entries[0].lastrev.time, Time.gm(2007, 12, 13, 16, 27, 22)
64 end
62 end
65
63
66 def test_path_encoding_default_utf8
64 def test_path_encoding_default_utf8
67 adpt1 = Redmine::Scm::Adapters::CvsAdapter.new(
65 adpt1 = Redmine::Scm::Adapters::CvsAdapter.new(
68 MODULE_NAME,
66 MODULE_NAME,
69 REPOSITORY_PATH
67 REPOSITORY_PATH
70 )
68 )
71 assert_equal "UTF-8", adpt1.path_encoding
69 assert_equal "UTF-8", adpt1.path_encoding
72 adpt2 = Redmine::Scm::Adapters::CvsAdapter.new(
70 adpt2 = Redmine::Scm::Adapters::CvsAdapter.new(
73 MODULE_NAME,
71 MODULE_NAME,
74 REPOSITORY_PATH,
72 REPOSITORY_PATH,
75 nil,
73 nil,
76 nil,
74 nil,
77 ""
75 ""
78 )
76 )
79 assert_equal "UTF-8", adpt2.path_encoding
77 assert_equal "UTF-8", adpt2.path_encoding
80 end
78 end
81
79
82 def test_root_url_path
80 def test_root_url_path
83 to_test = {
81 to_test = {
84 ':pserver:cvs_user:cvs_password@123.456.789.123:9876/repo' => '/repo',
82 ':pserver:cvs_user:cvs_password@123.456.789.123:9876/repo' => '/repo',
85 ':pserver:cvs_user:cvs_password@123.456.789.123/repo' => '/repo',
83 ':pserver:cvs_user:cvs_password@123.456.789.123/repo' => '/repo',
86 ':pserver:cvs_user:cvs_password@cvs_server:/repo' => '/repo',
84 ':pserver:cvs_user:cvs_password@cvs_server:/repo' => '/repo',
87 ':pserver:cvs_user:cvs_password@cvs_server:9876/repo' => '/repo',
85 ':pserver:cvs_user:cvs_password@cvs_server:9876/repo' => '/repo',
88 ':pserver:cvs_user:cvs_password@cvs_server/repo' => '/repo',
86 ':pserver:cvs_user:cvs_password@cvs_server/repo' => '/repo',
89 ':pserver:cvs_user:cvs_password@cvs_server/path/repo' => '/path/repo',
87 ':pserver:cvs_user:cvs_password@cvs_server/path/repo' => '/path/repo',
90 ':ext:cvsservername:/path' => '/path'
88 ':ext:cvsservername:/path' => '/path'
91 }
89 }
92
90
93 to_test.each do |string, expected|
91 to_test.each do |string, expected|
94 assert_equal expected, Redmine::Scm::Adapters::CvsAdapter.new('foo', string).send(:root_url_path), "#{string} failed"
92 assert_equal expected, Redmine::Scm::Adapters::CvsAdapter.new('foo', string).send(:root_url_path), "#{string} failed"
95 end
96 end
93 end
94 end
97
95
98 private
96 private
99
97
100 def test_scm_version_for(scm_command_version, version)
98 def test_scm_version_for(scm_command_version, version)
101 @adapter.class.expects(:scm_version_from_command_line).returns(scm_command_version)
99 @adapter.class.expects(:scm_version_from_command_line).returns(scm_command_version)
102 assert_equal version, @adapter.class.scm_command_version
100 assert_equal version, @adapter.class.scm_command_version
103 end
104 else
105 puts "Cvs test repository NOT FOUND. Skipping unit tests !!!"
106 def test_fake; assert true end
107 end
101 end
108 end
102 else
109
103 puts "Cvs test repository NOT FOUND. Skipping unit tests !!!"
110 rescue LoadError
104 def test_fake; assert true end
111 class CvsMochaFake < ActiveSupport::TestCase
112 def test_fake; assert(false, "Requires mocha to run those tests") end
113 end
105 end
114 end
106 end
115
@@ -16,54 +16,45
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../../../../../test_helper', __FILE__)
18 require File.expand_path('../../../../../../test_helper', __FILE__)
19 begin
20 require 'mocha/setup'
21
19
22 class DarcsAdapterTest < ActiveSupport::TestCase
20 class DarcsAdapterTest < ActiveSupport::TestCase
23 REPOSITORY_PATH = Rails.root.join('tmp/test/darcs_repository').to_s
21 REPOSITORY_PATH = Rails.root.join('tmp/test/darcs_repository').to_s
24
22
25 if File.directory?(REPOSITORY_PATH)
23 if File.directory?(REPOSITORY_PATH)
26 def setup
24 def setup
27 @adapter = Redmine::Scm::Adapters::DarcsAdapter.new(REPOSITORY_PATH)
25 @adapter = Redmine::Scm::Adapters::DarcsAdapter.new(REPOSITORY_PATH)
28 end
26 end
29
30 def test_darcsversion
31 to_test = { "1.0.9 (release)\n" => [1,0,9] ,
32 "2.2.0 (release)\n" => [2,2,0] }
33 to_test.each do |s, v|
34 test_darcsversion_for(s, v)
35 end
36 end
37
27
38 def test_revisions
28 def test_darcsversion
39 id1 = '20080308225258-98289-761f654d669045eabee90b91b53a21ce5593cadf.gz'
29 to_test = { "1.0.9 (release)\n" => [1,0,9] ,
40 revs = @adapter.revisions('', nil, nil, {:with_path => true})
30 "2.2.0 (release)\n" => [2,2,0] }
41 assert_equal 6, revs.size
31 to_test.each do |s, v|
42 assert_equal id1, revs[5].scmid
32 test_darcsversion_for(s, v)
43 paths = revs[5].paths
44 assert_equal 5, paths.size
45 assert_equal 'A', paths[0][:action]
46 assert_equal '/README', paths[0][:path]
47 assert_equal 'A', paths[1][:action]
48 assert_equal '/images', paths[1][:path]
49 end
33 end
34 end
50
35
51 private
36 def test_revisions
37 id1 = '20080308225258-98289-761f654d669045eabee90b91b53a21ce5593cadf.gz'
38 revs = @adapter.revisions('', nil, nil, {:with_path => true})
39 assert_equal 6, revs.size
40 assert_equal id1, revs[5].scmid
41 paths = revs[5].paths
42 assert_equal 5, paths.size
43 assert_equal 'A', paths[0][:action]
44 assert_equal '/README', paths[0][:path]
45 assert_equal 'A', paths[1][:action]
46 assert_equal '/images', paths[1][:path]
47 end
52
48
53 def test_darcsversion_for(darcsversion, version)
49 private
54 @adapter.class.expects(:darcs_binary_version_from_command_line).returns(darcsversion)
55 assert_equal version, @adapter.class.darcs_binary_version
56 end
57
50
58 else
51 def test_darcsversion_for(darcsversion, version)
59 puts "Darcs test repository NOT FOUND. Skipping unit tests !!!"
52 @adapter.class.expects(:darcs_binary_version_from_command_line).returns(darcsversion)
60 def test_fake; assert true end
53 assert_equal version, @adapter.class.darcs_binary_version
61 end
54 end
62 end
63
55
64 rescue LoadError
56 else
65 class DarcsMochaFake < ActiveSupport::TestCase
57 puts "Darcs test repository NOT FOUND. Skipping unit tests !!!"
66 def test_fake; assert(false, "Requires mocha to run those tests") end
58 def test_fake; assert true end
67 end
59 end
68 end
60 end
69
This diff has been collapsed as it changes many lines, (1028 lines changed) Show them Hide them
@@ -16,594 +16,582
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../../../../../test_helper', __FILE__)
18 require File.expand_path('../../../../../../test_helper', __FILE__)
19 begin
20 require 'mocha/setup'
21
22 class GitAdapterTest < ActiveSupport::TestCase
23 REPOSITORY_PATH = Rails.root.join('tmp/test/git_repository').to_s
24
25 FELIX_HEX = "Felix Sch\xC3\xA4fer"
26 CHAR_1_HEX = "\xc3\x9c"
27
28 ## Git, Mercurial and CVS path encodings are binary.
29 ## Subversion supports URL encoding for path.
30 ## Redmine Mercurial adapter and extension use URL encoding.
31 ## Git accepts only binary path in command line parameter.
32 ## So, there is no way to use binary command line parameter in JRuby.
33 JRUBY_SKIP = (RUBY_PLATFORM == 'java')
34 JRUBY_SKIP_STR = "TODO: This test fails in JRuby"
35
36 if File.directory?(REPOSITORY_PATH)
37 ## Ruby uses ANSI api to fork a process on Windows.
38 ## Japanese Shift_JIS and Traditional Chinese Big5 have 0x5c(backslash) problem
39 ## and these are incompatible with ASCII.
40 ## Git for Windows (msysGit) changed internal API from ANSI to Unicode in 1.7.10
41 ## http://code.google.com/p/msysgit/issues/detail?id=80
42 ## So, Latin-1 path tests fail on Japanese Windows
43 WINDOWS_PASS = (Redmine::Platform.mswin? &&
44 Redmine::Scm::Adapters::GitAdapter.client_version_above?([1, 7, 10]))
45 WINDOWS_SKIP_STR = "TODO: This test fails in Git for Windows above 1.7.10"
46
47 def setup
48 adapter_class = Redmine::Scm::Adapters::GitAdapter
49 assert adapter_class
50 assert adapter_class.client_command
51 assert_equal true, adapter_class.client_available
52 assert_equal true, adapter_class.client_version_above?([1])
53 assert_equal true, adapter_class.client_version_above?([1, 0])
54
55 @adapter = Redmine::Scm::Adapters::GitAdapter.new(
56 REPOSITORY_PATH,
57 nil,
58 nil,
59 nil,
60 'ISO-8859-1'
61 )
62 assert @adapter
63 @char_1 = CHAR_1_HEX.dup
64 @str_felix_hex = FELIX_HEX.dup
65 if @char_1.respond_to?(:force_encoding)
66 @char_1.force_encoding('UTF-8')
67 @str_felix_hex.force_encoding('ASCII-8BIT')
68 end
69 end
70
19
71 def test_scm_version
20 class GitAdapterTest < ActiveSupport::TestCase
72 to_test = { "git version 1.7.3.4\n" => [1,7,3,4],
21 REPOSITORY_PATH = Rails.root.join('tmp/test/git_repository').to_s
73 "1.6.1\n1.7\n1.8" => [1,6,1],
22
74 "1.6.2\r\n1.8.1\r\n1.9.1" => [1,6,2]}
23 FELIX_HEX = "Felix Sch\xC3\xA4fer"
75 to_test.each do |s, v|
24 CHAR_1_HEX = "\xc3\x9c"
76 test_scm_version_for(s, v)
25
77 end
26 ## Git, Mercurial and CVS path encodings are binary.
78 end
27 ## Subversion supports URL encoding for path.
28 ## Redmine Mercurial adapter and extension use URL encoding.
29 ## Git accepts only binary path in command line parameter.
30 ## So, there is no way to use binary command line parameter in JRuby.
31 JRUBY_SKIP = (RUBY_PLATFORM == 'java')
32 JRUBY_SKIP_STR = "TODO: This test fails in JRuby"
33
34 if File.directory?(REPOSITORY_PATH)
35 ## Ruby uses ANSI api to fork a process on Windows.
36 ## Japanese Shift_JIS and Traditional Chinese Big5 have 0x5c(backslash) problem
37 ## and these are incompatible with ASCII.
38 ## Git for Windows (msysGit) changed internal API from ANSI to Unicode in 1.7.10
39 ## http://code.google.com/p/msysgit/issues/detail?id=80
40 ## So, Latin-1 path tests fail on Japanese Windows
41 WINDOWS_PASS = (Redmine::Platform.mswin? &&
42 Redmine::Scm::Adapters::GitAdapter.client_version_above?([1, 7, 10]))
43 WINDOWS_SKIP_STR = "TODO: This test fails in Git for Windows above 1.7.10"
44
45 def setup
46 adapter_class = Redmine::Scm::Adapters::GitAdapter
47 assert adapter_class
48 assert adapter_class.client_command
49 assert_equal true, adapter_class.client_available
50 assert_equal true, adapter_class.client_version_above?([1])
51 assert_equal true, adapter_class.client_version_above?([1, 0])
52
53 @adapter = Redmine::Scm::Adapters::GitAdapter.new(
54 REPOSITORY_PATH,
55 nil,
56 nil,
57 nil,
58 'ISO-8859-1'
59 )
60 assert @adapter
61 @char_1 = CHAR_1_HEX.dup.force_encoding('UTF-8')
62 @str_felix_hex = FELIX_HEX.dup.force_encoding('ASCII-8BIT')
63 end
79
64
80 def test_branches
65 def test_scm_version
81 brs = []
66 to_test = { "git version 1.7.3.4\n" => [1,7,3,4],
82 @adapter.branches.each do |b|
67 "1.6.1\n1.7\n1.8" => [1,6,1],
83 brs << b
68 "1.6.2\r\n1.8.1\r\n1.9.1" => [1,6,2]}
84 end
69 to_test.each do |s, v|
85 assert_equal 6, brs.length
70 test_scm_version_for(s, v)
86 br_issue_8857 = brs[0]
87 assert_equal 'issue-8857', br_issue_8857.to_s
88 assert_equal '2a682156a3b6e77a8bf9cd4590e8db757f3c6c78', br_issue_8857.revision
89 assert_equal br_issue_8857.scmid, br_issue_8857.revision
90 assert_equal false, br_issue_8857.is_default
91 br_latin_1_path = brs[1]
92 assert_equal 'latin-1-path-encoding', br_latin_1_path.to_s
93 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', br_latin_1_path.revision
94 assert_equal br_latin_1_path.scmid, br_latin_1_path.revision
95 assert_equal false, br_latin_1_path.is_default
96 br_master = brs[2]
97 assert_equal 'master', br_master.to_s
98 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', br_master.revision
99 assert_equal br_master.scmid, br_master.revision
100 assert_equal false, br_master.is_default
101 br_master_20120212 = brs[3]
102 assert_equal 'master-20120212', br_master_20120212.to_s
103 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', br_master_20120212.revision
104 assert_equal br_master_20120212.scmid, br_master_20120212.revision
105 assert_equal true, br_master_20120212.is_default
106 br_latin_1 = brs[-2]
107 assert_equal 'test-latin-1', br_latin_1.to_s
108 assert_equal '67e7792ce20ccae2e4bb73eed09bb397819c8834', br_latin_1.revision
109 assert_equal br_latin_1.scmid, br_latin_1.revision
110 assert_equal false, br_latin_1.is_default
111 br_test = brs[-1]
112 assert_equal 'test_branch', br_test.to_s
113 assert_equal 'fba357b886984ee71185ad2065e65fc0417d9b92', br_test.revision
114 assert_equal br_test.scmid, br_test.revision
115 assert_equal false, br_test.is_default
116 end
71 end
72 end
117
73
118 def test_default_branch
74 def test_branches
119 assert_equal 'master-20120212', @adapter.default_branch
75 brs = []
120 end
76 @adapter.branches.each do |b|
77 brs << b
78 end
79 assert_equal 6, brs.length
80 br_issue_8857 = brs[0]
81 assert_equal 'issue-8857', br_issue_8857.to_s
82 assert_equal '2a682156a3b6e77a8bf9cd4590e8db757f3c6c78', br_issue_8857.revision
83 assert_equal br_issue_8857.scmid, br_issue_8857.revision
84 assert_equal false, br_issue_8857.is_default
85 br_latin_1_path = brs[1]
86 assert_equal 'latin-1-path-encoding', br_latin_1_path.to_s
87 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', br_latin_1_path.revision
88 assert_equal br_latin_1_path.scmid, br_latin_1_path.revision
89 assert_equal false, br_latin_1_path.is_default
90 br_master = brs[2]
91 assert_equal 'master', br_master.to_s
92 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', br_master.revision
93 assert_equal br_master.scmid, br_master.revision
94 assert_equal false, br_master.is_default
95 br_master_20120212 = brs[3]
96 assert_equal 'master-20120212', br_master_20120212.to_s
97 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', br_master_20120212.revision
98 assert_equal br_master_20120212.scmid, br_master_20120212.revision
99 assert_equal true, br_master_20120212.is_default
100 br_latin_1 = brs[-2]
101 assert_equal 'test-latin-1', br_latin_1.to_s
102 assert_equal '67e7792ce20ccae2e4bb73eed09bb397819c8834', br_latin_1.revision
103 assert_equal br_latin_1.scmid, br_latin_1.revision
104 assert_equal false, br_latin_1.is_default
105 br_test = brs[-1]
106 assert_equal 'test_branch', br_test.to_s
107 assert_equal 'fba357b886984ee71185ad2065e65fc0417d9b92', br_test.revision
108 assert_equal br_test.scmid, br_test.revision
109 assert_equal false, br_test.is_default
110 end
121
111
122 def test_tags
112 def test_default_branch
123 assert_equal [
113 assert_equal 'master-20120212', @adapter.default_branch
124 "tag00.lightweight",
114 end
125 "tag01.annotated",
126 ], @adapter.tags
127 end
128
115
129 def test_revisions_master_all
116 def test_tags
130 revs1 = []
117 assert_equal [
131 @adapter.revisions('', nil, "master",{}) do |rev|
118 "tag00.lightweight",
132 revs1 << rev
119 "tag01.annotated",
133 end
120 ], @adapter.tags
134 assert_equal 15, revs1.length
121 end
135 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[ 0].identifier
136 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs1[-1].identifier
137
138 revs2 = []
139 @adapter.revisions('', nil, "master",
140 {:reverse => true}) do |rev|
141 revs2 << rev
142 end
143 assert_equal 15, revs2.length
144 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs2[-1].identifier
145 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs2[ 0].identifier
146 end
147
122
148 def test_revisions_master_merged_rev
123 def test_revisions_master_all
149 revs1 = []
124 revs1 = []
150 @adapter.revisions('',
125 @adapter.revisions('', nil, "master",{}) do |rev|
151 "713f4944648826f558cf548222f813dabe7cbb04",
126 revs1 << rev
152 "master",
153 {:reverse => true}) do |rev|
154 revs1 << rev
155 end
156 assert_equal 8, revs1.length
157 assert_equal 'fba357b886984ee71185ad2065e65fc0417d9b92', revs1[ 0].identifier
158 assert_equal '7e61ac704deecde634b51e59daa8110435dcb3da', revs1[ 1].identifier
159 # 4a07fe31b is not a child of 713f49446
160 assert_equal '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8', revs1[ 2].identifier
161 # Merged revision
162 assert_equal '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf', revs1[ 3].identifier
163 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[-1].identifier
164
165 revs2 = []
166 @adapter.revisions('',
167 "fba357b886984ee71185ad2065e65fc0417d9b92",
168 "master",
169 {:reverse => true}) do |rev|
170 revs2 << rev
171 end
172 assert_equal 7, revs2.length
173 assert_equal '7e61ac704deecde634b51e59daa8110435dcb3da', revs2[ 0].identifier
174 # 4a07fe31b is not a child of fba357b8869
175 assert_equal '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8', revs2[ 1].identifier
176 # Merged revision
177 assert_equal '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf', revs2[ 2].identifier
178 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs2[-1].identifier
179 end
127 end
128 assert_equal 15, revs1.length
129 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[ 0].identifier
130 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs1[-1].identifier
180
131
181 def test_revisions_branch_latin_1_path_encoding_all
132 revs2 = []
182 revs1 = []
133 @adapter.revisions('', nil, "master",
183 @adapter.revisions('', nil, "latin-1-path-encoding",{}) do |rev|
134 {:reverse => true}) do |rev|
184 revs1 << rev
135 revs2 << rev
185 end
186 assert_equal 8, revs1.length
187 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs1[ 0].identifier
188 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs1[-1].identifier
189
190 revs2 = []
191 @adapter.revisions('', nil, "latin-1-path-encoding",
192 {:reverse => true}) do |rev|
193 revs2 << rev
194 end
195 assert_equal 8, revs2.length
196 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs2[-1].identifier
197 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs2[ 0].identifier
198 end
136 end
137 assert_equal 15, revs2.length
138 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs2[-1].identifier
139 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs2[ 0].identifier
140 end
199
141
200 def test_revisions_branch_latin_1_path_encoding_with_rev
142 def test_revisions_master_merged_rev
201 revs1 = []
143 revs1 = []
202 @adapter.revisions('',
144 @adapter.revisions('',
203 '7234cb2750b63f47bff735edc50a1c0a433c2518',
145 "713f4944648826f558cf548222f813dabe7cbb04",
204 "latin-1-path-encoding",
146 "master",
205 {:reverse => true}) do |rev|
147 {:reverse => true}) do |rev|
206 revs1 << rev
148 revs1 << rev
207 end
149 end
208 assert_equal 7, revs1.length
150 assert_equal 8, revs1.length
209 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', revs1[ 0].identifier
151 assert_equal 'fba357b886984ee71185ad2065e65fc0417d9b92', revs1[ 0].identifier
210 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs1[-1].identifier
152 assert_equal '7e61ac704deecde634b51e59daa8110435dcb3da', revs1[ 1].identifier
153 # 4a07fe31b is not a child of 713f49446
154 assert_equal '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8', revs1[ 2].identifier
155 # Merged revision
156 assert_equal '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf', revs1[ 3].identifier
157 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[-1].identifier
158
159 revs2 = []
160 @adapter.revisions('',
161 "fba357b886984ee71185ad2065e65fc0417d9b92",
162 "master",
163 {:reverse => true}) do |rev|
164 revs2 << rev
165 end
166 assert_equal 7, revs2.length
167 assert_equal '7e61ac704deecde634b51e59daa8110435dcb3da', revs2[ 0].identifier
168 # 4a07fe31b is not a child of fba357b8869
169 assert_equal '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8', revs2[ 1].identifier
170 # Merged revision
171 assert_equal '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf', revs2[ 2].identifier
172 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs2[-1].identifier
173 end
211
174
212 revs2 = []
175 def test_revisions_branch_latin_1_path_encoding_all
213 @adapter.revisions('',
176 revs1 = []
214 '57ca437c0acbbcb749821fdf3726a1367056d364',
177 @adapter.revisions('', nil, "latin-1-path-encoding",{}) do |rev|
215 "latin-1-path-encoding",
178 revs1 << rev
216 {:reverse => true}) do |rev|
217 revs2 << rev
218 end
219 assert_equal 3, revs2.length
220 assert_equal '4fc55c43bf3d3dc2efb66145365ddc17639ce81e', revs2[ 0].identifier
221 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs2[-1].identifier
222 end
179 end
180 assert_equal 8, revs1.length
181 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs1[ 0].identifier
182 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs1[-1].identifier
223
183
224 def test_revisions_invalid_rev
184 revs2 = []
225 assert_equal [], @adapter.revisions('', '1234abcd', "master")
185 @adapter.revisions('', nil, "latin-1-path-encoding",
226 assert_raise Redmine::Scm::Adapters::CommandFailed do
186 {:reverse => true}) do |rev|
227 revs1 = []
187 revs2 << rev
228 @adapter.revisions('',
229 '1234abcd',
230 "master",
231 {:reverse => true}) do |rev|
232 revs1 << rev
233 end
234 end
235 end
188 end
189 assert_equal 8, revs2.length
190 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs2[-1].identifier
191 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs2[ 0].identifier
192 end
236
193
237 def test_revisions_includes_master_two_revs
194 def test_revisions_branch_latin_1_path_encoding_with_rev
238 revs1 = []
195 revs1 = []
239 @adapter.revisions('', nil, nil,
196 @adapter.revisions('',
240 {:reverse => true,
197 '7234cb2750b63f47bff735edc50a1c0a433c2518',
241 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
198 "latin-1-path-encoding",
242 :excludes => ['4f26664364207fa8b1af9f8722647ab2d4ac5d43']}) do |rev|
199 {:reverse => true}) do |rev|
243 revs1 << rev
200 revs1 << rev
244 end
201 end
245 assert_equal 2, revs1.length
202 assert_equal 7, revs1.length
246 assert_equal 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b', revs1[ 0].identifier
203 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', revs1[ 0].identifier
247 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[-1].identifier
204 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs1[-1].identifier
248 end
205
206 revs2 = []
207 @adapter.revisions('',
208 '57ca437c0acbbcb749821fdf3726a1367056d364',
209 "latin-1-path-encoding",
210 {:reverse => true}) do |rev|
211 revs2 << rev
212 end
213 assert_equal 3, revs2.length
214 assert_equal '4fc55c43bf3d3dc2efb66145365ddc17639ce81e', revs2[ 0].identifier
215 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs2[-1].identifier
216 end
249
217
250 def test_revisions_includes_master_two_revs_from_origin
218 def test_revisions_invalid_rev
219 assert_equal [], @adapter.revisions('', '1234abcd', "master")
220 assert_raise Redmine::Scm::Adapters::CommandFailed do
251 revs1 = []
221 revs1 = []
252 @adapter.revisions('', nil, nil,
222 @adapter.revisions('',
253 {:reverse => true,
223 '1234abcd',
254 :includes => ['899a15dba03a3b350b89c3f537e4bbe02a03cdc9'],
224 "master",
255 :excludes => []}) do |rev|
225 {:reverse => true}) do |rev|
256 revs1 << rev
226 revs1 << rev
257 end
227 end
258 assert_equal 2, revs1.length
259 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs1[ 0].identifier
260 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', revs1[ 1].identifier
261 end
228 end
229 end
262
230
263 def test_revisions_includes_merged_revs
231 def test_revisions_includes_master_two_revs
264 revs1 = []
232 revs1 = []
265 @adapter.revisions('', nil, nil,
233 @adapter.revisions('', nil, nil,
266 {:reverse => true,
234 {:reverse => true,
267 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
235 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
268 :excludes => ['fba357b886984ee71185ad2065e65fc0417d9b92']}) do |rev|
236 :excludes => ['4f26664364207fa8b1af9f8722647ab2d4ac5d43']}) do |rev|
269 revs1 << rev
237 revs1 << rev
270 end
238 end
271 assert_equal 7, revs1.length
239 assert_equal 2, revs1.length
272 assert_equal '7e61ac704deecde634b51e59daa8110435dcb3da', revs1[ 0].identifier
240 assert_equal 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b', revs1[ 0].identifier
273 assert_equal '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8', revs1[ 1].identifier
241 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[-1].identifier
274 assert_equal '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf', revs1[ 2].identifier
242 end
275 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[-1].identifier
276 end
277
243
278 def test_revisions_includes_two_heads
244 def test_revisions_includes_master_two_revs_from_origin
279 revs1 = []
245 revs1 = []
280 @adapter.revisions('', nil, nil,
246 @adapter.revisions('', nil, nil,
281 {:reverse => true,
247 {:reverse => true,
282 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c',
248 :includes => ['899a15dba03a3b350b89c3f537e4bbe02a03cdc9'],
283 '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127'],
249 :excludes => []}) do |rev|
284 :excludes => ['4f26664364207fa8b1af9f8722647ab2d4ac5d43',
250 revs1 << rev
285 '4fc55c43bf3d3dc2efb66145365ddc17639ce81e']}) do |rev|
251 end
286 revs1 << rev
252 assert_equal 2, revs1.length
287 end
253 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs1[ 0].identifier
288 assert_equal 4, revs1.length
254 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', revs1[ 1].identifier
289 assert_equal 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b', revs1[ 0].identifier
255 end
290 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[ 1].identifier
291 assert_equal '64f1f3e89ad1cb57976ff0ad99a107012ba3481d', revs1[-2].identifier
292 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs1[-1].identifier
293 end
294
256
295 def test_revisions_disjointed_histories_revisions
257 def test_revisions_includes_merged_revs
258 revs1 = []
259 @adapter.revisions('', nil, nil,
260 {:reverse => true,
261 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
262 :excludes => ['fba357b886984ee71185ad2065e65fc0417d9b92']}) do |rev|
263 revs1 << rev
264 end
265 assert_equal 7, revs1.length
266 assert_equal '7e61ac704deecde634b51e59daa8110435dcb3da', revs1[ 0].identifier
267 assert_equal '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8', revs1[ 1].identifier
268 assert_equal '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf', revs1[ 2].identifier
269 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[-1].identifier
270 end
271
272 def test_revisions_includes_two_heads
273 revs1 = []
274 @adapter.revisions('', nil, nil,
275 {:reverse => true,
276 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c',
277 '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127'],
278 :excludes => ['4f26664364207fa8b1af9f8722647ab2d4ac5d43',
279 '4fc55c43bf3d3dc2efb66145365ddc17639ce81e']}) do |rev|
280 revs1 << rev
281 end
282 assert_equal 4, revs1.length
283 assert_equal 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b', revs1[ 0].identifier
284 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[ 1].identifier
285 assert_equal '64f1f3e89ad1cb57976ff0ad99a107012ba3481d', revs1[-2].identifier
286 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs1[-1].identifier
287 end
288
289 def test_revisions_disjointed_histories_revisions
290 revs1 = []
291 @adapter.revisions('', nil, nil,
292 {:reverse => true,
293 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c',
294 '92397af84d22f27389c822848ecd5b463c181583'],
295 :excludes => ['95488a44bc25f7d1f97d775a31359539ff333a63',
296 '4f26664364207fa8b1af9f8722647ab2d4ac5d43'] }) do |rev|
297 revs1 << rev
298 end
299 assert_equal 4, revs1.length
300 assert_equal 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b', revs1[ 0].identifier
301 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[ 1].identifier
302 assert_equal 'bc201c95999c4f10d018b0aa03b541cd6a2ff0ee', revs1[-2].identifier
303 assert_equal '92397af84d22f27389c822848ecd5b463c181583', revs1[-1].identifier
304 end
305
306 def test_revisions_invalid_rev_excludes
307 assert_equal [],
308 @adapter.revisions('', nil, nil,
309 {:reverse => true,
310 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
311 :excludes => ['0123abcd4567']})
312 assert_raise Redmine::Scm::Adapters::CommandFailed do
296 revs1 = []
313 revs1 = []
297 @adapter.revisions('', nil, nil,
314 @adapter.revisions('', nil, nil,
298 {:reverse => true,
315 {:reverse => true,
299 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c',
316 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
300 '92397af84d22f27389c822848ecd5b463c181583'],
317 :excludes => ['0123abcd4567']}) do |rev|
301 :excludes => ['95488a44bc25f7d1f97d775a31359539ff333a63',
302 '4f26664364207fa8b1af9f8722647ab2d4ac5d43'] }) do |rev|
303 revs1 << rev
318 revs1 << rev
304 end
319 end
305 assert_equal 4, revs1.length
306 assert_equal 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b', revs1[ 0].identifier
307 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[ 1].identifier
308 assert_equal 'bc201c95999c4f10d018b0aa03b541cd6a2ff0ee', revs1[-2].identifier
309 assert_equal '92397af84d22f27389c822848ecd5b463c181583', revs1[-1].identifier
310 end
311
312 def test_revisions_invalid_rev_excludes
313 assert_equal [],
314 @adapter.revisions('', nil, nil,
315 {:reverse => true,
316 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
317 :excludes => ['0123abcd4567']})
318 assert_raise Redmine::Scm::Adapters::CommandFailed do
319 revs1 = []
320 @adapter.revisions('', nil, nil,
321 {:reverse => true,
322 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
323 :excludes => ['0123abcd4567']}) do |rev|
324 revs1 << rev
325 end
326 end
327 end
320 end
321 end
328
322
329 def test_getting_revisions_with_spaces_in_filename
323 def test_getting_revisions_with_spaces_in_filename
330 assert_equal 1, @adapter.revisions("filemane with spaces.txt",
324 assert_equal 1, @adapter.revisions("filemane with spaces.txt",
331 nil, "master").length
325 nil, "master").length
332 end
326 end
333
327
334 def test_parents
328 def test_parents
335 revs1 = []
329 revs1 = []
336 @adapter.revisions('',
330 @adapter.revisions('',
337 nil,
331 nil,
338 "master",
332 "master",
339 {:reverse => true}) do |rev|
333 {:reverse => true}) do |rev|
340 revs1 << rev
334 revs1 << rev
341 end
335 end
342 assert_equal 15, revs1.length
336 assert_equal 15, revs1.length
343 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
337 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
344 revs1[0].identifier
338 revs1[0].identifier
345 assert_equal nil, revs1[0].parents
339 assert_equal nil, revs1[0].parents
346 assert_equal "899a15dba03a3b350b89c3f537e4bbe02a03cdc9",
340 assert_equal "899a15dba03a3b350b89c3f537e4bbe02a03cdc9",
347 revs1[1].identifier
341 revs1[1].identifier
348 assert_equal 1, revs1[1].parents.length
342 assert_equal 1, revs1[1].parents.length
349 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
343 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
350 revs1[1].parents[0]
344 revs1[1].parents[0]
351 assert_equal "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
345 assert_equal "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
352 revs1[10].identifier
346 revs1[10].identifier
353 assert_equal 2, revs1[10].parents.length
347 assert_equal 2, revs1[10].parents.length
354 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8",
348 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8",
355 revs1[10].parents[0]
349 revs1[10].parents[0]
356 assert_equal "7e61ac704deecde634b51e59daa8110435dcb3da",
350 assert_equal "7e61ac704deecde634b51e59daa8110435dcb3da",
357 revs1[10].parents[1]
351 revs1[10].parents[1]
358 end
352 end
359
353
360 def test_getting_revisions_with_leading_and_trailing_spaces_in_filename
354 def test_getting_revisions_with_leading_and_trailing_spaces_in_filename
361 assert_equal " filename with a leading space.txt ",
355 assert_equal " filename with a leading space.txt ",
362 @adapter.revisions(" filename with a leading space.txt ",
356 @adapter.revisions(" filename with a leading space.txt ",
363 nil, "master")[0].paths[0][:path]
357 nil, "master")[0].paths[0][:path]
364 end
358 end
365
359
366 def test_getting_entries_with_leading_and_trailing_spaces_in_filename
360 def test_getting_entries_with_leading_and_trailing_spaces_in_filename
367 assert_equal " filename with a leading space.txt ",
361 assert_equal " filename with a leading space.txt ",
368 @adapter.entries('',
362 @adapter.entries('',
369 '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c')[3].name
363 '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c')[3].name
370 end
364 end
371
365
372 def test_annotate
366 def test_annotate
373 annotate = @adapter.annotate('sources/watchers_controller.rb')
367 annotate = @adapter.annotate('sources/watchers_controller.rb')
374 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
368 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
375 assert_equal 41, annotate.lines.size
369 assert_equal 41, annotate.lines.size
376 assert_equal "# This program is free software; you can redistribute it and/or",
370 assert_equal "# This program is free software; you can redistribute it and/or",
377 annotate.lines[4].strip
371 annotate.lines[4].strip
378 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
372 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
379 annotate.revisions[4].identifier
373 annotate.revisions[4].identifier
380 assert_equal "jsmith", annotate.revisions[4].author
374 assert_equal "jsmith", annotate.revisions[4].author
381 end
375 end
382
376
383 def test_annotate_moved_file
377 def test_annotate_moved_file
384 annotate = @adapter.annotate('renamed_test.txt')
378 annotate = @adapter.annotate('renamed_test.txt')
385 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
379 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
386 assert_equal 2, annotate.lines.size
380 assert_equal 2, annotate.lines.size
387 end
381 end
388
382
389 def test_last_rev
383 def test_last_rev
390 last_rev = @adapter.lastrev("README",
384 last_rev = @adapter.lastrev("README",
391 "4f26664364207fa8b1af9f8722647ab2d4ac5d43")
385 "4f26664364207fa8b1af9f8722647ab2d4ac5d43")
392 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.scmid
386 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.scmid
393 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.identifier
387 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.identifier
394 assert_equal "Adam Soltys <asoltys@gmail.com>", last_rev.author
388 assert_equal "Adam Soltys <asoltys@gmail.com>", last_rev.author
395 assert_equal Time.gm(2009, 6, 24, 5, 27, 38), last_rev.time
389 assert_equal Time.gm(2009, 6, 24, 5, 27, 38), last_rev.time
396 end
390 end
397
391
398 def test_last_rev_with_spaces_in_filename
392 def test_last_rev_with_spaces_in_filename
399 last_rev = @adapter.lastrev("filemane with spaces.txt",
393 last_rev = @adapter.lastrev("filemane with spaces.txt",
400 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b")
394 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b")
401 last_rev_author = last_rev.author
395 last_rev_author = last_rev.author
402 assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.scmid
396 assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.scmid
403 assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.identifier
397 assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.identifier
404 assert_equal "#{@str_felix_hex} <felix@fachschaften.org>",
398 assert_equal "#{@str_felix_hex} <felix@fachschaften.org>",
405 last_rev.author
399 last_rev.author
406 assert_equal Time.gm(2010, 9, 18, 19, 59, 46), last_rev.time
400 assert_equal Time.gm(2010, 9, 18, 19, 59, 46), last_rev.time
407 end
401 end
408
402
409 def test_latin_1_path
403 def test_latin_1_path
410 if WINDOWS_PASS
404 if WINDOWS_PASS
411 puts WINDOWS_SKIP_STR
405 puts WINDOWS_SKIP_STR
412 elsif JRUBY_SKIP
406 elsif JRUBY_SKIP
413 puts JRUBY_SKIP_STR
407 puts JRUBY_SKIP_STR
414 else
408 else
415 p2 = "latin-1-dir/test-#{@char_1}-2.txt"
409 p2 = "latin-1-dir/test-#{@char_1}-2.txt"
416 ['4fc55c43bf3d3dc2efb66145365ddc17639ce81e', '4fc55c43bf3'].each do |r1|
410 ['4fc55c43bf3d3dc2efb66145365ddc17639ce81e', '4fc55c43bf3'].each do |r1|
417 assert @adapter.diff(p2, r1)
411 assert @adapter.diff(p2, r1)
418 assert @adapter.cat(p2, r1)
412 assert @adapter.cat(p2, r1)
419 assert_equal 1, @adapter.annotate(p2, r1).lines.length
413 assert_equal 1, @adapter.annotate(p2, r1).lines.length
420 ['64f1f3e89ad1cb57976ff0ad99a107012ba3481d', '64f1f3e89ad1cb5797'].each do |r2|
414 ['64f1f3e89ad1cb57976ff0ad99a107012ba3481d', '64f1f3e89ad1cb5797'].each do |r2|
421 assert @adapter.diff(p2, r1, r2)
415 assert @adapter.diff(p2, r1, r2)
422 end
423 end
416 end
424 end
417 end
425 end
418 end
419 end
426
420
427 def test_latin_1_user_annotate
421 def test_latin_1_user_annotate
428 ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', '83ca5fd546063a'].each do |r1|
422 ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', '83ca5fd546063a'].each do |r1|
429 annotate = @adapter.annotate(" filename with a leading space.txt ", r1)
423 annotate = @adapter.annotate(" filename with a leading space.txt ", r1)
430 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
424 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
431 assert_equal 1, annotate.lines.size
425 assert_equal 1, annotate.lines.size
432 assert_equal "And this is a file with a leading and trailing space...",
426 assert_equal "And this is a file with a leading and trailing space...",
433 annotate.lines[0].strip
427 annotate.lines[0].strip
434 assert_equal "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
428 assert_equal "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
435 annotate.revisions[0].identifier
429 annotate.revisions[0].identifier
436 assert_equal @str_felix_hex, annotate.revisions[0].author
430 assert_equal @str_felix_hex, annotate.revisions[0].author
437 end
438 end
431 end
432 end
439
433
440 def test_entries_tag
434 def test_entries_tag
441 entries1 = @adapter.entries(nil, 'tag01.annotated',
435 entries1 = @adapter.entries(nil, 'tag01.annotated',
442 options = {:report_last_commit => true})
436 options = {:report_last_commit => true})
443 assert entries1
437 assert entries1
444 assert_equal 3, entries1.size
438 assert_equal 3, entries1.size
445 assert_equal 'sources', entries1[1].name
439 assert_equal 'sources', entries1[1].name
446 assert_equal 'sources', entries1[1].path
440 assert_equal 'sources', entries1[1].path
447 assert_equal 'dir', entries1[1].kind
441 assert_equal 'dir', entries1[1].kind
448 readme = entries1[2]
442 readme = entries1[2]
449 assert_equal 'README', readme.name
443 assert_equal 'README', readme.name
450 assert_equal 'README', readme.path
444 assert_equal 'README', readme.path
451 assert_equal 'file', readme.kind
445 assert_equal 'file', readme.kind
452 assert_equal 27, readme.size
446 assert_equal 27, readme.size
453 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', readme.lastrev.identifier
447 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', readme.lastrev.identifier
454 assert_equal Time.gm(2007, 12, 14, 9, 24, 1), readme.lastrev.time
448 assert_equal Time.gm(2007, 12, 14, 9, 24, 1), readme.lastrev.time
455 end
449 end
456
450
457 def test_entries_branch
451 def test_entries_branch
458 entries1 = @adapter.entries(nil, 'test_branch',
452 entries1 = @adapter.entries(nil, 'test_branch',
459 options = {:report_last_commit => true})
453 options = {:report_last_commit => true})
460 assert entries1
454 assert entries1
461 assert_equal 4, entries1.size
455 assert_equal 4, entries1.size
462 assert_equal 'sources', entries1[1].name
456 assert_equal 'sources', entries1[1].name
463 assert_equal 'sources', entries1[1].path
457 assert_equal 'sources', entries1[1].path
464 assert_equal 'dir', entries1[1].kind
458 assert_equal 'dir', entries1[1].kind
465 readme = entries1[2]
459 readme = entries1[2]
466 assert_equal 'README', readme.name
460 assert_equal 'README', readme.name
467 assert_equal 'README', readme.path
461 assert_equal 'README', readme.path
468 assert_equal 'file', readme.kind
462 assert_equal 'file', readme.kind
469 assert_equal 159, readme.size
463 assert_equal 159, readme.size
470 assert_equal '713f4944648826f558cf548222f813dabe7cbb04', readme.lastrev.identifier
464 assert_equal '713f4944648826f558cf548222f813dabe7cbb04', readme.lastrev.identifier
471 assert_equal Time.gm(2009, 6, 19, 4, 37, 23), readme.lastrev.time
465 assert_equal Time.gm(2009, 6, 19, 4, 37, 23), readme.lastrev.time
472 end
466 end
473
467
474 def test_entries_wrong_path_encoding
468 def test_entries_wrong_path_encoding
475 adpt = Redmine::Scm::Adapters::GitAdapter.new(
469 adpt = Redmine::Scm::Adapters::GitAdapter.new(
476 REPOSITORY_PATH,
470 REPOSITORY_PATH,
477 nil,
471 nil,
478 nil,
472 nil,
479 nil,
473 nil,
480 'EUC-JP'
474 'EUC-JP'
481 )
475 )
482 entries1 = adpt.entries('latin-1-dir', '64f1f3e8')
476 entries1 = adpt.entries('latin-1-dir', '64f1f3e8')
483 assert entries1
477 assert entries1
484 assert_equal 3, entries1.size
478 assert_equal 3, entries1.size
485 f1 = entries1[1]
479 f1 = entries1[1]
486 assert_equal nil, f1.name
480 assert_equal nil, f1.name
487 assert_equal nil, f1.path
481 assert_equal nil, f1.path
488 assert_equal 'file', f1.kind
482 assert_equal 'file', f1.kind
489 end
483 end
484
485 def test_entries_latin_1_files
486 entries1 = @adapter.entries('latin-1-dir', '64f1f3e8')
487 assert entries1
488 assert_equal 3, entries1.size
489 f1 = entries1[1]
490 assert_equal "test-#{@char_1}-2.txt", f1.name
491 assert_equal "latin-1-dir/test-#{@char_1}-2.txt", f1.path
492 assert_equal 'file', f1.kind
493 end
490
494
491 def test_entries_latin_1_files
495 def test_entries_latin_1_dir
492 entries1 = @adapter.entries('latin-1-dir', '64f1f3e8')
496 if WINDOWS_PASS
497 puts WINDOWS_SKIP_STR
498 elsif JRUBY_SKIP
499 puts JRUBY_SKIP_STR
500 else
501 entries1 = @adapter.entries("latin-1-dir/test-#{@char_1}-subdir",
502 '1ca7f5ed')
493 assert entries1
503 assert entries1
494 assert_equal 3, entries1.size
504 assert_equal 3, entries1.size
495 f1 = entries1[1]
505 f1 = entries1[1]
496 assert_equal "test-#{@char_1}-2.txt", f1.name
506 assert_equal "test-#{@char_1}-2.txt", f1.name
497 assert_equal "latin-1-dir/test-#{@char_1}-2.txt", f1.path
507 assert_equal "latin-1-dir/test-#{@char_1}-subdir/test-#{@char_1}-2.txt", f1.path
498 assert_equal 'file', f1.kind
508 assert_equal 'file', f1.kind
499 end
509 end
510 end
500
511
501 def test_entries_latin_1_dir
512 def test_entry
502 if WINDOWS_PASS
513 entry = @adapter.entry()
503 puts WINDOWS_SKIP_STR
514 assert_equal "", entry.path
504 elsif JRUBY_SKIP
515 assert_equal "dir", entry.kind
505 puts JRUBY_SKIP_STR
516 entry = @adapter.entry('')
506 else
517 assert_equal "", entry.path
507 entries1 = @adapter.entries("latin-1-dir/test-#{@char_1}-subdir",
518 assert_equal "dir", entry.kind
508 '1ca7f5ed')
519 assert_nil @adapter.entry('invalid')
509 assert entries1
520 assert_nil @adapter.entry('/invalid')
510 assert_equal 3, entries1.size
521 assert_nil @adapter.entry('/invalid/')
511 f1 = entries1[1]
522 assert_nil @adapter.entry('invalid/invalid')
512 assert_equal "test-#{@char_1}-2.txt", f1.name
523 assert_nil @adapter.entry('invalid/invalid/')
513 assert_equal "latin-1-dir/test-#{@char_1}-subdir/test-#{@char_1}-2.txt", f1.path
524 assert_nil @adapter.entry('/invalid/invalid')
514 assert_equal 'file', f1.kind
525 assert_nil @adapter.entry('/invalid/invalid/')
515 end
526 ["README", "/README"].each do |path|
516 end
527 entry = @adapter.entry(path, '7234cb2750b63f')
517
528 assert_equal "README", entry.path
518 def test_entry
529 assert_equal "file", entry.kind
519 entry = @adapter.entry()
530 end
520 assert_equal "", entry.path
531 ["sources", "/sources", "/sources/"].each do |path|
532 entry = @adapter.entry(path, '7234cb2750b63f')
533 assert_equal "sources", entry.path
521 assert_equal "dir", entry.kind
534 assert_equal "dir", entry.kind
522 entry = @adapter.entry('')
523 assert_equal "", entry.path
524 assert_equal "dir", entry.kind
525 assert_nil @adapter.entry('invalid')
526 assert_nil @adapter.entry('/invalid')
527 assert_nil @adapter.entry('/invalid/')
528 assert_nil @adapter.entry('invalid/invalid')
529 assert_nil @adapter.entry('invalid/invalid/')
530 assert_nil @adapter.entry('/invalid/invalid')
531 assert_nil @adapter.entry('/invalid/invalid/')
532 ["README", "/README"].each do |path|
533 entry = @adapter.entry(path, '7234cb2750b63f')
534 assert_equal "README", entry.path
535 assert_equal "file", entry.kind
536 end
537 ["sources", "/sources", "/sources/"].each do |path|
538 entry = @adapter.entry(path, '7234cb2750b63f')
539 assert_equal "sources", entry.path
540 assert_equal "dir", entry.kind
541 end
542 ["sources/watchers_controller.rb", "/sources/watchers_controller.rb"].each do |path|
543 entry = @adapter.entry(path, '7234cb2750b63f')
544 assert_equal "sources/watchers_controller.rb", entry.path
545 assert_equal "file", entry.kind
546 end
547 end
535 end
548
536 ["sources/watchers_controller.rb", "/sources/watchers_controller.rb"].each do |path|
549 def test_path_encoding_default_utf8
537 entry = @adapter.entry(path, '7234cb2750b63f')
550 adpt1 = Redmine::Scm::Adapters::GitAdapter.new(
538 assert_equal "sources/watchers_controller.rb", entry.path
551 REPOSITORY_PATH
539 assert_equal "file", entry.kind
552 )
553 assert_equal "UTF-8", adpt1.path_encoding
554 adpt2 = Redmine::Scm::Adapters::GitAdapter.new(
555 REPOSITORY_PATH,
556 nil,
557 nil,
558 nil,
559 ""
560 )
561 assert_equal "UTF-8", adpt2.path_encoding
562 end
540 end
541 end
563
542
564 def test_cat_path_invalid
543 def test_path_encoding_default_utf8
565 assert_nil @adapter.cat('invalid')
544 adpt1 = Redmine::Scm::Adapters::GitAdapter.new(
566 end
545 REPOSITORY_PATH
546 )
547 assert_equal "UTF-8", adpt1.path_encoding
548 adpt2 = Redmine::Scm::Adapters::GitAdapter.new(
549 REPOSITORY_PATH,
550 nil,
551 nil,
552 nil,
553 ""
554 )
555 assert_equal "UTF-8", adpt2.path_encoding
556 end
567
557
568 def test_cat_revision_invalid
558 def test_cat_path_invalid
569 assert @adapter.cat('README')
559 assert_nil @adapter.cat('invalid')
570 assert_nil @adapter.cat('README', '1234abcd5678')
560 end
571 end
572
561
573 def test_diff_path_invalid
562 def test_cat_revision_invalid
574 assert_equal [], @adapter.diff('invalid', '713f4944648826f5')
563 assert @adapter.cat('README')
575 end
564 assert_nil @adapter.cat('README', '1234abcd5678')
565 end
576
566
577 def test_diff_revision_invalid
567 def test_diff_path_invalid
578 assert_nil @adapter.diff(nil, '1234abcd5678')
568 assert_equal [], @adapter.diff('invalid', '713f4944648826f5')
579 assert_nil @adapter.diff(nil, '713f4944648826f5', '1234abcd5678')
569 end
580 assert_nil @adapter.diff(nil, '1234abcd5678', '713f4944648826f5')
581 end
582
570
583 def test_annotate_path_invalid
571 def test_diff_revision_invalid
584 assert_nil @adapter.annotate('invalid')
572 assert_nil @adapter.diff(nil, '1234abcd5678')
585 end
573 assert_nil @adapter.diff(nil, '713f4944648826f5', '1234abcd5678')
574 assert_nil @adapter.diff(nil, '1234abcd5678', '713f4944648826f5')
575 end
586
576
587 def test_annotate_revision_invalid
577 def test_annotate_path_invalid
588 assert @adapter.annotate('README')
578 assert_nil @adapter.annotate('invalid')
589 assert_nil @adapter.annotate('README', '1234abcd5678')
579 end
590 end
591
580
592 private
581 def test_annotate_revision_invalid
582 assert @adapter.annotate('README')
583 assert_nil @adapter.annotate('README', '1234abcd5678')
584 end
593
585
594 def test_scm_version_for(scm_command_version, version)
586 private
595 @adapter.class.expects(:scm_version_from_command_line).returns(scm_command_version)
596 assert_equal version, @adapter.class.scm_command_version
597 end
598
587
599 else
588 def test_scm_version_for(scm_command_version, version)
600 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
589 @adapter.class.expects(:scm_version_from_command_line).returns(scm_command_version)
601 def test_fake; assert true end
590 assert_equal version, @adapter.class.scm_command_version
602 end
591 end
603 end
604
592
605 rescue LoadError
593 else
606 class GitMochaFake < ActiveSupport::TestCase
594 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
607 def test_fake; assert(false, "Requires mocha to run those tests") end
595 def test_fake; assert true end
608 end
596 end
609 end
597 end
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (1098 lines changed) Show them Hide them
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now