##// 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 3 # You can also run tests on your environment.
4 4 language: ruby
5 5 rvm:
6 - 1.8.7
7 - 1.9.2
8 6 - 1.9.3
9 7 - 2.0
10 8 - 2.1
@@ -34,6 +32,7 script:
34 32 - "bundle install"
35 33 - "RUN_ON_NOT_OFFICIAL='' RUBY_VER=1.9 BRANCH=trunk bundle exec rake config/database.yml"
36 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 37 notifications:
39 38 email: false
@@ -1,12 +1,17
1 1 source 'https://rubygems.org'
2 2
3 gem "rails", "3.2.19"
3 gem "rails", "4.1.6"
4 4 gem "jquery-rails", "~> 3.1.1"
5 5 gem "coderay", "~> 1.1.0"
6 gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby]
7 6 gem "builder", ">= 3.0.4"
8 7 gem "request_store", "1.0.5"
9 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 15 gem "rbpdf", "~> 1.18.1"
11 16
12 17 # Optional gem for LDAP authentication
@@ -23,16 +28,12 end
23 28 platforms :mri, :mingw do
24 29 # Optional gem for exporting the gantt to a PNG file, not supported with jruby
25 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 31 gem "rmagick", ">= 2.0.0"
30 32 end
31 33
32 34 # Optional Markdown support, not for JRuby
33 35 group :markdown do
34 # TODO: upgrade to redcarpet 3.x when ruby1.8 support is dropped
35 gem "redcarpet", "~> 2.3.0"
36 gem "redcarpet", "~> 3.1.2"
36 37 end
37 38 end
38 39
@@ -57,7 +58,6 if File.exist?(database_file)
57 58 gem "mysql2", "~> 0.3.11", :platforms => [:mri, :mingw]
58 59 gem "activerecord-jdbcmysql-adapter", :platforms => :jruby
59 60 when 'mysql'
60 gem "mysql", "~> 2.8.1", :platforms => [:mri, :mingw]
61 61 gem "activerecord-jdbcmysql-adapter", :platforms => :jruby
62 62 when /postgresql/
63 63 gem "pg", ">= 0.11.0", :platforms => [:mri, :mingw]
@@ -85,24 +85,20 group :development do
85 85 end
86 86
87 87 group :test do
88 gem "shoulda", "~> 3.3.2"
89 gem "shoulda-matchers", "1.4.1"
88 gem "minitest"
89 gem "shoulda-context"
90 90 gem "mocha", "~> 1.0.0", :require => 'mocha/api'
91 if RUBY_VERSION >= '1.9.3'
92 gem "capybara", "~> 2.1.0"
93 gem "selenium-webdriver"
94 end
91 # For running UI tests
92 gem "capybara", "~> 2.1.0"
93 gem "selenium-webdriver"
95 94 end
96 95
97 96 local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
98 97 if File.exists?(local_gemfile)
99 puts "Loading Gemfile.local ..." if $DEBUG # `ruby -d` or `bundle -v`
100 instance_eval File.read(local_gemfile)
98 eval_gemfile local_gemfile
101 99 end
102 100
103 101 # Load plugins' Gemfiles
104 102 Dir.glob File.expand_path("../plugins/*/{Gemfile,PluginGemfile}", __FILE__) do |file|
105 puts "Loading #{file} ..." if $DEBUG # `ruby -d` or `bundle -v`
106 #TODO: switch to "eval_gemfile file" when bundler >= 1.2.0 will be required (rails 4)
107 instance_eval File.read(file), file
103 eval_gemfile file
108 104 end
@@ -34,7 +34,7 class AdminController < ApplicationController
34 34
35 35 scope = Project.status(@status).order('lft')
36 36 scope = scope.like(params[:name]) if params[:name].present?
37 @projects = scope.all
37 @projects = scope.to_a
38 38
39 39 render :action => "projects", :layout => false if request.xhr?
40 40 end
@@ -496,7 +496,7 class ApplicationController < ActionController::Base
496 496 end
497 497
498 498 def render_feed(items, options={})
499 @items = items || []
499 @items = (items || []).to_a
500 500 @items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
501 501 @items = @items.slice(0, Setting.feeds_limit.to_i)
502 502 @title = options[:title] || Setting.app_title
@@ -26,7 +26,7 class AutoCompletesController < ApplicationController
26 26 if q.match(/\A#?(\d+)\z/)
27 27 @issues << scope.find_by_id($1.to_i)
28 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 30 @issues.compact!
31 31 end
32 32 render :layout => false
@@ -25,7 +25,7 class BoardsController < ApplicationController
25 25 helper :watchers
26 26
27 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 29 # show the board if there is only one
30 30 if @boards.size == 1
31 31 @board = @boards.first
@@ -45,12 +45,13 class BoardsController < ApplicationController
45 45 @topic_pages = Paginator.new @topic_count, per_page_option, params['page']
46 46 @topics = @board.topics.
47 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 50 limit(@topic_pages.per_page).
50 51 offset(@topic_pages.offset).
51 52 order(sort_clause).
52 53 preload(:author, {:last_reply => :author}).
53 all
54 to_a
54 55 @message = Message.new(:board => @board)
55 56 render :action => 'show', :layout => !request.xhr?
56 57 }
@@ -59,7 +60,7 class BoardsController < ApplicationController
59 60 reorder('created_on DESC').
60 61 includes(:author, :board).
61 62 limit(Setting.feeds_limit.to_i).
62 all
63 to_a
63 64 render_feed(@messages, :title => "#{@project}: #{@board}")
64 65 }
65 66 end
@@ -27,7 +27,7 class DocumentsController < ApplicationController
27 27
28 28 def index
29 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 31 case @sort_by
32 32 when 'date'
33 33 @grouped = documents.group_by {|d| d.updated_on.to_date }
@@ -43,7 +43,7 class DocumentsController < ApplicationController
43 43 end
44 44
45 45 def show
46 @attachments = @document.attachments.all
46 @attachments = @document.attachments.to_a
47 47 end
48 48
49 49 def new
@@ -69,7 +69,7 class DocumentsController < ApplicationController
69 69
70 70 def update
71 71 @document.safe_attributes = params[:document]
72 if request.put? and @document.save
72 if @document.save
73 73 flash[:notice] = l(:notice_successful_update)
74 74 redirect_to document_path(@document)
75 75 else
@@ -32,7 +32,7 class EnumerationsController < ApplicationController
32 32 format.api {
33 33 @klass = Enumeration.get_subclass(params[:type])
34 34 if @klass
35 @enumerations = @klass.shared.sorted.all
35 @enumerations = @klass.shared.sorted.to_a
36 36 else
37 37 render_404
38 38 end
@@ -56,7 +56,7 class EnumerationsController < ApplicationController
56 56 end
57 57
58 58 def update
59 if request.put? && @enumeration.update_attributes(params[:enumeration])
59 if @enumeration.update_attributes(params[:enumeration])
60 60 flash[:notice] = l(:notice_successful_update)
61 61 redirect_to enumerations_path
62 62 else
@@ -75,7 +75,7 class EnumerationsController < ApplicationController
75 75 redirect_to enumerations_path
76 76 return
77 77 end
78 @enumerations = @enumeration.class.system.all - [@enumeration]
78 @enumerations = @enumeration.class.system.to_a - [@enumeration]
79 79 end
80 80
81 81 private
@@ -31,8 +31,10 class FilesController < ApplicationController
31 31 'size' => "#{Attachment.table_name}.filesize",
32 32 'downloads' => "#{Attachment.table_name}.downloads"
33 33
34 @containers = [ Project.includes(:attachments).reorder(sort_clause).find(@project.id)]
35 @containers += @project.versions.includes(:attachments).reorder(sort_clause).all.sort.reverse
34 @containers = [Project.includes(:attachments).
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 38 render :layout => !request.xhr?
37 39 end
38 40
@@ -27,13 +27,13 class GroupsController < ApplicationController
27 27 def index
28 28 respond_to do |format|
29 29 format.html {
30 @groups = Group.sorted.all
30 @groups = Group.sorted.to_a
31 31 @user_count_by_group_id = user_count_by_group_id
32 32 }
33 33 format.api {
34 34 scope = Group.sorted
35 35 scope = scope.givable unless params[:builtin] == '1'
36 @groups = scope.all
36 @groups = scope.to_a
37 37 }
38 38 end
39 39 end
@@ -95,7 +95,7 class GroupsController < ApplicationController
95 95 end
96 96
97 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 99 @group.users << @users if request.post?
100 100 respond_to do |format|
101 101 format.html { redirect_to edit_group_path(@group, :tab => 'users') }
@@ -27,7 +27,7 class IssueCategoriesController < ApplicationController
27 27 def index
28 28 respond_to do |format|
29 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 31 end
32 32 end
33 33
@@ -29,7 +29,7 class IssueStatusesController < ApplicationController
29 29 render :action => "index", :layout => false if request.xhr?
30 30 }
31 31 format.api {
32 @issue_statuses = IssueStatus.order('position').all
32 @issue_statuses = IssueStatus.order('position').to_a
33 33 }
34 34 end
35 35 end
@@ -104,15 +104,16 class IssuesController < ApplicationController
104 104 end
105 105
106 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 110 @journals.each_with_index {|j,i| j.indice = i+1}
109 111 @journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
110 112 Journal.preload_journals_details_custom_fields(@journals)
111 # TODO: use #select! when ruby1.8 support is dropped
112 @journals.reject! {|journal| !journal.notes? && journal.visible_details.empty?}
113 @journals.select! {|journal| journal.notes? || journal.visible_details.any?}
113 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 117 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
117 118
118 119 @relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
@@ -189,7 +190,7 class IssuesController < ApplicationController
189 190 rescue ActiveRecord::StaleObjectError
190 191 @conflict = true
191 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 194 @conflict_journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
194 195 end
195 196 end
@@ -301,7 +302,7 class IssuesController < ApplicationController
301 302 else
302 303 @saved_issues = @issues
303 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 306 bulk_edit
306 307 render :action => 'bulk_edit'
307 308 end
@@ -375,7 +376,9 class IssuesController < ApplicationController
375 376 def update_issue_from_params
376 377 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
377 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 383 @issue.init_journal(User.current)
381 384
@@ -422,7 +425,9 class IssuesController < ApplicationController
422 425 @issue.project = @project
423 426 @issue.author ||= User.current
424 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 431 if @issue.tracker.nil?
427 432 render_error l(:error_no_tracker_in_project)
428 433 return false
@@ -34,7 +34,6 class JournalsController < ApplicationController
34 34 retrieve_query
35 35 sort_init 'id', 'desc'
36 36 sort_update(@query.sortable_columns)
37
38 37 if @query.valid?
39 38 @journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC",
40 39 :limit => 25)
@@ -32,7 +32,7 class MembersController < ApplicationController
32 32 order("#{Member.table_name}.id").
33 33 limit(@limit).
34 34 offset(@offset).
35 all
35 to_a
36 36 respond_to do |format|
37 37 format.html { head 406 }
38 38 format.api
@@ -63,7 +63,10 class MembersController < ApplicationController
63 63
64 64 respond_to do |format|
65 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 70 format.api {
68 71 @member = members.first
69 72 if @member.valid?
@@ -43,10 +43,10 class MessagesController < ApplicationController
43 43 @reply_pages = Paginator.new @reply_count, REPLIES_PER_PAGE, page
44 44 @replies = @topic.children.
45 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 47 limit(@reply_pages.per_page).
48 48 offset(@reply_pages.offset).
49 all
49 to_a
50 50
51 51 @reply = Message.new(:subject => "RE: #{@message.subject}")
52 52 render :action => "show", :layout => false if request.xhr?
@@ -53,8 +53,8 class MyController < ApplicationController
53 53 @user = User.current
54 54 @pref = @user.pref
55 55 if request.post?
56 @user.safe_attributes = params[:user]
57 @user.pref.attributes = params[:pref]
56 @user.safe_attributes = params[:user] if params[:user]
57 @user.pref.attributes = params[:pref] if params[:pref]
58 58 if @user.save
59 59 @user.pref.save
60 60 set_language_if_valid @user.language
@@ -46,7 +46,7 class NewsController < ApplicationController
46 46 order("#{News.table_name}.created_on DESC").
47 47 limit(@limit).
48 48 offset(@offset).
49 all
49 to_a
50 50 respond_to do |format|
51 51 format.html {
52 52 @news = News.new # for adding news inline
@@ -20,7 +20,7 class ProjectEnumerationsController < ApplicationController
20 20 before_filter :authorize
21 21
22 22 def update
23 if request.put? && params[:enumerations]
23 if params[:enumerations]
24 24 Project.transaction do
25 25 params[:enumerations].each do |id, activity|
26 26 @project.update_or_create_time_entry_activity(id, activity)
@@ -53,30 +53,30 class ProjectsController < ApplicationController
53 53 unless params[:closed]
54 54 scope = scope.active
55 55 end
56 @projects = scope.visible.order('lft').all
56 @projects = scope.visible.order('lft').to_a
57 57 }
58 58 format.api {
59 59 @offset, @limit = api_offset_and_limit
60 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 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 65 render_feed(projects, :title => "#{Setting.app_title}: #{l(:label_project_latest)}")
66 66 }
67 67 end
68 68 end
69 69
70 70 def new
71 @issue_custom_fields = IssueCustomField.sorted.all
72 @trackers = Tracker.sorted.all
71 @issue_custom_fields = IssueCustomField.sorted.to_a
72 @trackers = Tracker.sorted.to_a
73 73 @project = Project.new
74 74 @project.safe_attributes = params[:project]
75 75 end
76 76
77 77 def create
78 @issue_custom_fields = IssueCustomField.sorted.all
79 @trackers = Tracker.sorted.all
78 @issue_custom_fields = IssueCustomField.sorted.to_a
79 @trackers = Tracker.sorted.to_a
80 80 @project = Project.new
81 81 @project.safe_attributes = params[:project]
82 82
@@ -109,8 +109,8 class ProjectsController < ApplicationController
109 109 end
110 110
111 111 def copy
112 @issue_custom_fields = IssueCustomField.sorted.all
113 @trackers = Tracker.sorted.all
112 @issue_custom_fields = IssueCustomField.sorted.to_a
113 @trackers = Tracker.sorted.to_a
114 114 @source_project = Project.find(params[:id])
115 115 if request.get?
116 116 @project = Project.copy_from(@source_project)
@@ -145,8 +145,8 class ProjectsController < ApplicationController
145 145 end
146 146
147 147 @users_by_role = @project.users_by_role
148 @subprojects = @project.children.visible.all
149 @news = @project.news.limit(5).includes(:author, :project).reorder("#{News.table_name}.created_on DESC").all
148 @subprojects = @project.children.visible.to_a
149 @news = @project.news.limit(5).includes(:author, :project).reorder("#{News.table_name}.created_on DESC").to_a
150 150 @trackers = @project.rolled_up_trackers
151 151
152 152 cond = @project.project_condition(Setting.display_subprojects_issues?)
@@ -167,10 +167,10 class ProjectsController < ApplicationController
167 167 end
168 168
169 169 def settings
170 @issue_custom_fields = IssueCustomField.sorted.all
170 @issue_custom_fields = IssueCustomField.sorted.to_a
171 171 @issue_category ||= IssueCategory.new
172 172 @member ||= @project.members.new
173 @trackers = Tracker.sorted.all
173 @trackers = Tracker.sorted.to_a
174 174 @wiki ||= @project.wiki
175 175 end
176 176
@@ -37,8 +37,9 class QueriesController < ApplicationController
37 37 order("#{Query.table_name}.name").
38 38 limit(@limit).
39 39 offset(@offset).
40 all
40 to_a
41 41 respond_to do |format|
42 format.html {render_error :status => 406}
42 43 format.api
43 44 end
44 45 end
@@ -90,6 +90,6 class ReportsController < ApplicationController
90 90 private
91 91
92 92 def find_issue_statuses
93 @statuses = IssueStatus.sorted.all
93 @statuses = IssueStatus.sorted.to_a
94 94 end
95 95 end
@@ -69,7 +69,7 class RepositoriesController < ApplicationController
69 69 @repository.merge_extra_info(attrs[:attrs_extra])
70 70 end
71 71 @repository.project = @project
72 if request.put? && @repository.save
72 if @repository.save
73 73 redirect_to settings_project_path(@project, :tab => 'repositories')
74 74 else
75 75 render :action => 'edit'
@@ -94,7 +94,7 class RepositoriesController < ApplicationController
94 94 @committers = @repository.committers
95 95 @users = @project.users
96 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 98 @users.compact!
99 99 @users.sort!
100 100 if request.post? && params[:committers].is_a?(Hash)
@@ -145,7 +145,7 class RepositoriesController < ApplicationController
145 145 limit(@changeset_pages.per_page).
146 146 offset(@changeset_pages.offset).
147 147 includes(:user, :repository, :parents).
148 all
148 to_a
149 149
150 150 respond_to do |format|
151 151 format.html { render :layout => false if request.xhr? }
@@ -30,7 +30,7 class RolesController < ApplicationController
30 30 render :action => "index", :layout => false if request.xhr?
31 31 }
32 32 format.api {
33 @roles = Role.givable.all
33 @roles = Role.givable.to_a
34 34 }
35 35 end
36 36 end
@@ -47,7 +47,7 class RolesController < ApplicationController
47 47 if params[:copy].present? && @copy_from = Role.find_by_id(params[:copy])
48 48 @role.copy_from(@copy_from)
49 49 end
50 @roles = Role.sorted.all
50 @roles = Role.sorted.to_a
51 51 end
52 52
53 53 def create
@@ -60,7 +60,7 class RolesController < ApplicationController
60 60 flash[:notice] = l(:notice_successful_create)
61 61 redirect_to roles_path
62 62 else
63 @roles = Role.sorted.all
63 @roles = Role.sorted.to_a
64 64 render :action => 'new'
65 65 end
66 66 end
@@ -69,7 +69,7 class RolesController < ApplicationController
69 69 end
70 70
71 71 def update
72 if request.put? and @role.update_attributes(params[:role])
72 if @role.update_attributes(params[:role])
73 73 flash[:notice] = l(:notice_successful_update)
74 74 redirect_to roles_path
75 75 else
@@ -86,7 +86,7 class RolesController < ApplicationController
86 86 end
87 87
88 88 def permissions
89 @roles = Role.sorted.all
89 @roles = Role.sorted.to_a
90 90 @permissions = Redmine::AccessControl.permissions.select { |p| !p.public? }
91 91 if request.post?
92 92 @roles.each do |role|
@@ -31,7 +31,7 class SearchController < ApplicationController
31 31 when 'my_projects'
32 32 User.current.memberships.collect(&:project)
33 33 when 'subprojects'
34 @project ? (@project.self_and_descendants.active.all) : nil
34 @project ? (@project.self_and_descendants.active.to_a) : nil
35 35 else
36 36 @project
37 37 end
@@ -19,7 +19,8 class SysController < ActionController::Base
19 19 before_filter :check_enabled
20 20
21 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 24 # extra_info attribute from repository breaks activeresource client
24 25 render :xml => p.to_xml(
25 26 :only => [:id, :identifier, :name, :is_public, :status],
@@ -56,7 +57,7 class SysController < ActionController::Base
56 57 raise ActiveRecord::RecordNotFound unless project
57 58 projects << project
58 59 else
59 projects = scope.all
60 projects = scope.to_a
60 61 end
61 62 projects.each do |project|
62 63 project.repositories.each do |repository|
@@ -52,7 +52,7 class TimelogController < ApplicationController
52 52 format.html {
53 53 @entry_count = scope.count
54 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 56 @total_hours = scope.sum(:hours).to_f
57 57
58 58 render :layout => !request.xhr?
@@ -60,15 +60,15 class TimelogController < ApplicationController
60 60 format.api {
61 61 @entry_count = scope.count
62 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 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 67 render_feed(entries, :title => l(:label_spent_time))
68 68 }
69 69 format.csv {
70 70 # Export all entries
71 @entries = scope.all
71 @entries = scope.to_a
72 72 send_data(query_to_csv(@entries, @query, params), :type => 'text/csv; header=present', :filename => 'timelog.csv')
73 73 }
74 74 end
@@ -232,7 +232,7 private
232 232 end
233 233
234 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 236 raise ActiveRecord::RecordNotFound if @time_entries.empty?
237 237 @projects = @time_entries.collect(&:project).compact.uniq
238 238 @project = @projects.first if @projects.size == 1
@@ -29,14 +29,14 class TrackersController < ApplicationController
29 29 render :action => "index", :layout => false if request.xhr?
30 30 }
31 31 format.api {
32 @trackers = Tracker.sorted.all
32 @trackers = Tracker.sorted.to_a
33 33 }
34 34 end
35 35 end
36 36
37 37 def new
38 38 @tracker ||= Tracker.new(params[:tracker])
39 @trackers = Tracker.sorted.all
39 @trackers = Tracker.sorted.to_a
40 40 @projects = Project.all
41 41 end
42 42
@@ -95,7 +95,7 class TrackersController < ApplicationController
95 95 redirect_to fields_trackers_path
96 96 return
97 97 end
98 @trackers = Tracker.sorted.all
98 @trackers = Tracker.sorted.to_a
99 99 @custom_fields = IssueCustomField.all.sort
100 100 end
101 101 end
@@ -47,7 +47,7 class UsersController < ApplicationController
47 47 @user_count = scope.count
48 48 @user_pages = Paginator.new @user_count, @limit, params['page']
49 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 52 respond_to do |format|
53 53 format.html {
@@ -60,7 +60,7 class UsersController < ApplicationController
60 60
61 61 def show
62 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 65 events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
66 66 @events_by_day = events.group_by(&:event_date)
@@ -90,7 +90,7 class UsersController < ApplicationController
90 90 @user.admin = params[:user][:admin] || false
91 91 @user.login = params[:user][:login]
92 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 95 if @user.save
96 96 Mailer.account_information(@user, @user.password).deliver if params[:send_information]
@@ -134,7 +134,7 class UsersController < ApplicationController
134 134 # Was the account actived ? (do it before User#save clears the change)
135 135 was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
136 136 # TODO: Similar to My#account
137 @user.pref.attributes = params[:pref]
137 @user.pref.attributes = params[:pref] if params[:pref]
138 138
139 139 if @user.save
140 140 @user.pref.save
@@ -31,7 +31,7 class VersionsController < ApplicationController
31 31 def index
32 32 respond_to do |format|
33 33 format.html {
34 @trackers = @project.trackers.sorted.all
34 @trackers = @project.trackers.sorted.to_a
35 35 retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?})
36 36 @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
37 37 project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
@@ -56,7 +56,7 class VersionsController < ApplicationController
56 56 @versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?}
57 57 }
58 58 format.api {
59 @versions = @project.shared_versions.all
59 @versions = @project.shared_versions.to_a
60 60 }
61 61 end
62 62 end
@@ -67,7 +67,7 class VersionsController < ApplicationController
67 67 @issues = @version.fixed_issues.visible.
68 68 includes(:status, :tracker, :priority).
69 69 reorder("#{Tracker.table_name}.position, #{Issue.table_name}.id").
70 all
70 to_a
71 71 }
72 72 format.api
73 73 end
@@ -117,7 +117,7 class VersionsController < ApplicationController
117 117 end
118 118
119 119 def update
120 if request.put? && params[:version]
120 if params[:version]
121 121 attributes = params[:version].dup
122 122 attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing'])
123 123 @version.safe_attributes = attributes
@@ -53,7 +53,7 class WatchersController < ApplicationController
53 53 def append
54 54 if params[:watcher].is_a?(Hash)
55 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 57 end
58 58 if @users.blank?
59 59 render :nothing => true
@@ -92,7 +92,7 class WatchersController < ApplicationController
92 92 def find_watchables
93 93 klass = Object.const_get(params[:object_type].camelcase) rescue nil
94 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 96 raise Unauthorized if @watchables.any? {|w|
97 97 if w.respond_to?(:visible?)
98 98 !w.visible?
@@ -219,7 +219,7 class WikiController < ApplicationController
219 219 reorder('version DESC').
220 220 limit(@version_pages.per_page + 1).
221 221 offset(@version_pages.offset).
222 all
222 to_a
223 223
224 224 render :layout => false if request.xhr?
225 225 end
@@ -280,7 +280,7 class WikiController < ApplicationController
280 280 @pages = @wiki.pages.
281 281 order('title').
282 282 includes([:content, {:attachments => :author}]).
283 all
283 to_a
284 284 respond_to do |format|
285 285 format.html {
286 286 export = render_to_string :action => 'export_multiple', :layout => false
@@ -327,7 +327,7 private
327 327 def find_existing_or_new_page
328 328 @page = @wiki.find_or_new_page(params[:id])
329 329 if @wiki.page_found_with_redirect?
330 redirect_to params.update(:id => @page.title)
330 redirect_to_page @page
331 331 end
332 332 end
333 333
@@ -339,10 +339,14 private
339 339 return
340 340 end
341 341 if @wiki.page_found_with_redirect?
342 redirect_to params.update(:id => @page.title)
342 redirect_to_page @page
343 343 end
344 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 350 # Returns true if the current user is allowed to edit the page, otherwise false
347 351 def editable?(page = @page)
348 352 page.editable_by?(User.current)
@@ -360,6 +364,6 private
360 364 reorder("#{WikiPage.table_name}.title").
361 365 includes(:wiki => :project).
362 366 includes(:parent).
363 all
367 to_a
364 368 end
365 369 end
@@ -86,9 +86,9 class WorkflowsController < ApplicationController
86 86 @source_role = Role.find_by_id(params[:source_role_id].to_i)
87 87 end
88 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 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 92 if request.post?
93 93 if params[:source_tracker_id].blank? || params[:source_role_id].blank? || (@source_tracker.nil? && @source_role.nil?)
94 94 flash.now[:error] = l(:error_workflow_copy_source)
@@ -113,9 +113,9 class WorkflowsController < ApplicationController
113 113 def find_roles
114 114 ids = Array.wrap(params[:role_id])
115 115 if ids == ['all']
116 @roles = Role.sorted.all
116 @roles = Role.sorted.to_a
117 117 elsif ids.present?
118 @roles = Role.where(:id => ids).all
118 @roles = Role.where(:id => ids).to_a
119 119 end
120 120 @roles = nil if @roles.blank?
121 121 end
@@ -123,9 +123,9 class WorkflowsController < ApplicationController
123 123 def find_trackers
124 124 ids = Array.wrap(params[:tracker_id])
125 125 if ids == ['all']
126 @trackers = Tracker.sorted.all
126 @trackers = Tracker.sorted.to_a
127 127 elsif ids.present?
128 @trackers = Tracker.where(:id => ids).all
128 @trackers = Tracker.where(:id => ids).to_a
129 129 end
130 130 @trackers = nil if @trackers.blank?
131 131 end
@@ -135,6 +135,6 class WorkflowsController < ApplicationController
135 135 if @trackers && @used_statuses_only
136 136 @statuses = @trackers.map(&:issue_statuses).flatten.uniq.sort.presence
137 137 end
138 @statuses ||= IssueStatus.sorted.all
138 @statuses ||= IssueStatus.sorted.to_a
139 139 end
140 140 end
@@ -138,9 +138,7 module ApplicationHelper
138 138 if project.archived?
139 139 h(project.name)
140 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."
142 url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
143 link_to project.name, url, html_options
141 raise "#link_to_project no longer accepts :action option in Redmine 3.0"
144 142 else
145 143 link_to project.name, project_path(project, options), html_options
146 144 end
@@ -157,13 +155,6 module ApplicationHelper
157 155 end
158 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 158 # Helper that formats object for html or text rendering
168 159 def format_object(object, html=true, &block)
169 160 if block_given?
@@ -185,7 +176,7 module ApplicationHelper
185 176 when 'Project'
186 177 html ? link_to_project(object) : object.to_s
187 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 180 when 'TrueClass'
190 181 l(:general_text_Yes)
191 182 when 'FalseClass'
@@ -247,7 +238,7 module ApplicationHelper
247 238 end
248 239
249 240 def format_version_name(version)
250 if !version.shared? || version.project == @project
241 if version.project == @project
251 242 h(version)
252 243 else
253 244 h("#{version.project} - #{version}")
@@ -502,7 +493,7 module ApplicationHelper
502 493 h(Setting.app_title)
503 494 else
504 495 b = []
505 ancestors = (@project.root? ? [] : @project.ancestors.visible.all)
496 ancestors = (@project.root? ? [] : @project.ancestors.visible.to_a)
506 497 if ancestors.any?
507 498 root = ancestors.shift
508 499 b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
@@ -1217,7 +1208,7 module ApplicationHelper
1217 1208 source
1218 1209 end
1219 1210 end
1220 super sources, options
1211 super *sources, options
1221 1212 end
1222 1213
1223 1214 # Overrides Rails' image_tag with themes and plugins support.
@@ -1250,7 +1241,7 module ApplicationHelper
1250 1241 end
1251 1242 end
1252 1243 end
1253 super sources, options
1244 super *sources, options
1254 1245 end
1255 1246
1256 1247 # TODO: remove this in 2.5.0
@@ -1288,12 +1279,7 module ApplicationHelper
1288 1279 end
1289 1280
1290 1281 def sanitize_anchor_name(anchor)
1291 if ''.respond_to?(:encoding) || RUBY_PLATFORM == 'java'
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
1282 anchor.gsub(%r{[^\s\-\p{Word}]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
1297 1283 end
1298 1284
1299 1285 # Returns the javascript tags that are included in the html layout head
@@ -30,7 +30,7 module GroupsHelper
30 30 scope = User.active.sorted.not_in_group(group).like(params[:q])
31 31 principal_count = scope.count
32 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 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 64 def render_issue_subject_with_tree(issue)
65 65 s = ''
66 ancestors = issue.root? ? [] : issue.ancestors.visible.all
66 ancestors = issue.root? ? [] : issue.ancestors.visible.to_a
67 67 ancestors.each do |ancestor|
68 68 s << '<div>' + content_tag('p', link_to_issue(ancestor, :project => (issue.project_id != ancestor.project_id)))
69 69 end
@@ -204,7 +204,7 module IssuesHelper
204 204 order("#{Query.table_name}.name ASC").
205 205 # Project specific queries and global queries
206 206 where(@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id]).
207 all
207 to_a
208 208 end
209 209 @sidebar_queries
210 210 end
@@ -408,7 +408,7 module IssuesHelper
408 408 if association
409 409 record = association.class_name.constantize.find_by_id(id)
410 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 412 return record.name
413 413 end
414 414 end
@@ -22,7 +22,7 module MembersHelper
22 22 scope = Principal.active.sorted.not_member_of(project).like(params[:q])
23 23 principal_count = scope.count
24 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 27 s = content_tag('div', principals_check_box_tags('membership[user_ids][]', principals), :id => 'principals')
28 28
@@ -23,11 +23,12 module MyHelper
23 23 where(:project_id => User.current.projects.map(&:id)).
24 24 where("(start_date>=? and start_date<=?) or (due_date>=? and due_date<=?)", startdt, enddt, startdt, enddt).
25 25 includes(:project, :tracker, :priority, :assigned_to).
26 all
26 references(:project, :tracker, :priority, :assigned_to).
27 to_a
27 28 end
28 29
29 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 32 end
32 33
33 34 def issuesassignedtome_items
@@ -35,8 +36,9 module MyHelper
35 36 where(:assigned_to_id => ([User.current.id] + User.current.group_ids)).
36 37 limit(10).
37 38 includes(:status, :project, :tracker, :priority).
39 references(:status, :project, :tracker, :priority).
38 40 order("#{IssuePriority.table_name}.position DESC, #{Issue.table_name}.updated_on DESC").
39 all
41 to_a
40 42 end
41 43
42 44 def issuesreportedbyme_items
@@ -44,12 +46,13 module MyHelper
44 46 where(:author_id => User.current.id).
45 47 limit(10).
46 48 includes(:status, :project, :tracker).
49 references(:status, :project, :tracker).
47 50 order("#{Issue.table_name}.updated_on DESC").
48 all
51 to_a
49 52 end
50 53
51 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 56 end
54 57
55 58 def news_items
@@ -57,15 +60,17 module MyHelper
57 60 where(:project_id => User.current.projects.map(&:id)).
58 61 limit(10).
59 62 includes(:project, :author).
63 references(:project, :author).
60 64 order("#{News.table_name}.created_on DESC").
61 all
65 to_a
62 66 end
63 67
64 68 def timelog_items
65 69 TimeEntry.
66 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 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 75 end
71 76 end
@@ -18,6 +18,11
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 19
20 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 26 def project_settings_tabs
22 27 tabs = [{:name => 'info', :action => :edit_project, :partial => 'projects/edit', :label => :label_information_plural},
23 28 {:name => 'modules', :action => :select_project_modules, :partial => 'projects/settings/modules', :label => :label_module_plural},
@@ -143,7 +143,7 module QueriesHelper
143 143 end
144 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 147 # csv header fields
148 148 csv << columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) }
149 149 # csv lines
@@ -29,7 +29,6 module SearchHelper
29 29 result << '...'
30 30 break
31 31 end
32 words = words.mb_chars
33 32 if i.even?
34 33 result << h(words.length > 100 ? "#{words.slice(0..44)} ... #{words.slice(-45..-1)}" : words)
35 34 else
@@ -84,7 +84,8 module SortHelper
84 84 def to_sql
85 85 sql = @criteria.collect do |k,o|
86 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 89 end
89 90 end.flatten.compact
90 91 sql.blank? ? nil : sql
@@ -105,7 +105,7 module TimelogHelper
105 105
106 106 def report_to_csv(report)
107 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 109 # Column headers
110 110 headers = report.criteria.collect {|criteria| l(report.available_criteria[criteria][:label]) }
111 111 headers += report.periods
@@ -27,6 +27,7 class Attachment < ActiveRecord::Base
27 27 validates_length_of :disk_filename, :maximum => 255
28 28 validates_length_of :description, :maximum => 255
29 29 validate :validate_max_file_size
30 attr_protected :id
30 31
31 32 acts_as_event :title => :filename,
32 33 :url => Proc.new {|o| {:controller => 'attachments', :action => 'download', :id => o.id, :filename => o.filename}}
@@ -34,16 +35,16 class Attachment < ActiveRecord::Base
34 35 acts_as_activity_provider :type => 'files',
35 36 :permission => :view_files,
36 37 :author_key => :author_id,
37 :find_options => {: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 "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 )"}
38 :scope => select("#{Attachment.table_name}.*").
39 joins("LEFT JOIN #{Version.table_name} ON #{Attachment.table_name}.container_type='Version' AND #{Version.table_name}.id = #{Attachment.table_name}.container_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 42 acts_as_activity_provider :type => 'documents',
42 43 :permission => :view_documents,
43 44 :author_key => :author_id,
44 :find_options => {: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 "LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id"}
45 :scope => select("#{Attachment.table_name}.*").
46 joins("LEFT JOIN #{Document.table_name} ON #{Attachment.table_name}.container_type='Document' AND #{Document.table_name}.id = #{Attachment.table_name}.container_id " +
47 "LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id")
47 48
48 49 cattr_accessor :storage_path
49 50 @@storage_path = Redmine::Configuration['attachments_storage_path'] || File.join(Rails.root, "files")
@@ -74,7 +75,7 class Attachment < ActiveRecord::Base
74 75 if @temp_file.size > 0
75 76 if @temp_file.respond_to?(:original_filename)
76 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 79 end
79 80 if @temp_file.respond_to?(:content_type)
80 81 self.content_type = @temp_file.content_type.to_s.chomp
@@ -29,6 +29,7 class AuthSource < ActiveRecord::Base
29 29 validates_presence_of :name
30 30 validates_uniqueness_of :name
31 31 validates_length_of :name, :maximum => 60
32 attr_protected :id
32 33
33 34 def authenticate(login, password)
34 35 end
@@ -18,8 +18,8
18 18 class Board < ActiveRecord::Base
19 19 include Redmine::SafeAttributes
20 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"
22 has_many :messages, :dependent => :destroy, :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, lambda {order("#{Message.table_name}.created_on DESC")}, :dependent => :destroy
23 23 belongs_to :last_message, :class_name => 'Message', :foreign_key => :last_message_id
24 24 acts_as_tree :dependent => :nullify
25 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 29 validates_length_of :name, :maximum => 30
30 30 validates_length_of :description, :maximum => 255
31 31 validate :validate_board
32 attr_protected :id
32 33
33 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 40 safe_attributes 'name', 'description', 'parent_id', 'move_to'
@@ -21,6 +21,7 class Change < ActiveRecord::Base
21 21 validates_presence_of :changeset_id, :action, :path
22 22 before_save :init_path
23 23 before_validation :replace_invalid_utf8_of_path
24 attr_protected :id
24 25
25 26 def relative_path
26 27 changeset.repository.relative_path(path)
@@ -35,20 +35,23 class Changeset < ActiveRecord::Base
35 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 37 acts_as_searchable :columns => 'comments',
38 :include => {:repository => :project},
38 :scope => preload(:repository => :project),
39 39 :project_key => "#{Repository.table_name}.project_id",
40 40 :date_column => 'committed_on'
41 41
42 42 acts_as_activity_provider :timestamp => "#{table_name}.committed_on",
43 43 :author_key => :user_id,
44 :find_options => {:include => [:user, {:repository => :project}]}
44 :scope => preload(:user, {:repository => :project})
45 45
46 46 validates_presence_of :repository_id, :revision, :committed_on, :commit_date
47 47 validates_uniqueness_of :revision, :scope => :repository_id
48 48 validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
49 attr_protected :id
49 50
50 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 57 after_create :scan_for_issues
@@ -21,6 +21,7 class Comment < ActiveRecord::Base
21 21 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
22 22
23 23 validates_presence_of :commented, :author, :comments
24 attr_protected :id
24 25
25 26 after_create :send_notification
26 27
@@ -29,6 +29,7 class CustomField < ActiveRecord::Base
29 29 validates_length_of :name, :maximum => 30
30 30 validates_inclusion_of :field_format, :in => Proc.new { Redmine::FieldFormat.available_formats }
31 31 validate :validate_custom_field
32 attr_protected :id
32 33
33 34 before_validation :set_searchable
34 35 before_save do |field|
@@ -117,7 +118,7 class CustomField < ActiveRecord::Base
117 118 values = read_attribute(:possible_values)
118 119 if values.is_a?(Array)
119 120 values.each do |value|
120 value.force_encoding('UTF-8') if value.respond_to?(:force_encoding)
121 value.force_encoding('UTF-8')
121 122 end
122 123 values
123 124 else
@@ -218,7 +219,7 class CustomField < ActiveRecord::Base
218 219
219 220 # to move in project_custom_field
220 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 223 end
223 224
224 225 def type_name
@@ -18,6 +18,7
18 18 class CustomValue < ActiveRecord::Base
19 19 belongs_to :custom_field
20 20 belongs_to :customized, :polymorphic => true
21 attr_protected :id
21 22
22 23 def initialize(attributes=nil, *args)
23 24 super
@@ -21,19 +21,23 class Document < ActiveRecord::Base
21 21 belongs_to :category, :class_name => "DocumentCategory", :foreign_key => "category_id"
22 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 26 acts_as_event :title => Proc.new {|o| "#{l(:label_document)}: #{o.title}"},
26 27 :author => Proc.new {|o| o.attachments.reorder("#{Attachment.table_name}.created_on ASC").first.try(:author) },
27 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 31 validates_presence_of :project, :title, :category
31 32 validates_length_of :title, :maximum => 60
33 attr_protected :id
32 34
33 35 after_create :send_notification
34 36
35 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 43 safe_attributes 'category_id', 'title', 'description'
@@ -21,6 +21,7 class EnabledModule < ActiveRecord::Base
21 21
22 22 validates_presence_of :name
23 23 validates_uniqueness_of :name, :scope => :project_id
24 attr_protected :id
24 25
25 26 after_create :module_enabled
26 27
@@ -18,7 +18,7
18 18 class Enumeration < ActiveRecord::Base
19 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 23 belongs_to :project
24 24
@@ -28,6 +28,7 class Group < Principal
28 28 validates_presence_of :lastname
29 29 validates_uniqueness_of :lastname, :case_sensitive => false
30 30 validates_length_of :lastname, :maximum => 255
31 attr_protected :id
31 32
32 33 before_destroy :remove_references_before_destroy
33 34
@@ -81,7 +82,8 class Group < Principal
81 82 def user_removed(user)
82 83 members.each do |member|
83 84 MemberRole.
84 includes(:member).
85 joins(:member).
86 references(:member).
85 87 where("#{Member.table_name}.user_id = ? AND #{MemberRole.table_name}.inherited_from IN (?)", user.id, member.member_role_ids).
86 88 each(&:destroy)
87 89 end
@@ -95,10 +97,6 class Group < Principal
95 97 super(attr_name, *args)
96 98 end
97 99
98 def self.builtin_id(arg)
99 (arg.anonymous? ? GroupAnonymous : GroupNonMember).instance_id
100 end
101
102 100 def self.anonymous
103 101 GroupAnonymous.load_instance
104 102 end
@@ -23,8 +23,4 class GroupAnonymous < GroupBuiltin
23 23 def builtin_type
24 24 "anonymous"
25 25 end
26
27 def self.instance_id
28 @@instance_id ||= load_instance.id
29 end
30 26 end
@@ -37,7 +37,7 class GroupBuiltin < Group
37 37 class << self
38 38 def load_instance
39 39 return nil if self == GroupBuiltin
40 instance = first(:order => 'id') || create_instance
40 instance = order('id').first || create_instance
41 41 end
42 42
43 43 def create_instance
@@ -23,8 +23,4 class GroupNonMember < GroupBuiltin
23 23 def builtin_type
24 24 "non_member"
25 25 end
26
27 def self.instance_id
28 @@instance_id ||= load_instance.id
29 end
30 26 end
@@ -31,15 +31,12 class Issue < ActiveRecord::Base
31 31
32 32 has_many :journals, :as => :journalized, :dependent => :destroy
33 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 35 :class_name => 'Journal',
35 :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
36 :as => :journalized
40 37
41 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 41 has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all
45 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 46 acts_as_customizable
50 47 acts_as_watchable
51 48 acts_as_searchable :columns => ['subject', "#{table_name}.description", "#{Journal.table_name}.notes"],
52 :include => [:project, :visible_journals],
53 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 57 acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id} (#{o.status}): #{o.subject}"},
56 58 :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}},
57 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 62 :author_key => :author_id
61 63
62 64 DONE_RATIO_OPTIONS = %w(issue_field issue_status)
@@ -72,19 +74,26 class Issue < ActiveRecord::Base
72 74 validates :start_date, :date => true
73 75 validates :due_date, :date => true
74 76 validate :validate_issue, :validate_required_fields
77 attr_protected :id
75 78
76 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 85 scope :open, lambda {|*args|
81 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 92 scope :recently_updated, lambda { order("#{Issue.table_name}.updated_on DESC") }
86 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 98 scope :fixed_version, lambda {|versions|
90 99 ids = [versions].flatten.compact.map {|v| v.is_a?(Version) ? v.id : v}
@@ -107,7 +116,7 class Issue < ActiveRecord::Base
107 116 # Returns a SQL conditions string used to find all issues visible by the specified user
108 117 def self.visible_condition(user, options={})
109 118 Project.allowed_to_condition(user, :view_issues, options) do |role, user|
110 if user.logged?
119 if user.id && user.logged?
111 120 case role.issues_visibility
112 121 when 'all'
113 122 nil
@@ -351,6 +360,10 class Issue < ActiveRecord::Base
351 360 # Do not redefine alias chain on reload (see #4838)
352 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 367 def estimated_hours=(h)
355 368 write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h)
356 369 end
@@ -423,7 +436,7 class Issue < ActiveRecord::Base
423 436 def safe_attributes=(attrs, user=User.current)
424 437 return unless attrs.is_a?(Hash)
425 438
426 attrs = attrs.dup
439 attrs = attrs.deep_dup
427 440
428 441 # Project and Tracker must be set before since new_statuses_allowed_to depends on it.
429 442 if (p = attrs.delete('project_id')) && safe_attribute?('project_id')
@@ -458,14 +471,12 class Issue < ActiveRecord::Base
458 471
459 472 if attrs['custom_field_values'].present?
460 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
462 attrs['custom_field_values'] = attrs['custom_field_values'].reject {|k, v| !editable_custom_field_ids.include?(k.to_s)}
474 attrs['custom_field_values'].select! {|k, v| editable_custom_field_ids.include?(k.to_s)}
463 475 end
464 476
465 477 if attrs['custom_fields'].present?
466 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
468 attrs['custom_fields'] = attrs['custom_fields'].reject {|c| !editable_custom_field_ids.include?(c['id'].to_s)}
479 attrs['custom_fields'].select! {|c| editable_custom_field_ids.include?(c['id'].to_s)}
469 480 end
470 481
471 482 # mass-assignment security bypass
@@ -733,7 +744,7 class Issue < ActiveRecord::Base
733 744 def assignable_versions
734 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 748 if fixed_version
738 749 if fixed_version_id_changed?
739 750 # nothing to do
@@ -879,10 +890,14 class Issue < ActiveRecord::Base
879 890 if issues.any?
880 891 issue_ids = issues.map(&:id)
881 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 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
885
897 relations_to = IssueRelation.joins(:issue_from => :project).
898 references(:issue_from => :project).
899 where(visible_condition(user)).
900 where(:issue_to_id => issue_ids).to_a
886 901 issues.each do |issue|
887 902 relations =
888 903 relations_from.select {|relation| relation.issue_from_id == issue.id} +
@@ -1121,6 +1136,7 class Issue < ActiveRecord::Base
1121 1136 def parent_issue_id=(arg)
1122 1137 s = arg.to_s.strip.presence
1123 1138 if s && (m = s.match(%r{\A#?(\d+)\z})) && (@parent_issue = Issue.find_by_id(m[1]))
1139 @parent_issue.id
1124 1140 @invalid_parent_issue_id = nil
1125 1141 elsif s.blank?
1126 1142 @parent_issue = nil
@@ -1349,7 +1365,7 class Issue < ActiveRecord::Base
1349 1365 self.root_id = (@parent_issue.nil? ? id : @parent_issue.root_id)
1350 1366 cond = ["root_id = ? AND lft >= ? AND rgt <= ? ", old_root_id, lft, rgt]
1351 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 1369 Issue.where(cond).
1354 1370 update_all(["root_id = ?, lft = lft + ?, rgt = rgt + ?", root_id, offset, offset])
1355 1371 end
@@ -1367,6 +1383,14 class Issue < ActiveRecord::Base
1367 1383 recalculate_attributes_for(former_parent_id) if former_parent_id
1368 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 1394 def update_parent_attributes
1371 1395 recalculate_attributes_for(parent_id) if parent_id
1372 1396 end
@@ -1395,7 +1419,7 class Issue < ActiveRecord::Base
1395 1419 end
1396 1420 done = p.leaves.joins(:status).
1397 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 1423 progress = done / (average * leaves_count)
1400 1424 p.done_ratio = progress.round
1401 1425 end
@@ -1415,7 +1439,8 class Issue < ActiveRecord::Base
1415 1439 def self.update_versions(conditions=nil)
1416 1440 # Only need to update issues with a fixed_version from
1417 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 1444 where("#{Issue.table_name}.fixed_version_id IS NOT NULL" +
1420 1445 " AND #{Issue.table_name}.project_id <> #{Version.table_name}.project_id" +
1421 1446 " AND #{Version.table_name}.sharing <> 'system'").
@@ -24,6 +24,7 class IssueCategory < ActiveRecord::Base
24 24 validates_presence_of :name
25 25 validates_uniqueness_of :name, :scope => [:project_id]
26 26 validates_length_of :name, :maximum => 30
27 attr_protected :id
27 28
28 29 safe_attributes 'name', 'assigned_to_id'
29 30
@@ -32,7 +32,7 class IssueCustomField < CustomField
32 32 sql = super
33 33 id_column ||= id
34 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 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 38 "((#{sql}) AND (#{tracker_condition}) AND (#{project_condition}))"
@@ -45,7 +45,9 class IssueQuery < Query
45 45 scope :visible, lambda {|*args|
46 46 user = args.shift || User.current
47 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 52 if user.admin?
51 53 scope.where("#{table_name}.visibility <> ? OR #{table_name}.user_id = ?", VISIBILITY_PRIVATE, user.id)
@@ -132,17 +134,17 class IssueQuery < Query
132 134 if project
133 135 principals += project.principals.sort
134 136 unless project.leaf?
135 subprojects = project.descendants.visible.all
137 subprojects = project.descendants.visible.to_a
136 138 principals += Principal.member_of(subprojects)
137 139 end
138 versions = project.shared_versions.all
139 categories = project.issue_categories.all
140 versions = project.shared_versions.to_a
141 categories = project.issue_categories.to_a
140 142 issue_custom_fields = project.all_issue_custom_fields
141 143 else
142 144 if all_projects.any?
143 145 principals += Principal.member_of(all_projects)
144 146 end
145 versions = Version.visible.where(:sharing => 'system').all
147 versions = Version.visible.where(:sharing => 'system').to_a
146 148 issue_custom_fields = IssueCustomField.where(:is_for_all => true)
147 149 end
148 150 principals.uniq!
@@ -339,7 +341,7 class IssueQuery < Query
339 341 scope = scope.preload(:author)
340 342 end
341 343
342 issues = scope.all
344 issues = scope.to_a
343 345
344 346 if has_column?(:spent_hours)
345 347 Issue.load_visible_spent_hours(issues)
@@ -360,12 +362,13 class IssueQuery < Query
360 362 joins(:status, :project).
361 363 where(statement).
362 364 includes(([:status, :project] + (options[:include] || [])).uniq).
365 references(([:status, :project] + (options[:include] || [])).uniq).
363 366 where(options[:conditions]).
364 367 order(order_option).
365 368 joins(joins_for_order_statement(order_option.join(','))).
366 369 limit(options[:limit]).
367 370 offset(options[:offset]).
368 find_ids
371 pluck(:id)
369 372 rescue ::ActiveRecord::StatementInvalid => e
370 373 raise StatementInvalid.new(e.message)
371 374 end
@@ -380,7 +383,7 class IssueQuery < Query
380 383 limit(options[:limit]).
381 384 offset(options[:offset]).
382 385 preload(:details, :user, {:issue => [:project, :author, :tracker, :status]}).
383 all
386 to_a
384 387 rescue ::ActiveRecord::StatementInvalid => e
385 388 raise StatementInvalid.new(e.message)
386 389 end
@@ -392,7 +395,8 class IssueQuery < Query
392 395 where(project_statement).
393 396 where(options[:conditions]).
394 397 includes(:project).
395 all
398 references(:project).
399 to_a
396 400 rescue ::ActiveRecord::StatementInvalid => e
397 401 raise StatementInvalid.new(e.message)
398 402 end
@@ -411,7 +415,7 class IssueQuery < Query
411 415 groups = Group.givable
412 416 operator = '!' # Override the operator since we want to find by assigned_to
413 417 else
414 groups = Group.where(:id => value).all
418 groups = Group.where(:id => value).to_a
415 419 end
416 420 groups ||= []
417 421
@@ -431,7 +435,7 class IssueQuery < Query
431 435 " WHERE #{Member.table_name}.project_id = #{Issue.table_name}.project_id))"
432 436 when "=", "!"
433 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 439 "1=0"
436 440
437 441 sw = operator == "!" ? 'NOT' : ''
@@ -443,7 +447,7 class IssueQuery < Query
443 447
444 448 def sql_for_is_private_field(field, operator, value)
445 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 452 "#{Issue.table_name}.is_private #{op} (#{va})"
449 453 end
@@ -462,14 +466,14 class IssueQuery < Query
462 466 sql = case operator
463 467 when "*", "!*"
464 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 470 when "=", "!"
467 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 473 when "=p", "=!p", "!p"
470 474 op = (operator == "!p" ? 'NOT IN' : 'IN')
471 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 477 end
474 478
475 479 if relation_options[:sym] == field && !options[:reverse]
@@ -27,6 +27,7 class IssueStatus < ActiveRecord::Base
27 27 validates_uniqueness_of :name
28 28 validates_length_of :name, :maximum => 30
29 29 validates_inclusion_of :default_done_ratio, :in => 0..100, :allow_nil => true
30 attr_protected :id
30 31
31 32 scope :sorted, lambda { order("#{table_name}.position ASC") }
32 33 scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)}
@@ -79,7 +80,7 class IssueStatus < ActiveRecord::Base
79 80 includes(:new_status).
80 81 where(["role_id IN (:role_ids) AND tracker_id = :tracker_id AND (#{conditions})",
81 82 {:role_ids => roles.collect(&:id), :tracker_id => tracker.id, :true => true, :false => false}
82 ]).all.
83 ]).to_a.
83 84 map(&:new_status).compact.sort
84 85 else
85 86 []
@@ -24,6 +24,7 class Journal < ActiveRecord::Base
24 24 belongs_to :user
25 25 has_many :details, :class_name => "JournalDetail", :dependent => :delete_all
26 26 attr_accessor :indice
27 attr_protected :id
27 28
28 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 30 :description => :notes,
@@ -34,17 +35,18 class Journal < ActiveRecord::Base
34 35
35 36 acts_as_activity_provider :type => 'issues',
36 37 :author_key => :user_id,
37 :find_options => {:include => [{:issue => :project}, :details, :user],
38 :conditions => "#{Journal.table_name}.journalized_type = 'Issue' AND" +
39 " (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')"}
38 :scope => preload({:issue => :project}, :user).
39 joins("LEFT OUTER JOIN #{JournalDetail.table_name} ON #{JournalDetail.table_name}.journal_id = #{Journal.table_name}.id").
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 43 before_create :split_private_notes
42 44 after_create :send_notification
43 45
44 46 scope :visible, lambda {|*args|
45 47 user = args.shift || User.current
46
47 includes(:issue => :project).
48 joins(:issue => :project).
49 references(:project).
48 50 where(Issue.visible_condition(user, *args)).
49 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 18 class JournalDetail < ActiveRecord::Base
19 19 belongs_to :journal
20 20 before_save :normalize_values
21 attr_protected :id
21 22
22 23 def custom_field
23 24 if property == 'cf'
@@ -42,7 +42,7 class MailHandler < ActionMailer::Base
42 42 @@handler_options[:no_notification] = (@@handler_options[:no_notification].to_s == '1')
43 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 46 super(email)
47 47 end
48 48
@@ -417,7 +417,7 class MailHandler < ActionMailer::Base
417 417 end
418 418
419 419 parts.reject! do |part|
420 part.header[:content_disposition].try(:disposition_type) == 'attachment'
420 part.attachment?
421 421 end
422 422
423 423 @plain_text_body = parts.map do |p|
@@ -25,6 +25,7 class Member < ActiveRecord::Base
25 25 validates_presence_of :principal, :project
26 26 validates_uniqueness_of :user_id, :scope => :project_id
27 27 validate :validate_role
28 attr_protected :id
28 29
29 30 before_destroy :set_issue_category_nil
30 31
@@ -26,6 +26,7 class MemberRole < ActiveRecord::Base
26 26
27 27 validates_presence_of :role
28 28 validate :validate_role_member
29 attr_protected :id
29 30
30 31 def validate_role_member
31 32 errors.add :role_id, :invalid if role && !role.member?
@@ -22,9 +22,10 class Message < ActiveRecord::Base
22 22 acts_as_tree :counter_cache => :replies_count, :order => "#{Message.table_name}.created_on ASC"
23 23 acts_as_attachable
24 24 belongs_to :last_reply, :class_name => 'Message', :foreign_key => 'last_reply_id'
25 attr_protected :id
25 26
26 27 acts_as_searchable :columns => ['subject', 'content'],
27 :include => {:board => :project},
28 :scope => preload(:board => :project),
28 29 :project_key => "#{Board.table_name}.project_id",
29 30 :date_column => "#{table_name}.created_on"
30 31 acts_as_event :title => Proc.new {|o| "#{o.board.name}: #{o.subject}"},
@@ -34,7 +35,7 class Message < ActiveRecord::Base
34 35 :url => Proc.new {|o| {:controller => 'messages', :action => 'show', :board_id => o.board_id}.merge(o.parent_id.nil? ? {:id => o.id} :
35 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 39 :author_key => :author_id
39 40 acts_as_watchable
40 41
@@ -48,7 +49,9 class Message < ActiveRecord::Base
48 49 after_create :send_notification
49 50
50 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 57 safe_attributes 'subject', 'content'
@@ -19,16 +19,18 class News < ActiveRecord::Base
19 19 include Redmine::SafeAttributes
20 20 belongs_to :project
21 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 24 validates_presence_of :title, :description
25 25 validates_length_of :title, :maximum => 60
26 26 validates_length_of :summary, :maximum => 255
27 attr_protected :id
27 28
28 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 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 34 :author_key => :author_id
33 35 acts_as_watchable
34 36
@@ -36,7 +38,9 class News < ActiveRecord::Base
36 38 after_create :send_notification
37 39
38 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 46 safe_attributes 'title', 'summary', 'description'
@@ -68,7 +72,7 class News < ActiveRecord::Base
68 72
69 73 # returns latest news for projects visible by user
70 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 76 end
73 77
74 78 private
@@ -25,11 +25,13 class Principal < ActiveRecord::Base
25 25 STATUS_LOCKED = 3
26 26
27 27 has_many :members, :foreign_key => 'user_id', :dependent => :destroy
28 has_many :memberships, :class_name => 'Member',
29 :foreign_key => 'user_id',
30 :include => [:project, :roles],
31 :conditions => "#{Project.table_name}.status<>#{Project::STATUS_ARCHIVED}",
32 :order => "#{Project.table_name}.name"
28 has_many :memberships,
29 lambda {preload(:project, :roles).
30 joins(:project).
31 where("#{Project.table_name}.status<>#{Project::STATUS_ARCHIVED}").
32 order("#{Project.table_name}.name")},
33 :class_name => 'Member',
34 :foreign_key => 'user_id'
33 35 has_many :projects, :through => :memberships
34 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 59 # Principals that are members of a collection of projects
58 60 scope :member_of, lambda {|projects|
59 projects = [projects] unless projects.is_a?(Array)
60 if projects.empty?
61 projects = [projects] if projects.is_a?(Project)
62 if projects.blank?
61 63 where("1=0")
62 64 else
63 65 ids = projects.map(&:id)
@@ -28,31 +28,35 class Project < ActiveRecord::Base
28 28
29 29 # Specific overridden Activities
30 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 35 has_many :memberships, :class_name => 'Member'
33 has_many :member_principals, :class_name => 'Member',
34 :include => :principal,
35 :conditions => "#{Principal.table_name}.status=#{Principal::STATUS_ACTIVE}"
36
36 has_many :member_principals,
37 lambda { joins(:principal).
38 references(:principal).
39 where("#{Principal.table_name}.status=#{Principal::STATUS_ACTIVE}")},
40 :class_name => 'Member'
37 41 has_many :enabled_modules, :dependent => :delete_all
38 has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
39 has_many :issues, :dependent => :destroy, :include => [:status, :tracker]
42 has_and_belongs_to_many :trackers, lambda {order("#{Tracker.table_name}.position")}
43 has_many :issues, :dependent => :destroy
40 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 46 has_many :time_entries, :dependent => :destroy
43 47 has_many :queries, :class_name => 'IssueQuery', :dependent => :delete_all
44 48 has_many :documents, :dependent => :destroy
45 has_many :news, :dependent => :destroy, :include => :author
46 has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name"
47 has_many :boards, :dependent => :destroy, :order => "position ASC"
48 has_one :repository, :conditions => ["is_default = ?", true]
49 has_many :news, lambda {includes(:author)}, :dependent => :destroy
50 has_many :issue_categories, lambda {order("#{IssueCategory.table_name}.name")}, :dependent => :delete_all
51 has_many :boards, lambda {order("position ASC")}, :dependent => :destroy
52 has_one :repository, lambda {where(["is_default = ?", true])}
49 53 has_many :repositories, :dependent => :destroy
50 54 has_many :changesets, :through => :repository
51 55 has_one :wiki, :dependent => :destroy
52 56 # Custom field for the project issues
53 57 has_and_belongs_to_many :issue_custom_fields,
58 lambda {order("#{CustomField.table_name}.position")},
54 59 :class_name => 'IssueCustomField',
55 :order => "#{CustomField.table_name}.position",
56 60 :join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}",
57 61 :association_foreign_key => 'custom_field_id'
58 62
@@ -126,9 +130,9 class Project < ActiveRecord::Base
126 130 if !initialized.key?('trackers') && !initialized.key?('tracker_ids')
127 131 default = Setting.default_projects_tracker_ids
128 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 134 else
131 self.trackers = Tracker.sorted.all
135 self.trackers = Tracker.sorted.to_a
132 136 end
133 137 end
134 138 end
@@ -144,7 +148,7 class Project < ActiveRecord::Base
144 148 # returns latest created projects
145 149 # non public projects will be returned only if user is a member of those
146 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 152 end
149 153
150 154 # Returns true if the project is visible to +user+ or to the current user.
@@ -212,9 +216,9 class Project < ActiveRecord::Base
212 216 end
213 217
214 218 def override_roles(role)
215 @override_members ||= memberships.where(:user_id => [GroupAnonymous.instance_id, GroupNonMember.instance_id]).all
216 member = @override_members.detect {|m| role.anonymous? ^ (m.user_id == GroupNonMember.instance_id)}
217 member ? member.roles : [role]
219 group_class = role.anonymous? ? GroupAnonymous : GroupNonMember
220 member = member_principals.where("#{Principal.table_name}.type = ?", group_class.name).first
221 member ? member.roles.to_a : [role]
218 222 end
219 223
220 224 def principals
@@ -364,7 +368,7 class Project < ActiveRecord::Base
364 368 # by the current user
365 369 def allowed_parents
366 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 372 @allowed_parents = @allowed_parents - self_and_descendants
369 373 if User.current.allowed_to?(:add_project, nil, :global => true) || (!new_record? && parent.nil?)
370 374 @allowed_parents << nil
@@ -435,11 +439,12 class Project < ActiveRecord::Base
435 439 @rolled_up_trackers ||=
436 440 Tracker.
437 441 joins(:projects).
442 references(:project).
438 443 joins("JOIN #{EnabledModule.table_name} ON #{EnabledModule.table_name}.project_id = #{Project.table_name}.id AND #{EnabledModule.table_name}.name = 'issue_tracking'").
439 444 select("DISTINCT #{Tracker.table_name}.*").
440 445 where("#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status <> #{STATUS_ARCHIVED}", lft, rgt).
441 446 sorted.
442 all
447 to_a
443 448 end
444 449
445 450 # Closes open and locked project versions that are completed
@@ -457,7 +462,8 class Project < ActiveRecord::Base
457 462 def rolled_up_versions
458 463 @rolled_up_versions ||=
459 464 Version.
460 includes(:project).
465 joins(:project).
466 references(:project).
461 467 where("#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status <> ?", lft, rgt, STATUS_ARCHIVED)
462 468 end
463 469
@@ -465,13 +471,17 class Project < ActiveRecord::Base
465 471 def shared_versions
466 472 if new_record?
467 473 Version.
468 includes(:project).
474 joins(:project).
475 references(:project).
476 preload(:project).
469 477 where("#{Project.table_name}.status <> ? AND #{Version.table_name}.sharing = 'system'", STATUS_ARCHIVED)
470 478 else
471 479 @shared_versions ||= begin
472 480 r = root? ? self : root
473 481 Version.
474 includes(:project).
482 joins(:project).
483 references(:project).
484 preload(:project).
475 485 where("#{Project.table_name}.id = #{id}" +
476 486 " OR (#{Project.table_name}.status <> #{Project::STATUS_ARCHIVED} AND (" +
477 487 " #{Version.table_name}.sharing = 'system'" +
@@ -497,7 +507,7 class Project < ActiveRecord::Base
497 507 # Deletes all project's members
498 508 def delete_all_members
499 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 511 Member.delete_all(['project_id = ?', id])
502 512 end
503 513
@@ -728,7 +738,7 class Project < ActiveRecord::Base
728 738 project = project.is_a?(Project) ? project : Project.find(project)
729 739
730 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 743 Project.transaction do
734 744 if save
@@ -740,6 +750,7 class Project < ActiveRecord::Base
740 750 save
741 751 end
742 752 end
753 true
743 754 end
744 755
745 756 # Returns a new unsaved Project instance with attributes copied from +project+
@@ -958,11 +969,10 class Project < ActiveRecord::Base
958 969 def copy_queries(project)
959 970 project.queries.each do |query|
960 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 973 new_query.sort_criteria = query.sort_criteria if query.sort_criteria
963 974 new_query.project = self
964 975 new_query.user_id = query.user_id
965 new_query.role_ids = query.role_ids if query.visibility == IssueQuery::VISIBILITY_ROLES
966 976 self.queries << new_query
967 977 end
968 978 end
@@ -288,7 +288,7 class Query < ActiveRecord::Base
288 288 end
289 289
290 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 292 end
293 293
294 294 # Returns a hash of localized labels for all filter operators
@@ -306,7 +306,7 class Query < ActiveRecord::Base
306 306 end
307 307
308 308 def all_projects
309 @all_projects ||= Project.visible.all
309 @all_projects ||= Project.visible.to_a
310 310 end
311 311
312 312 def all_projects_values
@@ -655,7 +655,7 class Query < ActiveRecord::Base
655 655 sql = "#{db_table}.#{db_field} BETWEEN #{value.first.to_f - 1e-5} AND #{value.first.to_f + 1e-5}"
656 656 end
657 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 659 end
660 660 else
661 661 # IN an empty set
@@ -663,7 +663,7 class Query < ActiveRecord::Base
663 663 end
664 664 when "!"
665 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 667 else
668 668 # NOT IN an empty set
669 669 sql = "1=1"
@@ -705,9 +705,9 class Query < ActiveRecord::Base
705 705 end
706 706 end
707 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 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 711 when "><t-"
712 712 # between today - n days and today
713 713 sql = relative_date_clause(db_table, db_field, - value.first.to_i, 0)
@@ -769,9 +769,9 class Query < ActiveRecord::Base
769 769 date = Date.today
770 770 sql = date_clause(db_table, db_field, date.beginning_of_year, date.end_of_year)
771 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 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 775 else
776 776 raise "Unknown query operator #{operator}"
777 777 end
@@ -834,7 +834,7 class Query < ActiveRecord::Base
834 834 if self.class.default_timezone == :utc
835 835 from = from.utc
836 836 end
837 s << ("#{table}.#{field} > '%s'" % [connection.quoted_date(from)])
837 s << ("#{table}.#{field} > '%s'" % [self.class.connection.quoted_date(from)])
838 838 end
839 839 if to
840 840 if to.is_a?(Date)
@@ -843,7 +843,7 class Query < ActiveRecord::Base
843 843 if self.class.default_timezone == :utc
844 844 to = to.utc
845 845 end
846 s << ("#{table}.#{field} <= '%s'" % [connection.quoted_date(to)])
846 s << ("#{table}.#{field} <= '%s'" % [self.class.connection.quoted_date(to)])
847 847 end
848 848 s.join(' AND ')
849 849 end
@@ -25,7 +25,7 class Repository < ActiveRecord::Base
25 25 IDENTIFIER_MAX_LENGTH = 255
26 26
27 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 29 has_many :filechanges, :class_name => 'Change', :through => :changesets
30 30
31 31 serialize :extra_info
@@ -45,6 +45,7 class Repository < ActiveRecord::Base
45 45 validates_format_of :identifier, :with => /\A(?!\d+$)[a-z0-9\-_]*\z/, :allow_blank => true
46 46 # Checks if the SCM is enabled when creating a repository
47 47 validate :repo_create_validation, :on => :create
48 attr_protected :id
48 49
49 50 safe_attributes 'identifier',
50 51 'login',
@@ -264,7 +265,7 class Repository < ActiveRecord::Base
264 265 reorder("#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC").
265 266 limit(limit).
266 267 preload(:user).
267 all
268 to_a
268 269 else
269 270 filechanges.
270 271 where("path = ?", path.with_leading_slash).
@@ -313,7 +314,8 class Repository < ActiveRecord::Base
313 314 return @found_committer_users[committer] if @found_committer_users.has_key?(committer)
314 315
315 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 319 if c && c.user
318 320 user = c.user
319 321 elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
@@ -484,10 +486,10 class Repository < ActiveRecord::Base
484 486 ci = "#{table_name_prefix}changesets_issues#{table_name_suffix}"
485 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})")
488 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})")
490 connection.delete("DELETE 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})")
490 self.class.connection.delete("DELETE FROM #{ci} WHERE #{ci}.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})")
492 self.class.connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
491 493 clear_extra_info_of_changesets
492 494 end
493 495
@@ -199,7 +199,7 class Repository::Cvs < Repository
199 199 # Need to retrieve existing revision numbers to sort them as integers
200 200 sql = "SELECT revision FROM #{Changeset.table_name} "
201 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 203 @current_revision_number += 1
204 204 end
205 205 end
@@ -241,7 +241,7 class Repository::Git < Repository
241 241 def latest_changesets(path,rev,limit=10)
242 242 revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false)
243 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 245 end
246 246
247 247 def clear_extra_info_of_changesets
@@ -20,7 +20,7 require 'redmine/scm/adapters/mercurial_adapter'
20 20 class Repository::Mercurial < Repository
21 21 # sort changesets by revision number
22 22 has_many :changesets,
23 :order => "#{Changeset.table_name}.id DESC",
23 lambda {order("#{Changeset.table_name}.id DESC")},
24 24 :foreign_key => 'repository_id'
25 25
26 26 attr_protected :root_url
@@ -117,9 +117,10 class Repository::Mercurial < Repository
117 117 changesets.
118 118 includes(:user).
119 119 where(latest_changesets_cond(path, rev, limit)).
120 references(:user).
120 121 limit(limit).
121 122 order("#{Changeset.table_name}.id DESC").
122 all
123 to_a
123 124 end
124 125
125 126 def is_short_id_in_db?
@@ -42,7 +42,7 class Repository::Subversion < Repository
42 42 revisions = scm.revisions(path, rev, nil, :limit => limit)
43 43 if revisions
44 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 46 else
47 47 []
48 48 end
@@ -166,7 +166,7 class Role < ActiveRecord::Base
166 166
167 167 # Find all the roles that can be given to a project member
168 168 def self.find_all_givable
169 Role.givable.all
169 Role.givable.to_a
170 170 end
171 171
172 172 # Return the builtin 'non member' role. If the role doesn't exist,
@@ -86,6 +86,7 class Setting < ActiveRecord::Base
86 86 validates_numericality_of :value, :only_integer => true, :if => Proc.new { |setting|
87 87 (s = @@available_settings[setting.name]) && s['format'] == 'int'
88 88 }
89 attr_protected :id
89 90
90 91 # Hash used to cache setting values
91 92 @cached_settings = {}
@@ -142,6 +143,7 END_SRC
142 143 def self.set_from_params(name, params)
143 144 params = params.dup
144 145 params.delete_if {|v| v.blank? } if params.is_a?(Array)
146 params.symbolize_keys! if params.is_a?(Hash)
145 147
146 148 m = "#{name}_from_params"
147 149 if respond_to? m
@@ -35,7 +35,7 class TimeEntry < ActiveRecord::Base
35 35
36 36 acts_as_activity_provider :timestamp => "#{table_name}.created_on",
37 37 :author_key => :user_id,
38 :find_options => {:include => :project}
38 :scope => preload(:project)
39 39
40 40 validates_presence_of :user_id, :activity_id, :project_id, :hours, :spent_on
41 41 validates_numericality_of :hours, :allow_nil => true, :message => :invalid
@@ -45,13 +45,19 class TimeEntry < ActiveRecord::Base
45 45 validate :validate_time_entry
46 46
47 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 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 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 62 scope :spent_between, lambda {|from, to|
57 63 if from && to
@@ -42,7 +42,7 class TimeEntryQuery < Query
42 42 if project
43 43 principals += project.principals.sort
44 44 unless project.leaf?
45 subprojects = project.descendants.visible.all
45 subprojects = project.descendants.visible.to_a
46 46 if subprojects.any?
47 47 add_available_filter "subproject_id",
48 48 :type => :list_subprojects,
@@ -109,7 +109,8 class TimeEntryQuery < Query
109 109 where(statement).
110 110 order(order_option).
111 111 joins(joins_for_order_statement(order_option.join(','))).
112 includes(:activity)
112 includes(:activity).
113 references(:activity)
113 114 end
114 115
115 116 def sql_for_activity_id_field(field, operator, value)
@@ -18,6 +18,7
18 18 class Token < ActiveRecord::Base
19 19 belongs_to :user
20 20 validates_uniqueness_of :value
21 attr_protected :id
21 22
22 23 before_create :delete_previous_tokens, :generate_new_token
23 24
@@ -63,7 +63,7 class Tracker < ActiveRecord::Base
63 63 connection.select_rows("SELECT DISTINCT old_status_id, new_status_id FROM #{WorkflowTransition.table_name} WHERE tracker_id = #{id} AND type = 'WorkflowTransition'").
64 64 flatten.
65 65 uniq
66 @issue_statuses = IssueStatus.where(:id => ids).all.sort
66 @issue_statuses = IssueStatus.where(:id => ids).to_a.sort
67 67 end
68 68
69 69 def disabled_core_fields
@@ -92,7 +92,7 class Tracker < ActiveRecord::Base
92 92 # Returns the fields that are disabled for all the given trackers
93 93 def self.disabled_core_fields(trackers)
94 94 if trackers.present?
95 trackers.uniq.map(&:disabled_core_fields).reduce(:&)
95 trackers.map(&:disabled_core_fields).reduce(:&)
96 96 else
97 97 []
98 98 end
@@ -79,8 +79,8 class User < Principal
79 79 :after_remove => Proc.new {|user, group| group.user_removed(user)}
80 80 has_many :changesets, :dependent => :nullify
81 81 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
82 has_one :rss_token, :class_name => 'Token', :conditions => "action='feeds'"
83 has_one :api_token, :class_name => 'Token', :conditions => "action='api'"
82 has_one :rss_token, lambda {where "action='feeds'"}, :class_name => 'Token'
83 has_one :api_token, lambda {where "action='api'"}, :class_name => 'Token'
84 84 belongs_to :auth_source
85 85
86 86 scope :logged, lambda { where("#{User.table_name}.status <> #{STATUS_ANONYMOUS}") }
@@ -105,9 +105,13 class User < Principal
105 105 validates_length_of :firstname, :lastname, :maximum => 30
106 106 validates_format_of :mail, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :allow_blank => true
107 107 validates_length_of :mail, :maximum => MAIL_LENGTH_LIMIT, :allow_nil => true
108 validates_confirmation_of :password, :allow_nil => true
109 108 validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
110 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 116 before_create :set_mail_notification
113 117 before_save :generate_password_if_needed, :update_hashed_password
@@ -151,6 +155,15 class User < Principal
151 155 write_attribute(:mail, arg.to_s.strip)
152 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 167 def identity_url=(url)
155 168 if url.blank?
156 169 write_attribute(:identity_url, '')
@@ -496,10 +509,12 class User < Principal
496 509
497 510 hash = Hash.new([])
498 511
499 members = Member.joins(:project).
512 group_class = anonymous? ? GroupAnonymous : GroupNonMember
513 members = Member.joins(:project, :principal).
500 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)).
502 preload(:project, :roles)
515 where("#{Member.table_name}.user_id = ? OR (#{Project.table_name}.is_public = ? AND #{Principal.table_name}.type = ?)", self.id, true, group_class.name).
516 preload(:project, :roles).
517 to_a
503 518
504 519 members.reject! {|member| member.user_id != id && project_ids.include?(member.project_id)}
505 520 members.each do |member|
@@ -558,6 +573,8 class User < Principal
558 573 # Authorize if user is authorized on every element of the array
559 574 context.map {|project| allowed_to?(action, project, options, &block)}.reduce(:&)
560 575 end
576 elsif context
577 raise ArgumentError.new("#allowed_to? context argument must be a Project, an Array of projects or nil")
561 578 elsif options[:global]
562 579 # Admin users are always authorized
563 580 return true if admin?
@@ -710,17 +727,17 class User < Principal
710 727 return if self.id.nil?
711 728
712 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 731 Comment.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
715 732 Issue.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
716 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 735 JournalDetail.
719 736 where(["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s]).
720 737 update_all(['old_value = ?', substitute.id.to_s])
721 738 JournalDetail.
722 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 741 Message.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
725 742 News.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
726 743 # Remove private queries and keep public ones
@@ -33,11 +33,14 class Version < ActiveRecord::Base
33 33 validates :effective_date, :date => true
34 34 validates_inclusion_of :status, :in => VERSION_STATUSES
35 35 validates_inclusion_of :sharing, :in => VERSION_SHARINGS
36 attr_protected :id
36 37
37 38 scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)}
38 39 scope :open, lambda { where(:status => 'open') }
39 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 46 safe_attributes 'name',
@@ -230,11 +233,6 class Version < ActiveRecord::Base
230 233 end
231 234 end
232 235
233 # Returns true if the version is shared, otherwise false
234 def shared?
235 sharing != 'none'
236 end
237
238 236 private
239 237
240 238 def load_issue_counts
@@ -22,6 +22,7 class Watcher < ActiveRecord::Base
22 22 validates_presence_of :user
23 23 validates_uniqueness_of :user_id, :scope => [:watchable_type, :watchable_id]
24 24 validate :validate_user
25 attr_protected :id
25 26
26 27 # Returns true if at least one object among objects is watched by user
27 28 def self.any_watched?(objects, user)
@@ -18,13 +18,14
18 18 class Wiki < ActiveRecord::Base
19 19 include Redmine::SafeAttributes
20 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 22 has_many :redirects, :class_name => 'WikiRedirect', :dependent => :delete_all
23 23
24 24 acts_as_watchable
25 25
26 26 validates_presence_of :start_page
27 27 validates_format_of :start_page, :with => /\A[^,\.\/\?\;\|\:]*\z/
28 attr_protected :id
28 29
29 30 safe_attributes 'start_page'
30 31
@@ -23,6 +23,7 class WikiContent < ActiveRecord::Base
23 23 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
24 24 validates_presence_of :text
25 25 validates_length_of :comments, :maximum => 255, :allow_nil => true
26 attr_protected :id
26 27
27 28 acts_as_versioned
28 29
@@ -68,13 +69,13 class WikiContent < ActiveRecord::Base
68 69 :timestamp => "#{WikiContent.versioned_table_name}.updated_on",
69 70 :author_key => "#{WikiContent.versioned_table_name}.author_id",
70 71 :permission => :view_wiki_edits,
71 :find_options => {: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}.page_id, #{WikiContent.versioned_table_name}.author_id, " +
74 "#{WikiContent.versioned_table_name}.id",
75 :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 #{Project.table_name} ON #{Project.table_name}.id = #{Wiki.table_name}.project_id"}
72 :scope => select("#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
73 "#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title, " +
74 "#{WikiContent.versioned_table_name}.page_id, #{WikiContent.versioned_table_name}.author_id, " +
75 "#{WikiContent.versioned_table_name}.id").
76 joins("LEFT JOIN #{WikiPage.table_name} ON #{WikiPage.table_name}.id = #{WikiContent.versioned_table_name}.page_id " +
77 "LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id " +
78 "LEFT JOIN #{Project.table_name} ON #{Project.table_name}.id = #{Wiki.table_name}.project_id")
78 79
79 80 after_destroy :page_update_after_destroy
80 81
@@ -104,7 +105,7 class WikiContent < ActiveRecord::Base
104 105 # uncompressed data
105 106 data
106 107 end
107 str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
108 str.force_encoding("UTF-8")
108 109 str
109 110 end
110 111 end
@@ -33,7 +33,7 class WikiPage < ActiveRecord::Base
33 33 :url => Proc.new {|o| {:controller => 'wiki', :action => 'show', :project_id => o.wiki.project, :id => o.title}}
34 34
35 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 37 :permission => :view_wiki_pages,
38 38 :project_key => "#{Wiki.table_name}.project_id"
39 39
@@ -43,6 +43,7 class WikiPage < ActiveRecord::Base
43 43 validates_format_of :title, :with => /\A[^,\.\/\?\;\|\s]*\z/
44 44 validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false
45 45 validates_associated :content
46 attr_protected :id
46 47
47 48 validate :validate_parent_title
48 49 before_destroy :remove_redirects
@@ -180,12 +181,10 class WikiPage < ActiveRecord::Base
180 181 def save_with_content(content)
181 182 ret = nil
182 183 transaction do
183 self.content = content
184 if new_record?
185 # Rails automatically saves associated content
186 ret = save
187 else
188 ret = save && (content.text_changed? ? content.save : true)
184 ret = save
185 if content.text_changed?
186 self.content = content
187 ret = ret && content.changed?
189 188 end
190 189 raise ActiveRecord::Rollback unless ret
191 190 end
@@ -20,4 +20,5 class WikiRedirect < ActiveRecord::Base
20 20
21 21 validates_presence_of :title, :redirects_to
22 22 validates_length_of :title, :redirects_to, :maximum => 255
23 attr_protected :id
23 24 end
@@ -24,6 +24,7 class WorkflowRule < ActiveRecord::Base
24 24 belongs_to :new_status, :class_name => 'IssueStatus', :foreign_key => 'new_status_id'
25 25
26 26 validates_presence_of :role, :tracker, :old_status
27 attr_protected :id
27 28
28 29 # Copies workflows from source to targets
29 30 def self.copy(source_tracker, source_role, target_trackers, target_roles)
@@ -34,7 +35,7 class WorkflowRule < ActiveRecord::Base
34 35 target_trackers = [target_trackers].flatten.compact
35 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 39 target_roles = Role.all if target_roles.empty?
39 40
40 41 target_trackers.each do |target_tracker|
@@ -40,7 +40,7 class WorkflowTransition < WorkflowRule
40 40 roles = Array.wrap roles
41 41
42 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 45 transitions.each do |old_status_id, transitions_by_new_status|
46 46 transitions_by_new_status.each do |new_status_id, transition_by_rule|
@@ -8,8 +8,8
8 8 <% end -%>
9 9 </ul>
10 10 <div class="tabs-buttons" style="display:none;">
11 <button class="tab-left" type="button" onclick="moveTabLeft(this);"></button>
12 <button class="tab-right" type="button" onclick="moveTabRight(this);"></button>
11 <button class="tab-left" onclick="moveTabLeft(this); return false;"></button>
12 <button class="tab-right" onclick="moveTabRight(this); return false;"></button>
13 13 </div>
14 14 </div>
15 15
@@ -1,5 +1,5
1 1 <% roles = Role.find_all_givable %>
2 <% projects = Project.active.all %>
2 <% projects = Project.active.to_a %>
3 3
4 4 <div class="splitcontentleft">
5 5 <% if @group.memberships.any? %>
@@ -1,6 +1,6
1 1 <%= error_messages_for 'member' %>
2 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 5 <div class="splitcontentleft">
6 6 <% if members.any? %>
@@ -8,7 +8,7
8 8 <% elsif params[:issue_id] %>
9 9 <%= hidden_field_tag 'issue_id', params[:issue_id] %>
10 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 12 <% end %>
13 13 <% end %>
14 14 <p>
@@ -1,5 +1,5
1 1 <% roles = Role.find_all_givable %>
2 <% projects = Project.active.all %>
2 <% projects = Project.active.to_a %>
3 3
4 4 <div class="splitcontentleft">
5 5 <% if @user.memberships.any? %>
@@ -23,7 +23,7
23 23 <%= fp.select :parent_id,
24 24 content_tag('option', '', :value => '') +
25 25 wiki_page_options_for_select(
26 @wiki.pages.includes(:parent).all -
26 @wiki.pages.includes(:parent).to_a -
27 27 @page.self_and_descendants, @page.parent) %>
28 28 </p>
29 29 <% end %>
@@ -13,7 +13,7
13 13 <p><%= f.select :parent_id,
14 14 content_tag('option', '', :value => '') +
15 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 17 @page.parent),
18 18 :label => :field_parent_title %></p>
19 19 </div>
@@ -2,12 +2,7 require File.expand_path('../boot', __FILE__)
2 2
3 3 require 'rails/all'
4 4
5 if defined?(Bundler)
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
5 Bundler.require(*Rails.groups)
11 6
12 7 module RedmineApp
13 8 class Application < Rails::Application
@@ -33,7 +28,7 module RedmineApp
33 28 # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
34 29 # config.i18n.default_locale = :de
35 30
36 I18n.enforce_available_locales = false
31 I18n.enforce_available_locales = true
37 32
38 33 # Configure the default encoding used in templates for Ruby 1.9.
39 34 config.encoding = "utf-8"
@@ -1,5 +1,3
1 require 'rubygems'
2
3 1 # Set up gems listed in the Gemfile.
4 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
2 # with MySQL and ruby1.8, replace the adapter name with `mysql`.
1 # Default setup is given for MySQL with ruby1.9.
3 2 # Examples for PostgreSQL, SQLite3 and SQL Server can be found at the end.
4 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 2 require File.expand_path('../application', __FILE__)
3 3
4 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 10 exit 1
11 11 end
12 12
13 # Initialize the rails application
14 RedmineApp::Application.initialize!
13 # Initialize the Rails application
14 Rails.application.initialize!
@@ -1,19 +1,21
1 # Settings specified here will take precedence over those in config/application.rb
2 RedmineApp::Application.configure do
1 Rails.application.configure do
2 # Settings specified here will take precedence over those in config/application.rb
3
3 4 # In the development environment your application's code is reloaded on
4 5 # every request. This slows down response time but is perfect for development
5 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 config.whiny_nils = true
9 # Do not eager load code on boot.
10 config.eager_load = false
10 11
11 12 # Show full error reports and disable caching
12 #config.action_controller.consider_all_requests_local = true
13 config.action_controller.perform_caching = false
13 config.consider_all_requests_local = true
14 config.action_controller.perform_caching = false
14 15
15 # Don't care if the mailer can't send
16 # Disable delivery errors
16 17 config.action_mailer.raise_delivery_errors = false
17 18
19 # Print deprecation notices to stderr and the Rails logger.
18 20 config.active_support.deprecation = [:stderr, :log]
19 21 end
@@ -1,33 +1,25
1 # Settings specified here will take precedence over those in config/application.rb
2 RedmineApp::Application.configure do
3 # The production environment is meant for finished, "live" apps.
4 # Code is not reloaded between requests
1 Rails.application.configure do
2 # Settings specified here will take precedence over those in config/application.rb
3
4 # Code is not reloaded between requests.
5 5 config.cache_classes = true
6 6
7 #####
8 # Customize the default logger
9 # http://www.ruby-doc.org/stdlib-1.8.7/libdoc/logger/rdoc/Logger.html
10 #
11 # Use a different logger for distributed setups
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
7 # Eager load code on boot. This eager loads most of Rails and
8 # your application in memory, allowing both threaded web servers
9 # and those relying on copy on write to perform better.
10 # Rake tasks automatically ignore this option for performance.
11 config.eager_load = true
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 15 config.action_controller.perform_caching = true
22 16
23 # Enable serving of images, stylesheets, and javascripts from an asset server
24 # config.action_controller.asset_host = "http://assets.example.com"
25
26 # Disable delivery errors if you bad email addresses should just be ignored
17 # Disable delivery errors
27 18 config.action_mailer.raise_delivery_errors = false
28 19
29 20 # No email in production log
30 21 config.action_mailer.logger = nil
31 22
23 # Print deprecation notices to the Rails logger.
32 24 config.active_support.deprecation = :log
33 25 end
@@ -1,16 +1,19
1 # Settings specified here will take precedence over those in config/application.rb
2 RedmineApp::Application.configure do
1 Rails.application.configure do
2 # Settings specified here will take precedence over those in config/application.rb
3
3 4 # The test environment is used exclusively to run your application's
4 5 # test suite. You never need to work with it otherwise. Remember that
5 6 # your test database is "scratch space" for the test suite and is wiped
6 7 # and recreated between test runs. Don't rely on the data there!
7 8 config.cache_classes = true
8 9
9 # Log error messages when you accidentally call methods on nil.
10 config.whiny_nils = true
10 # Do not eager load code on boot. This avoids loading your whole application
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 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 17 config.action_controller.perform_caching = false
15 18
16 19 config.action_mailer.perform_deliveries = true
@@ -20,10 +23,10 RedmineApp::Application.configure do
20 23 # ActionMailer::Base.deliveries array.
21 24 config.action_mailer.delivery_method = :test
22 25
23 # Skip protect_from_forgery in requests
24 # http://m.onkey.org/2007/9/28/csrf-protection-for-your-existing-rails-application
26 # Disable request forgery protection in test environment.
25 27 config.action_controller.allow_forgery_protection = false
26 28
29 # Print deprecation notices to stderr and the Rails logger.
27 30 config.active_support.deprecation = [:stderr, :log]
28 31
29 32 config.secret_token = 'a secret token for running the tests'
@@ -1,1 +1,2
1 # Same as test.rb
1 2 instance_eval File.read(File.join(File.dirname(__FILE__), 'test.rb'))
@@ -1,1 +1,2
1 # Same as test.rb
1 2 instance_eval File.read(File.join(File.dirname(__FILE__), 'test.rb'))
@@ -54,56 +54,23 module ActionView
54 54 end
55 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 57 ActionView::Base.field_error_proc = Proc.new{ |html_tag, instance| html_tag || ''.html_safe }
93 58
94 59 # HTML5: <option value=""></option> is invalid, use <option value="">&nbsp;</option> instead
95 60 module ActionView
96 61 module Helpers
97 class InstanceTag
98 private
99 def add_options_with_non_empty_blank_option(option_tags, options, value = nil)
100 if options[:include_blank] == true
101 options = options.dup
102 options[:include_blank] = '&nbsp;'.html_safe
62 module Tags
63 class Base
64 private
65 def add_options_with_non_empty_blank_option(option_tags, options, value = nil)
66 if options[:include_blank] == true
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 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 73 end
106 alias_method_chain :add_options, :non_empty_blank_option
107 74 end
108 75
109 76 module FormTagHelper
@@ -1,5 +1,7
1 1 I18n.default_locale = 'en'
2 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 6 require 'redmine'
5 7
@@ -20,9 +22,3 Redmine::Plugin.load
20 22 unless Redmine::Configuration['mirror_plugins_assets_on_startup'] == false
21 23 Redmine::Plugin.mirror_assets
22 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 249 field_is_for_all: 全プロジェクト向け
250 250 field_possible_values: 選択肢
251 251 field_regexp: 正規表現
252 field_min_length: 短長
253 field_max_length: 最大
252 field_min_length: 小値
253 field_max_length: 最大
254 254 field_value:
255 255 field_category: カテゴリ
256 256 field_title: タイトル
@@ -305,7 +305,7 ja:
305 305 field_activity: 活動
306 306 field_spent_on: 日付
307 307 field_identifier: 識別子
308 field_is_filter: フィルタとして使
308 field_is_filter: フィルタとして使
309 309 field_issue_to: 関連するチケット
310 310 field_delay: 遅延
311 311 field_assignable: このロールにチケットを割り当て可能
@@ -314,7 +314,7 ja:
314 314 field_column_names: 項目
315 315 field_time_entries: 時間を記録
316 316 field_time_zone: タイムゾーン
317 field_searchable: 検索対象
317 field_searchable: 検索条件に設定可能とする
318 318 field_default_value: デフォルト値
319 319 field_comments_sorting: コメントの表示順
320 320 field_parent_title: 親ページ
@@ -541,7 +541,7 ja:
541 541 label_subproject_plural: サブプロジェクト
542 542 label_subproject_new: 新しいサブプロジェクト
543 543 label_and_its_subprojects: "%{value} とサブプロジェクト"
544 label_min_max_length: - 最大長
544 label_min_max_length: 小値 - 最大値の
545 545 label_list: リストから選択
546 546 label_date: 日付
547 547 label_integer: 整数
@@ -997,7 +997,6
997 997 label_only: 僅於
998 998 label_drop_down_list: 下拉式清單
999 999 label_checkboxes: 核取方塊
1000 label_radio_buttons: 選項按鈕
1001 1000 label_link_values_to: 連結欄位值至此網址
1002 1001 label_custom_field_select_type: 請選擇連結此自訂欄位的物件類型
1003 1002 label_check_for_updates: 檢查更新
@@ -1196,3 +1195,4
1196 1195 description_date_from: 輸入起始日期
1197 1196 description_date_to: 輸入結束日期
1198 1197 text_repository_identifier_info: '僅允許使用小寫英文字母 (a-z), 阿拉伯數字, 虛線與底線。<br />一旦儲存之後, 代碼便無法再次被更改。'
1198 label_radio_buttons: radio buttons
@@ -15,7 +15,7
15 15 # along with this program; if not, write to the Free Software
16 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 19 root :to => 'welcome#index', :as => 'home'
20 20
21 21 match 'login', :to => 'account#login', :as => 'signin', :via => [:get, :post]
@@ -25,10 +25,10 RedmineApp::Application.routes.draw do
25 25 match 'account/activate', :to => 'account#activate', :via => :get
26 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]
29 match '/issues/preview/new/:project_id', :to => 'previews#issue', :as => 'preview_new_issue', :via => [:get, :post, :put]
30 match '/issues/preview/edit/:id', :to => 'previews#issue', :as => 'preview_edit_issue', :via => [:get, :post, :put]
31 match '/issues/preview', :to => 'previews#issue', :as => 'preview_issue', :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, :patch]
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, :patch]
32 32
33 33 match 'projects/:id/wiki', :to => 'wikis#edit', :via => :post
34 34 match 'projects/:id/wiki/destroy', :to => 'wikis#destroy', :via => [:get, :post]
@@ -74,7 +74,7 RedmineApp::Application.routes.draw do
74 74 match 'my/order_blocks', :controller => 'my', :action => 'order_blocks', :via => :post
75 75
76 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 78 match 'users/:id/memberships/:membership_id', :to => 'users#destroy_membership', :via => :delete
79 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 113 get 'issues/:copy_from/copy', :to => 'issues#new', :as => 'copy_issue'
114 114 resources :issues, :only => [:index, :new, :create]
115 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 118 resources :files, :only => [:index, :new, :create]
119 119
@@ -151,7 +151,7 RedmineApp::Application.routes.draw do
151 151 post 'rename'
152 152 get 'history'
153 153 get 'diff'
154 match 'preview', :via => [:post, :put]
154 match 'preview', :via => [:post, :put, :patch]
155 155 post 'protect'
156 156 post 'add_attachment'
157 157 end
@@ -17,7 +17,10
17 17
18 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 24 # model removed
22 25 class Permission < ActiveRecord::Base; end
23 26
@@ -4,7 +4,7 class AddEnumerationsPosition < ActiveRecord::Migration
4 4 Enumeration.all.group_by(&:opt).each do |opt, enums|
5 5 enums.each_with_index do |enum, i|
6 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 8 end
9 9 end
10 10 end
@@ -4,7 +4,7 class AddCustomFieldsPosition < ActiveRecord::Migration
4 4 CustomField.all.group_by(&:type).each do |t, fields|
5 5 fields.each_with_index do |field, i|
6 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 8 end
9 9 end
10 10 end
@@ -7,7 +7,7 class PopulateChangesetsUserId < ActiveRecord::Migration
7 7 username, email = $1.strip, $3
8 8 u = User.find_by_login(username)
9 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 11 end
12 12 end
13 13 end
@@ -5,8 +5,8 class RemoveEnumerationsOpt < ActiveRecord::Migration
5 5
6 6 def self.down
7 7 add_column :enumerations, :opt, :string, :limit => 4, :default => '', :null => false
8 Enumeration.update_all("opt = 'IPRI'", "type = 'IssuePriority'")
9 Enumeration.update_all("opt = 'DCAT'", "type = 'DocumentCategory'")
10 Enumeration.update_all("opt = 'ACTI'", "type = 'TimeEntryActivity'")
8 Enumeration.where("type = 'IssuePriority'").update_all("opt = 'IPRI'")
9 Enumeration.where("type = 'DocumentCategory'").update_all("opt = 'DCAT'")
10 Enumeration.where("type = 'TimeEntryActivity'").update_all("opt = 'ACTI'")
11 11 end
12 12 end
@@ -1,6 +1,6
1 1 class EnableCalendarAndGanttModulesWhereAppropriate < ActiveRecord::Migration
2 2 def self.up
3 EnabledModule.where(:name => 'issue_tracking').all.each do |e|
3 EnabledModule.where(:name => 'issue_tracking').each do |e|
4 4 EnabledModule.create(:name => 'calendar', :project_id => e.project_id)
5 5 EnabledModule.create(:name => 'gantt', :project_id => e.project_id)
6 6 end
@@ -15,7 +15,8 class PopulateIssuesClosedOn < ActiveRecord::Migration
15 15
16 16 # Then set closed_on for closed issues that weren't up updated by the above UPDATE
17 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 20 end
20 21 end
21 22
@@ -86,7 +86,7 http://www.redmine.org/
86 86 * Defect #16655: start_date not set despite settings[default_issue_start_date_to_creation_date] being set.
87 87 * Defect #16668: Redmine links broken when object name contains special characters
88 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 90 * Defect #16739: custom_fields.json only returns single tracker instead of array of trackers
91 91 * Defect #16747: Remove useless settings when editing a query from the gantt
92 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 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 11 * RubyGems
12 12 * Bundler >= 1.0.21
13 13
@@ -30,9 +30,6 Optional:
30 30 3. Configure the database parameters in config/database.yml
31 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 33 4. Install the required gems by running:
37 34 bundle install --without development test
38 35
@@ -29,7 +29,7 module Redmine
29 29 send :include, Redmine::Acts::ActivityProvider::InstanceMethods
30 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 33 self.activity_provider_options ||= {}
34 34
35 35 # One model can provide different event types
@@ -54,7 +54,7 module Redmine
54 54 provider_options = activity_provider_options[event_type]
55 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 59 if from && to
60 60 scope = scope.where("#{provider_options[:timestamp]} BETWEEN ? AND ?", from, to)
@@ -79,7 +79,7 module Redmine
79 79 scope = scope.where(Project.allowed_to_condition(user, "view_#{self.name.underscore.pluralize}".to_sym, options))
80 80 end
81 81
82 scope.all(provider_options[:find_options].dup)
82 scope.to_a
83 83 end
84 84 end
85 85 end
@@ -29,9 +29,8 module Redmine
29 29 attachable_options[:view_permission] = options.delete(:view_permission) || "view_#{self.name.pluralize.underscore}".to_sym
30 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,
33 :order => "#{Attachment.table_name}.created_on ASC, #{Attachment.table_name}.id ASC",
34 :dependent => :destroy)
32 has_many :attachments, lambda {order("#{Attachment.table_name}.created_on ASC, #{Attachment.table_name}.id ASC")},
33 options.merge(:as => :container, :dependent => :destroy)
35 34 send :include, Redmine::Acts::Attachable::InstanceMethods
36 35 before_save :attach_saved_attachments
37 36 end
@@ -27,9 +27,8 module Redmine
27 27 return if self.included_modules.include?(Redmine::Acts::Customizable::InstanceMethods)
28 28 cattr_accessor :customizable_options
29 29 self.customizable_options = options
30 has_many :custom_values, :as => :customized,
31 :include => :custom_field,
32 :order => "#{CustomField.table_name}.position",
30 has_many :custom_values, lambda {includes(:custom_field).order("#{CustomField.table_name}.position")},
31 :as => :customized,
33 32 :dependent => :delete_all,
34 33 :validate => false
35 34
@@ -46,7 +45,7 module Redmine
46 45 end
47 46
48 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 49 end
51 50
52 51 # Sets the values of the object's custom fields
@@ -31,6 +31,7 module Redmine
31 31 # * :permission - permission required to search the model (default to :view_"objects")
32 32 def acts_as_searchable(options = {})
33 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 36 cattr_accessor :searchable_options
36 37 self.searchable_options = options
@@ -70,7 +71,7 module Redmine
70 71 # TODO: make user an argument
71 72 user = User.current
72 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 76 limit_options = {}
76 77 limit_options[:limit] = options[:limit] if options[:limit]
@@ -100,7 +101,10 module Redmine
100 101
101 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 108 project_conditions = []
105 109 if searchable_options.has_key?(:permission)
106 110 project_conditions << Project.allowed_to_condition(user, searchable_options[:permission] || :view_project)
@@ -118,10 +122,11 module Redmine
118 122 results_count = 0
119 123
120 124 scope = scope.
121 includes(searchable_options[:include]).
125 joins(searchable_options[:include]).
122 126 order("#{searchable_options[:order_column]} " + (options[:before] ? 'DESC' : 'ASC')).
123 127 where(project_conditions).
124 where(tokens_conditions)
128 where(tokens_conditions).
129 uniq
125 130
126 131 results_count = scope.count
127 132
@@ -129,7 +134,7 module Redmine
129 134 if options[:offset]
130 135 scope_with_limit = scope_with_limit.where("#{searchable_options[:date_column]} #{options[:before] ? '<' : '>'} ?", options[:offset])
131 136 end
132 results = scope_with_limit.all
137 results = scope_with_limit.to_a
133 138
134 139 [results, results_count]
135 140 end
@@ -44,7 +44,7 module ActiveRecord
44 44 configuration.update(options) if options.is_a?(Hash)
45 45
46 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 49 scope :roots, lambda {
50 50 where("#{configuration[:foreign_key]} IS NULL").
@@ -68,7 +68,7 module Redmine
68 68 end
69 69
70 70 def notified_watchers
71 notified = watcher_users.active
71 notified = watcher_users.active.to_a
72 72 notified.reject! {|user| user.mail.blank? || user.mail_notification == 'none'}
73 73 if respond_to?(:visible?)
74 74 notified.reject! {|user| !visible?(user)}
@@ -63,12 +63,7 require 'redmine/themes'
63 63 require 'redmine/hook'
64 64 require 'redmine/plugin'
65 65
66 if RUBY_VERSION < '1.9'
67 require 'fastercsv'
68 else
69 require 'csv'
70 FCSV = CSV
71 end
66 require 'csv'
72 67
73 68 Redmine::Scm::Base.add "Subversion"
74 69 Redmine::Scm::Base.add "Darcs"
@@ -58,9 +58,11 module Redmine
58 58 if action.is_a?(Symbol)
59 59 perm = permission(action)
60 60 !perm.nil? && perm.read?
61 else
61 elsif action.is_a?(Hash)
62 62 s = "#{action[:controller]}/#{action[:action]}"
63 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 66 end
65 67 end
66 68
@@ -1,160 +1,71
1 if RUBY_VERSION < '1.9'
2 require 'iconv'
3 end
4 1
5 2 module Redmine
6 3 module CodesetUtil
7 4
8 5 def self.replace_invalid_utf8(str)
9 6 return str if str.nil?
10 if str.respond_to?(:force_encoding)
11 str.force_encoding('UTF-8')
12 if ! str.valid_encoding?
13 str = str.encode("US-ASCII", :invalid => :replace,
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
7 str.force_encoding('UTF-8')
8 if ! str.valid_encoding?
9 str = str.encode("US-ASCII", :invalid => :replace,
10 :undef => :replace, :replace => '?').encode("UTF-8")
36 11 end
37 12 str
38 13 end
39 14
40 15 def self.to_utf8(str, encoding)
41 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 18 if str.empty?
44 str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
19 str.force_encoding("UTF-8")
45 20 return str
46 21 end
47 22 enc = encoding.blank? ? "UTF-8" : encoding
48 if str.respond_to?(:force_encoding)
49 if enc.upcase != "UTF-8"
50 str.force_encoding(enc)
51 str = str.encode("UTF-8", :invalid => :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
23 if enc.upcase != "UTF-8"
24 str.force_encoding(enc)
25 str = str.encode("UTF-8", :invalid => :replace,
26 :undef => :replace, :replace => '?')
67 27 else
68 ic = Iconv.new('UTF-8', enc)
69 txtar = ""
70 begin
71 txtar += ic.iconv(str)
72 rescue Iconv::IllegalSequence
73 txtar += $!.success
74 str = '?' + $!.failed[1,$!.failed.length]
75 retry
76 rescue
77 txtar += $!.success
28 str.force_encoding("UTF-8")
29 if ! str.valid_encoding?
30 str = str.encode("US-ASCII", :invalid => :replace,
31 :undef => :replace, :replace => '?').encode("UTF-8")
78 32 end
79 str = txtar
80 33 end
81 34 str
82 35 end
83 36
84 37 def self.to_utf8_by_setting(str)
85 38 return str if str.nil?
86 str = self.to_utf8_by_setting_internal(str)
87 if str.respond_to?(:force_encoding)
88 str.force_encoding('UTF-8')
89 end
90 str
39 self.to_utf8_by_setting_internal(str).force_encoding('UTF-8')
91 40 end
92 41
93 42 def self.to_utf8_by_setting_internal(str)
94 43 return str if str.nil?
95 if str.respond_to?(:force_encoding)
96 str.force_encoding('ASCII-8BIT')
97 end
44 str.force_encoding('ASCII-8BIT')
98 45 return str if str.empty?
99 46 return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
100 if str.respond_to?(:force_encoding)
101 str.force_encoding('UTF-8')
102 end
47 str.force_encoding('UTF-8')
103 48 encodings = Setting.repositories_encodings.split(',').collect(&:strip)
104 49 encodings.each do |encoding|
105 if str.respond_to?(:force_encoding)
106 begin
107 str.force_encoding(encoding)
108 utf8 = str.encode('UTF-8')
109 return utf8 if utf8.valid_encoding?
110 rescue
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
50 begin
51 str.force_encoding(encoding)
52 utf8 = str.encode('UTF-8')
53 return utf8 if utf8.valid_encoding?
54 rescue
55 # do nothing here and try the next encoding
119 56 end
120 57 end
121 str = self.replace_invalid_utf8(str)
122 if str.respond_to?(:force_encoding)
123 str.force_encoding('UTF-8')
124 end
125 str
58 self.replace_invalid_utf8(str).force_encoding('UTF-8')
126 59 end
127 60
128 61 def self.from_utf8(str, encoding)
129 62 str ||= ''
130 if str.respond_to?(:force_encoding)
131 str.force_encoding('UTF-8')
132 if encoding.upcase != 'UTF-8'
133 str = str.encode(encoding, :invalid => :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
63 str.force_encoding('UTF-8')
64 if encoding.upcase != 'UTF-8'
65 str = str.encode(encoding, :invalid => :replace,
66 :undef => :replace, :replace => '?')
145 67 else
146 ic = Iconv.new(encoding, 'UTF-8')
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
68 str = self.replace_invalid_utf8(str)
158 69 end
159 70 end
160 71 end
@@ -15,35 +15,10
15 15 # along with this program; if not, write to the Free Software
16 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 18 class DateValidator < ActiveModel::EachValidator
43 19 def validate_each(record, attribute, value)
44 20 before_type_cast = record.attributes_before_type_cast[attribute.to_s]
45 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 22 unless before_type_cast =~ /\A\d{4}-\d{2}-\d{2}( 00:00:00)?\z/ && value
48 23 record.errors.add attribute, :not_a_date
49 24 end
@@ -36,13 +36,6 module Redmine #:nodoc:
36 36 s.gsub!(',', '.')
37 37 begin; Kernel.Float(s); rescue; nil; end
38 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 39 end
47 40 end
48 41 end
@@ -678,9 +678,7 module Redmine
678 678 def self.rdm_from_utf8(txt, encoding)
679 679 txt ||= ''
680 680 txt = Redmine::CodesetUtil.from_utf8(txt, encoding)
681 if txt.respond_to?(:force_encoding)
682 txt.force_encoding('ASCII-8BIT')
683 end
681 txt.force_encoding('ASCII-8BIT')
684 682 txt
685 683 end
686 684
@@ -597,18 +597,13 module Redmine
597 597 def target_class
598 598 @target_class ||= self.class.name[/^(.*::)?(.+)Format$/, 2].constantize rescue nil
599 599 end
600
601 def reset_target_class
602 @target_class = nil
603 end
604 600
605 601 def possible_custom_value_options(custom_value)
606 602 options = possible_values_options(custom_value.custom_field, custom_value.customized)
607 603 missing = [custom_value.value_was].flatten.reject(&:blank?) - options.map(&:last)
608 604 if missing.any?
609 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
611 options = options.sort_by(&:first)
606 options.sort_by!(&:first)
612 607 end
613 608 options
614 609 end
@@ -167,7 +167,7 module Redmine
167 167 where("child.id IN (?)", ids).
168 168 order("#{Project.table_name}.lft ASC").
169 169 uniq.
170 all
170 to_a
171 171 else
172 172 @projects = []
173 173 end
@@ -193,7 +193,7 module Redmine
193 193 # * Checking the url target (project only)
194 194 # * Checking the conditions of the item
195 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 197 return false
198 198 end
199 199 if node.condition && !node.condition.call(project)
@@ -18,17 +18,13
18 18 require 'cgi'
19 19 require 'redmine/scm/adapters'
20 20
21 if RUBY_VERSION < '1.9'
22 require 'iconv'
23 end
24
25 21 module Redmine
26 22 module Scm
27 23 module Adapters
28 24 class AbstractAdapter #:nodoc:
29 25
30 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 29 class << self
34 30 def client_command
@@ -288,21 +284,12 module Redmine
288 284 def scm_iconv(to, from, str)
289 285 return nil if str.nil?
290 286 return str if to == from
291 if str.respond_to?(:force_encoding)
292 str.force_encoding(from)
293 begin
294 str.encode(to)
295 rescue Exception => err
296 logger.error("failed to convert from #{from} to #{to}. #{err}")
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
287 str.force_encoding(from)
288 begin
289 str.encode(to)
290 rescue Exception => err
291 logger.error("failed to convert from #{from} to #{to}. #{err}")
292 nil
306 293 end
307 294 end
308 295
@@ -43,10 +43,7 module Redmine
43 43 end
44 44
45 45 def scm_command_version
46 scm_version = scm_version_from_command_line.dup
47 if scm_version.respond_to?(:force_encoding)
48 scm_version.force_encoding('ASCII-8BIT')
49 end
46 scm_version = scm_version_from_command_line.dup.force_encoding('ASCII-8BIT')
50 47 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)})
51 48 m[2].scan(%r{\d+}).collect(&:to_i)
52 49 end
@@ -100,7 +97,7 module Redmine
100 97 prefix_utf8 = "#{url}/#{path}".gsub('\\', '/')
101 98 logger.debug "PREFIX: #{prefix_utf8}"
102 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 101 re = %r{^V\s+(#{Regexp.escape(prefix)})?(\/?)([^\/]+)(\/?)\s+(\S+)\r?$}
105 102 io.each_line do |line|
106 103 next unless line =~ re
@@ -43,10 +43,7 module Redmine
43 43 end
44 44
45 45 def scm_command_version
46 scm_version = scm_version_from_command_line.dup
47 if scm_version.respond_to?(:force_encoding)
48 scm_version.force_encoding('ASCII-8BIT')
49 end
46 scm_version = scm_version_from_command_line.dup.force_encoding('ASCII-8BIT')
50 47 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)}m)
51 48 m[2].scan(%r{\d+}).collect(&:to_i)
52 49 end
@@ -94,7 +91,7 module Redmine
94 91 def entries(path=nil, identifier=nil, options={})
95 92 logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'"
96 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 95 entries = Entries.new
99 96 cmd_args = %w|-q rls -e|
100 97 cmd_args << "-D" << time_to_cvstime_rlog(identifier) if identifier
@@ -171,6 +168,7 module Redmine
171 168 file_state = nil
172 169 branch_map = nil
173 170 io.each_line() do |line|
171 line = line.strip
174 172 if state != "revision" && /^#{ENDLOG}/ =~ line
175 173 commit_log = String.new
176 174 revision = nil
@@ -43,10 +43,7 module Redmine
43 43 end
44 44
45 45 def darcs_binary_version
46 darcsversion = darcs_binary_version_from_command_line.dup
47 if darcsversion.respond_to?(:force_encoding)
48 darcsversion.force_encoding('ASCII-8BIT')
49 end
46 darcsversion = darcs_binary_version_from_command_line.dup.force_encoding('ASCII-8BIT')
50 47 if m = darcsversion.match(%r{\A(.*?)((\d+\.)+\d+)})
51 48 m[2].scan(%r{\d+}).collect(&:to_i)
52 49 end
@@ -47,10 +47,7 module Redmine
47 47 end
48 48
49 49 def scm_command_version
50 scm_version = scm_version_from_command_line.dup
51 if scm_version.respond_to?(:force_encoding)
52 scm_version.force_encoding('ASCII-8BIT')
53 end
50 scm_version = scm_version_from_command_line.dup.force_encoding('ASCII-8BIT')
54 51 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)})
55 52 m[2].scan(%r{\d+}).collect(&:to_i)
56 53 end
@@ -145,10 +142,7 module Redmine
145 142 type = $1
146 143 sha = $2
147 144 size = $3
148 name = $4
149 if name.respond_to?(:force_encoding)
150 name.force_encoding(@path_encoding)
151 end
145 name = $4.force_encoding(@path_encoding)
152 146 full_path = p.empty? ? name : "#{p}/#{name}"
153 147 n = scm_iconv('UTF-8', @path_encoding, name)
154 148 full_p = scm_iconv('UTF-8', @path_encoding, full_path)
@@ -54,10 +54,7 module Redmine
54 54 # The hg version is expressed either as a
55 55 # release number (eg 0.9.5 or 1.0) or as a revision
56 56 # id composed of 12 hexa characters.
57 theversion = hgversion_from_command_line.dup
58 if theversion.respond_to?(:force_encoding)
59 theversion.force_encoding('ASCII-8BIT')
60 end
57 theversion = hgversion_from_command_line.dup.force_encoding('ASCII-8BIT')
61 58 if m = theversion.match(%r{\A(.*?)((\d+\.)+\d+)})
62 59 m[2].scan(%r{\d+}).collect(&:to_i)
63 60 end
@@ -130,10 +127,7 module Redmine
130 127 def summary
131 128 return @summary if @summary
132 129 hg 'rhsummary' do |io|
133 output = io.read
134 if output.respond_to?(:force_encoding)
135 output.force_encoding('UTF-8')
136 end
130 output = io.read.force_encoding('UTF-8')
137 131 begin
138 132 @summary = parse_xml(output)['rhsummary']
139 133 rescue
@@ -146,10 +140,7 module Redmine
146 140 p1 = scm_iconv(@path_encoding, 'UTF-8', path)
147 141 manifest = hg('rhmanifest', '-r', CGI.escape(hgrev(identifier)),
148 142 CGI.escape(without_leading_slash(p1.to_s))) do |io|
149 output = io.read
150 if output.respond_to?(:force_encoding)
151 output.force_encoding('UTF-8')
152 end
143 output = io.read.force_encoding('UTF-8')
153 144 begin
154 145 parse_xml(output)['rhmanifest']['repository']['manifest']
155 146 rescue
@@ -193,10 +184,7 module Redmine
193 184 hg_args << '--limit' << options[:limit] if options[:limit]
194 185 hg_args << hgtarget(path) unless path.blank?
195 186 log = hg(*hg_args) do |io|
196 output = io.read
197 if output.respond_to?(:force_encoding)
198 output.force_encoding('UTF-8')
199 end
187 output = io.read.force_encoding('UTF-8')
200 188 begin
201 189 # Mercurial < 1.5 does not support footer template for '</log>'
202 190 parse_xml("#{output}</log>")['log']
@@ -278,7 +266,7 module Redmine
278 266 blame = Annotate.new
279 267 hg 'rhannotate', '-ncu', '-r', CGI.escape(hgrev(identifier)), hgtarget(p) do |io|
280 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 270 next unless line =~ %r{^([^:]+)\s(\d+)\s([0-9a-f]+):\s(.*)$}
283 271 r = Revision.new(:author => $1.strip, :revision => $2, :scmid => $3,
284 272 :identifier => $3)
@@ -46,10 +46,7 module Redmine
46 46 end
47 47
48 48 def svn_binary_version
49 scm_version = scm_version_from_command_line.dup
50 if scm_version.respond_to?(:force_encoding)
51 scm_version.force_encoding('ASCII-8BIT')
52 end
49 scm_version = scm_version_from_command_line.dup.force_encoding('ASCII-8BIT')
53 50 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)})
54 51 m[2].scan(%r{\d+}).collect(&:to_i)
55 52 end
@@ -66,10 +63,7 module Redmine
66 63 cmd << credentials_string
67 64 info = nil
68 65 shellout(cmd) do |io|
69 output = io.read
70 if output.respond_to?(:force_encoding)
71 output.force_encoding('UTF-8')
72 end
66 output = io.read.force_encoding('UTF-8')
73 67 begin
74 68 doc = parse_xml(output)
75 69 # root_url = doc.elements["info/entry/repository/root"].text
@@ -98,10 +92,7 module Redmine
98 92 cmd = "#{self.class.sq_bin} list --xml #{target(path)}@#{identifier}"
99 93 cmd << credentials_string
100 94 shellout(cmd) do |io|
101 output = io.read
102 if output.respond_to?(:force_encoding)
103 output.force_encoding('UTF-8')
104 end
95 output = io.read.force_encoding('UTF-8')
105 96 begin
106 97 doc = parse_xml(output)
107 98 each_xml_element(doc['lists']['list'], 'entry') do |entry|
@@ -141,10 +132,7 module Redmine
141 132 cmd << credentials_string
142 133 properties = {}
143 134 shellout(cmd) do |io|
144 output = io.read
145 if output.respond_to?(:force_encoding)
146 output.force_encoding('UTF-8')
147 end
135 output = io.read.force_encoding('UTF-8')
148 136 begin
149 137 doc = parse_xml(output)
150 138 each_xml_element(doc['properties']['target'], 'property') do |property|
@@ -168,10 +156,7 module Redmine
168 156 cmd << " --limit #{options[:limit].to_i}" if options[:limit]
169 157 cmd << ' ' + target(path)
170 158 shellout(cmd) do |io|
171 output = io.read
172 if output.respond_to?(:force_encoding)
173 output.force_encoding('UTF-8')
174 end
159 output = io.read.force_encoding('UTF-8')
175 160 begin
176 161 doc = parse_xml(output)
177 162 each_xml_element(doc['log'], 'logentry') do |logentry|
@@ -199,28 +199,10 module Redmine
199 199 while starting < max && line_left[starting] == line_right[starting]
200 200 starting += 1
201 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 202 ending = -1
208 203 while ending >= -(max - starting) && (line_left[ending] == line_right[ending])
209 204 ending -= 1
210 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 206 unless starting == 0 && ending == -1
225 207 [starting, ending]
226 208 end
@@ -279,7 +261,7 module Redmine
279 261
280 262 def line_to_html(line, offsets)
281 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 265 html
284 266 end
285 267
@@ -33,7 +33,7 namespace :ci do
33 33 else
34 34 Rake::Task["test"].invoke
35 35 end
36 # Rake::Task["test:ui"].invoke if RUBY_VERSION >= '1.9.3'
36 # Rake::Task["test:ui"].invoke
37 37 end
38 38
39 39 desc "Finish the build"
@@ -52,7 +52,7 file 'config/database.yml' do
52 52
53 53 case database
54 54 when 'mysql'
55 dev_conf = {'adapter' => (RUBY_VERSION >= '1.9' ? 'mysql2' : 'mysql'),
55 dev_conf = {'adapter' => 'mysql2',
56 56 'database' => dev_db_name, 'host' => 'localhost',
57 57 'encoding' => 'utf8'}
58 58 if ENV['RUN_ON_NOT_OFFICIAL']
@@ -68,9 +68,6 namespace :locales do
68 68 desc <<-END_DESC
69 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 71 Options:
75 72 key=key_1,key_2 Comma-separated list of keys to delete
76 73 skip=en,de Comma-separated list of locale files to ignore (filename without extension)
@@ -18,7 +18,6
18 18 desc 'Mantis migration script'
19 19
20 20 require 'active_record'
21 require 'iconv' if RUBY_VERSION < '1.9'
22 21 require 'pp'
23 22
24 23 namespace :redmine do
@@ -452,12 +451,7 task :migrate_from_mantis => :environment do
452 451 end
453 452
454 453 def self.encode(text)
455 if RUBY_VERSION < '1.9'
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
454 text.to_s.force_encoding(@charset).encode('UTF-8')
461 455 end
462 456 end
463 457
@@ -16,7 +16,6
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'active_record'
19 require 'iconv' if RUBY_VERSION < '1.9'
20 19 require 'pp'
21 20
22 21 namespace :redmine do
@@ -155,7 +154,7 namespace :redmine do
155 154 attachment_type = read_attribute(:type)
156 155 #replace exotic characters with their hex representation to avoid invalid filenames
157 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 158 sprintf('%%%02x', codepoint)
160 159 end
161 160 "#{TracMigrate.trac_attachments_directory}/#{attachment_type}/#{id}/#{trac_file}"
@@ -715,12 +714,7 namespace :redmine do
715 714 end
716 715
717 716 def self.encode(text)
718 if RUBY_VERSION < '1.9'
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
717 text.to_s.force_encoding(@charset).encode('UTF-8')
724 718 end
725 719 end
726 720
@@ -1,6 +1,2
1 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
2 abort "script/about no longer exists, please use bin/about instead."
@@ -1,6 +1,2
1 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.
3
4 APP_PATH = File.expand_path('../../config/application', __FILE__)
5 require File.expand_path('../../config/boot', __FILE__)
6 require 'rails/commands'
2 abort "script/rails no longer exists, please use bin/rails instead."
@@ -72,8 +72,8 class AccountControllerTest < ActionController::TestCase
72 72 end
73 73
74 74 def test_login_with_suburi_should_redirect_to_back_url_param
75 @relative_url_root = ApplicationController.relative_url_root
76 ApplicationController.relative_url_root = '/redmine'
75 @relative_url_root = Redmine::Utils.relative_url_root
76 Redmine::Utils.relative_url_root = '/redmine'
77 77
78 78 back_urls = [
79 79 'http://test.host/redmine/issues/show/1',
@@ -84,7 +84,7 class AccountControllerTest < ActionController::TestCase
84 84 assert_redirected_to back_url
85 85 end
86 86 ensure
87 ApplicationController.relative_url_root = @relative_url_root
87 Redmine::Utils.relative_url_root = @relative_url_root
88 88 end
89 89
90 90 def test_login_should_not_redirect_to_another_host
@@ -99,8 +99,8 class AccountControllerTest < ActionController::TestCase
99 99 end
100 100
101 101 def test_login_with_suburi_should_not_redirect_to_another_suburi
102 @relative_url_root = ApplicationController.relative_url_root
103 ApplicationController.relative_url_root = '/redmine'
102 @relative_url_root = Redmine::Utils.relative_url_root
103 Redmine::Utils.relative_url_root = '/redmine'
104 104
105 105 back_urls = [
106 106 'http://test.host/',
@@ -115,7 +115,7 class AccountControllerTest < ActionController::TestCase
115 115 assert_redirected_to '/my/page'
116 116 end
117 117 ensure
118 ApplicationController.relative_url_root = @relative_url_root
118 Redmine::Utils.relative_url_root = @relative_url_root
119 119 end
120 120
121 121 def test_login_with_wrong_password
@@ -127,11 +127,13 class ActivitiesControllerTest < ActionController::TestCase
127 127 end
128 128
129 129 def test_index_atom_feed_with_one_item_type
130 get :index, :format => 'atom', :show_issues => '1'
131 assert_response :success
132 assert_template 'common/feed'
133
134 assert_select 'title', :text => /Issues/
130 with_settings :default_language => 'en' do
131 get :index, :format => 'atom', :show_issues => '1'
132 assert_response :success
133 assert_template 'common/feed'
134
135 assert_select 'title', :text => /Issues/
136 end
135 137 end
136 138
137 139 def test_index_should_show_private_notes_with_permission_only
@@ -139,8 +139,7 class AttachmentsControllerTest < ActionController::TestCase
139 139 assert a.save
140 140 assert_equal 'japanese-utf-8.txt', a.filename
141 141
142 str_japanese = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"
143 str_japanese.force_encoding('UTF-8') if str_japanese.respond_to?(:force_encoding)
142 str_japanese = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e".force_encoding('UTF-8')
144 143
145 144 get :show, :id => a.id
146 145 assert_response :success
@@ -48,10 +48,10 class CalendarsControllerTest < ActionController::TestCase
48 48 end
49 49
50 50 def test_week_number_calculation
51 Setting.start_of_week = 7
52
53 get :show, :month => '1', :year => '2010'
54 assert_response :success
51 with_settings :start_of_week => 7 do
52 get :show, :month => '1', :year => '2010'
53 assert_response :success
54 end
55 55
56 56 assert_select 'tr' do
57 57 assert_select 'td.week-number', :text => '53'
@@ -65,9 +65,10 class CalendarsControllerTest < ActionController::TestCase
65 65 assert_select 'td.even', :text => '9'
66 66 end
67 67
68 Setting.start_of_week = 1
69 get :show, :month => '1', :year => '2010'
70 assert_response :success
68 with_settings :start_of_week => 1 do
69 get :show, :month => '1', :year => '2010'
70 assert_response :success
71 end
71 72
72 73 assert_select 'tr' do
73 74 assert_select 'td.week-number', :text => '53'
@@ -56,12 +56,14 class ContextMenusControllerTest < ActionController::TestCase
56 56 end
57 57
58 58 def test_context_menu_one_issue_by_anonymous
59 get :issues, :ids => [1]
60 assert_response :success
61 assert_template 'context_menus/issues'
62 assert_tag :tag => 'a', :content => 'Delete',
63 :attributes => { :href => '#',
64 :class => 'icon-del disabled' }
59 with_settings :default_language => 'en' do
60 get :issues, :ids => [1]
61 assert_response :success
62 assert_template 'context_menus/issues'
63 assert_tag :tag => 'a', :content => 'Delete',
64 :attributes => { :href => '#',
65 :class => 'icon-del disabled' }
66 end
65 67 end
66 68
67 69 def test_context_menu_multiple_issues_of_same_project
@@ -112,7 +112,7 class CustomFieldsControllerTest < ActionController::TestCase
112 112 end
113 113
114 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 116 assert_response :success
117 117 assert_template 'new'
118 118 assert_equal 'text/javascript', response.content_type
@@ -201,7 +201,7 class GroupsControllerTest < ActionController::TestCase
201 201 end
202 202
203 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 205 assert_response :success
206 206 assert_include 'John Smith', response.body
207 207 end
@@ -474,12 +474,8 class IssuesControllerTest < ActionController::TestCase
474 474
475 475 def test_index_csv_big_5
476 476 with_settings :default_language => "zh-TW" do
477 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88"
478 str_big5 = "\xa4@\xa4\xeb"
479 if str_utf8.respond_to?(:force_encoding)
480 str_utf8.force_encoding('UTF-8')
481 str_big5.force_encoding('Big5')
482 end
477 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88".force_encoding('UTF-8')
478 str_big5 = "\xa4@\xa4\xeb".force_encoding('Big5')
483 479 issue = Issue.generate!(:subject => str_utf8)
484 480
485 481 get :index, :project_id => 1,
@@ -488,10 +484,7 class IssuesControllerTest < ActionController::TestCase
488 484 :format => 'csv'
489 485 assert_equal 'text/csv; header=present', @response.content_type
490 486 lines = @response.body.chomp.split("\n")
491 s1 = "\xaa\xac\xbaA"
492 if str_utf8.respond_to?(:force_encoding)
493 s1.force_encoding('Big5')
494 end
487 s1 = "\xaa\xac\xbaA".force_encoding('Big5')
495 488 assert_include s1, lines[0]
496 489 assert_include str_big5, lines[1]
497 490 end
@@ -499,10 +492,7 class IssuesControllerTest < ActionController::TestCase
499 492
500 493 def test_index_csv_cannot_convert_should_be_replaced_big_5
501 494 with_settings :default_language => "zh-TW" do
502 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85"
503 if str_utf8.respond_to?(:force_encoding)
504 str_utf8.force_encoding('UTF-8')
505 end
495 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85".force_encoding('UTF-8')
506 496 issue = Issue.generate!(:subject => str_utf8)
507 497
508 498 get :index, :project_id => 1,
@@ -513,21 +503,11 class IssuesControllerTest < ActionController::TestCase
513 503 :set_filter => 1
514 504 assert_equal 'text/csv; header=present', @response.content_type
515 505 lines = @response.body.chomp.split("\n")
516 s1 = "\xaa\xac\xbaA" # status
517 if str_utf8.respond_to?(:force_encoding)
518 s1.force_encoding('Big5')
519 end
506 s1 = "\xaa\xac\xbaA".force_encoding('Big5') # status
520 507 assert lines[0].include?(s1)
521 508 s2 = lines[1].split(",")[2]
522 if s1.respond_to?(:force_encoding)
523 s3 = "\xa5H?" # subject
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
509 s3 = "\xa5H?".force_encoding('Big5') # subject
510 assert_equal s3, s2
531 511 end
532 512 end
533 513
@@ -3120,20 +3100,15 class IssuesControllerTest < ActionController::TestCase
3120 3100 def test_put_update_with_attachment_that_fails_to_save
3121 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 3103 # anonymous user
3131 put :update,
3132 :id => 1,
3133 :issue => {:notes => ''},
3134 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
3135 assert_redirected_to :action => 'show', :id => '1'
3136 assert_equal '1 file(s) could not be saved.', flash[:warning]
3104 with_settings :attachment_max_size => 0 do
3105 put :update,
3106 :id => 1,
3107 :issue => {:notes => ''},
3108 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
3109 assert_redirected_to :action => 'show', :id => '1'
3110 assert_equal '1 file(s) could not be saved.', flash[:warning]
3111 end
3137 3112 end
3138 3113
3139 3114 def test_put_update_with_no_change
@@ -3379,7 +3354,7 class IssuesControllerTest < ActionController::TestCase
3379 3354 post :bulk_edit, :ids => [1, 2, 6], :issue => {:project_id => 1}
3380 3355 assert_response :success
3381 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 3359 assert_select 'select[name=?]', 'issue[fixed_version_id]' do
3385 3360 assert_select 'option', :text => '2.0'
@@ -199,8 +199,8 class IssuesCustomFieldsVisibilityTest < ActionController::TestCase
199 199 p1 = Project.generate!
200 200 p2 = Project.generate!
201 201 user = User.generate!
202 User.add_to_project(user, p1, Role.where(:id => [1, 3]).all)
203 User.add_to_project(user, p2, Role.where(:id => 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).to_a)
204 204 Issue.generate!(:project => p1, :tracker_id => 1, :custom_field_values => {@field2.id => 'ValueA'})
205 205 Issue.generate!(:project => p2, :tracker_id => 1, :custom_field_values => {@field2.id => 'ValueB'})
206 206 Issue.generate!(:project => p1, :tracker_id => 1, :custom_field_values => {@field2.id => 'ValueC'})
@@ -104,7 +104,7 class MembersControllerTest < ActionController::TestCase
104 104 end
105 105
106 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 108 assert_response :success
109 109 assert_include 'User Misc', response.body
110 110 end
@@ -59,8 +59,8 class MessagesControllerTest < ActionController::TestCase
59 59 assert_template 'show'
60 60 replies = assigns(:replies)
61 61 assert_not_nil replies
62 assert !replies.include?(message.children.order('id').first)
63 assert replies.include?(message.children.order('id').last)
62 assert_not_include message.children.reorder('id').first, replies
63 assert_include message.children.reorder('id').last, replies
64 64 end
65 65
66 66 def test_show_with_reply_permission
@@ -167,7 +167,7 class MyControllerTest < ActionController::TestCase
167 167 :new_password_confirmation => 'secret1234'
168 168 assert_response :success
169 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 172 # wrong password
173 173 post :password, :password => 'wrongpassword',
@@ -26,7 +26,7 class RepositoriesBazaarControllerTest < ActionController::TestCase
26 26 REPOSITORY_PATH = Rails.root.join('tmp/test/bazaar_repository').to_s
27 27 REPOSITORY_PATH_TRUNK = File.join(REPOSITORY_PATH, "trunk")
28 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 31 def setup
32 32 User.current = nil
@@ -36,10 +36,6 class RepositoriesBazaarControllerTest < ActionController::TestCase
36 36 :url => REPOSITORY_PATH_TRUNK,
37 37 :log_encoding => 'UTF-8')
38 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 39 end
44 40
45 41 if File.directory?(REPOSITORY_PATH)
@@ -176,31 +172,29 class RepositoriesBazaarControllerTest < ActionController::TestCase
176 172 end
177 173 end
178 174
179 if REPOSITORY_PATH.respond_to?(:force_encoding)
180 def test_annotate_author_non_ascii
181 log_encoding = nil
182 if Encoding.locale_charmap == "UTF-8" ||
183 Encoding.locale_charmap == "ISO-8859-1"
184 log_encoding = Encoding.locale_charmap
185 end
186 unless log_encoding.nil?
187 repository = Repository::Bazaar.create(
188 :project => @project,
189 :url => File.join(REPOSITORY_PATH, "author_non_ascii"),
190 :identifier => 'author_non_ascii',
191 :log_encoding => log_encoding)
192 assert repository
193 get :annotate, :id => PRJ_ID, :repository_id => 'author_non_ascii',
194 :path => repository_path_hash(['author-non-ascii-test.txt'])[:param]
195 assert_response :success
196 assert_template 'annotate'
197 assert_select "th.line-num", :text => '1' do
198 assert_select "+ td.revision" do
199 assert_select "a", :text => '2'
200 assert_select "+ td.author", :text => "test #{@char_1_utf8}" do
201 assert_select "+ td",
202 :text => "author non ASCII test"
203 end
175 def test_annotate_author_non_ascii
176 log_encoding = nil
177 if Encoding.locale_charmap == "UTF-8" ||
178 Encoding.locale_charmap == "ISO-8859-1"
179 log_encoding = Encoding.locale_charmap
180 end
181 unless log_encoding.nil?
182 repository = Repository::Bazaar.create(
183 :project => @project,
184 :url => File.join(REPOSITORY_PATH, "author_non_ascii"),
185 :identifier => 'author_non_ascii',
186 :log_encoding => log_encoding)
187 assert repository
188 get :annotate, :id => PRJ_ID, :repository_id => 'author_non_ascii',
189 :path => repository_path_hash(['author-non-ascii-test.txt'])[:param]
190 assert_response :success
191 assert_template 'annotate'
192 assert_select "th.line-num", :text => '1' do
193 assert_select "+ td.revision" do
194 assert_select "a", :text => '2'
195 assert_select "+ td.author", :text => "test #{CHAR_1_UTF8_HEX}" do
196 assert_select "+ td",
197 :text => "author non ASCII test"
204 198 end
205 199 end
206 200 end
@@ -27,8 +27,7 class RepositoriesFilesystemControllerTest < ActionController::TestCase
27 27 PRJ_ID = 3
28 28
29 29 def setup
30 @ruby19_non_utf8_pass =
31 (RUBY_VERSION >= '1.9' && Encoding.default_external.to_s != 'UTF-8')
30 @ruby19_non_utf8_pass = Encoding.default_external.to_s != 'UTF-8'
32 31 User.current = nil
33 32 Setting.enabled_scm << 'Filesystem' unless Setting.enabled_scm.include?('Filesystem')
34 33 @project = Project.find(PRJ_ID)
@@ -94,12 +93,11 class RepositoriesFilesystemControllerTest < ActionController::TestCase
94 93 :attributes => { :class => 'line-num' },
95 94 :sibling => { :tag => 'td', :content => /japanese/ }
96 95 if @ruby19_non_utf8_pass
97 puts "TODO: show repository file contents test fails in Ruby 1.9 " +
98 "and Encoding.default_external is not UTF-8. " +
96 puts "TODO: show repository file contents test fails " +
97 "when Encoding.default_external is not UTF-8. " +
99 98 "Current value is '#{Encoding.default_external.to_s}'"
100 99 else
101 str_japanese = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"
102 str_japanese.force_encoding('UTF-8') if str_japanese.respond_to?(:force_encoding)
100 str_japanese = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e".force_encoding('UTF-8')
103 101 assert_tag :tag => 'th',
104 102 :content => '3',
105 103 :attributes => { :class => 'line-num' },
@@ -109,7 +107,7 class RepositoriesFilesystemControllerTest < ActionController::TestCase
109 107 end
110 108
111 109 def test_show_utf16
112 enc = (RUBY_VERSION == "1.9.2" ? 'UTF-16LE' : 'UTF-16')
110 enc = 'UTF-16'
113 111 with_settings :repositories_encodings => enc do
114 112 get :entry, :id => PRJ_ID,
115 113 :path => repository_path_hash(['japanese', 'utf-16.txt'])[:param]
@@ -26,8 +26,8 class RepositoriesGitControllerTest < ActionController::TestCase
26 26 REPOSITORY_PATH = Rails.root.join('tmp/test/git_repository').to_s
27 27 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
28 28 PRJ_ID = 3
29 CHAR_1_HEX = "\xc3\x9c"
30 FELIX_HEX = "Felix Sch\xC3\xA4fer"
29 CHAR_1_HEX = "\xc3\x9c".force_encoding('UTF-8')
30 FELIX_HEX = "Felix Sch\xC3\xA4fer".force_encoding('UTF-8')
31 31 NUM_REV = 28
32 32
33 33 ## Git, Mercurial and CVS path encodings are binary.
@@ -39,8 +39,7 class RepositoriesGitControllerTest < ActionController::TestCase
39 39 JRUBY_SKIP_STR = "TODO: This test fails in JRuby"
40 40
41 41 def setup
42 @ruby19_non_utf8_pass =
43 (RUBY_VERSION >= '1.9' && Encoding.default_external.to_s != 'UTF-8')
42 @ruby19_non_utf8_pass = Encoding.default_external.to_s != 'UTF-8'
44 43
45 44 User.current = nil
46 45 @project = Project.find(PRJ_ID)
@@ -50,12 +49,6 class RepositoriesGitControllerTest < ActionController::TestCase
50 49 :path_encoding => 'ISO-8859-1'
51 50 )
52 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 52 end
60 53
61 54 def test_create_and_update
@@ -231,7 +224,7 class RepositoriesGitControllerTest < ActionController::TestCase
231 224 with_settings :repositories_encodings => 'UTF-8,ISO-8859-1' do
232 225 ['57ca437c', '57ca437c0acbbcb749821fdf3726a1367056d364'].each do |r1|
233 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 228 :rev => r1
236 229 assert_response :success
237 230 assert_template 'entry'
@@ -239,7 +232,7 class RepositoriesGitControllerTest < ActionController::TestCase
239 232 :content => '1',
240 233 :attributes => { :class => 'line-num' },
241 234 :sibling => { :tag => 'td',
242 :content => /test-#{@char_1}.txt/ }
235 :content => /test-#{CHAR_1_HEX}.txt/ }
243 236 end
244 237 end
245 238 end
@@ -419,14 +412,14 class RepositoriesGitControllerTest < ActionController::TestCase
419 412 :descendant => {
420 413 :tag => 'th',
421 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 417 :sibling => {
425 418 :tag => 'tbody',
426 419 :descendant => {
427 420 :tag => 'td',
428 421 :attributes => { :class => /diff_in/ },
429 :content => /test-#{@char_1}.txt/
422 :content => /test-#{CHAR_1_HEX}.txt/
430 423 }
431 424 }
432 425 end
@@ -498,11 +491,13 class RepositoriesGitControllerTest < ActionController::TestCase
498 491 end
499 492
500 493 def test_annotate_binary_file
501 get :annotate, :id => PRJ_ID,
502 :path => repository_path_hash(['images', 'edit.png'])[:param]
503 assert_response 500
504 assert_tag :tag => 'p', :attributes => { :id => /errorExplanation/ },
505 :content => /cannot be annotated/
494 with_settings :default_language => 'en' do
495 get :annotate, :id => PRJ_ID,
496 :path => repository_path_hash(['images', 'edit.png'])[:param]
497 assert_response 500
498 assert_tag :tag => 'p', :attributes => { :id => /errorExplanation/ },
499 :content => /cannot be annotated/
500 end
506 501 end
507 502
508 503 def test_annotate_error_when_too_big
@@ -533,14 +528,14 class RepositoriesGitControllerTest < ActionController::TestCase
533 528 with_settings :repositories_encodings => 'UTF-8,ISO-8859-1' do
534 529 ['57ca437c', '57ca437c0acbbcb749821fdf3726a1367056d364'].each do |r1|
535 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 532 :rev => r1
538 533 assert_select "th.line-num", :text => '1' do
539 534 assert_select "+ td.revision" do
540 535 assert_select "a", :text => '57ca437c'
541 536 assert_select "+ td.author", :text => "jsmith" do
542 537 assert_select "+ td",
543 :text => "test-#{@char_1}.txt"
538 :text => "test-#{CHAR_1_HEX}.txt"
544 539 end
545 540 end
546 541 end
@@ -557,7 +552,7 class RepositoriesGitControllerTest < ActionController::TestCase
557 552 assert_select "th.line-num", :text => '1' do
558 553 assert_select "+ td.revision" do
559 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 556 assert_select "+ td",
562 557 :text => "And this is a file with a leading and trailing space..."
563 558 end
@@ -643,8 +638,8 class RepositoriesGitControllerTest < ActionController::TestCase
643 638 private
644 639
645 640 def puts_ruby19_non_utf8_pass
646 puts "TODO: This test fails in Ruby 1.9 " +
647 "and Encoding.default_external is not UTF-8. " +
641 puts "TODO: This test fails " +
642 "when Encoding.default_external is not UTF-8. " +
648 643 "Current value is '#{Encoding.default_external.to_s}'"
649 644 end
650 645 else
@@ -28,8 +28,7 class RepositoriesMercurialControllerTest < ActionController::TestCase
28 28 PRJ_ID = 3
29 29 NUM_REV = 34
30 30
31 ruby19_non_utf8_pass =
32 (RUBY_VERSION >= '1.9' && Encoding.default_external.to_s != 'UTF-8')
31 ruby19_non_utf8_pass = Encoding.default_external.to_s != 'UTF-8'
33 32
34 33 def setup
35 34 User.current = nil
@@ -41,21 +40,15 class RepositoriesMercurialControllerTest < ActionController::TestCase
41 40 )
42 41 assert @repository
43 42 @diff_c_support = true
44 @char_1 = CHAR_1_HEX.dup
45 @tag_char_1 = "tag-#{CHAR_1_HEX}-00"
46 @branch_char_0 = "branch-#{CHAR_1_HEX}-00"
47 @branch_char_1 = "branch-#{CHAR_1_HEX}-01"
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
43 @char_1 = CHAR_1_HEX.dup.force_encoding('UTF-8')
44 @tag_char_1 = "tag-#{CHAR_1_HEX}-00".force_encoding('UTF-8')
45 @branch_char_0 = "branch-#{CHAR_1_HEX}-00".force_encoding('UTF-8')
46 @branch_char_1 = "branch-#{CHAR_1_HEX}-01".force_encoding('UTF-8')
54 47 end
55 48
56 49 if ruby19_non_utf8_pass
57 puts "TODO: Mercurial functional test fails in Ruby 1.9 " +
58 "and Encoding.default_external is not UTF-8. " +
50 puts "TODO: Mercurial functional test fails " +
51 "when Encoding.default_external is not UTF-8. " +
59 52 "Current value is '#{Encoding.default_external.to_s}'"
60 53 def test_fake; assert true end
61 54 elsif File.directory?(REPOSITORY_PATH)
@@ -31,7 +31,7 class RolesControllerTest < ActionController::TestCase
31 31 assert_template 'index'
32 32
33 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 36 assert_tag :tag => 'a', :attributes => { :href => '/roles/1/edit' },
37 37 :content => 'Manager'
@@ -160,7 +160,7 class RolesControllerTest < ActionController::TestCase
160 160 assert_template 'permissions'
161 161
162 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 165 assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
166 166 :name => 'permissions[3][]',
@@ -40,7 +40,9 class SearchControllerTest < ActionController::TestCase
40 40 end
41 41
42 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 46 assert_response :success
45 47 assert_template 'index'
46 48
@@ -240,12 +240,8 class TimeEntryReportsControllerTest < ActionController::TestCase
240 240
241 241 def test_csv_big_5
242 242 Setting.default_language = "zh-TW"
243 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88"
244 str_big5 = "\xa4@\xa4\xeb"
245 if str_utf8.respond_to?(:force_encoding)
246 str_utf8.force_encoding('UTF-8')
247 str_big5.force_encoding('Big5')
248 end
243 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88".force_encoding('UTF-8')
244 str_big5 = "\xa4@\xa4\xeb".force_encoding('Big5')
249 245 user = User.find_by_id(3)
250 246 user.firstname = str_utf8
251 247 user.lastname = "test-lastname"
@@ -270,21 +266,14 class TimeEntryReportsControllerTest < ActionController::TestCase
270 266 assert_equal 'text/csv; header=present', @response.content_type
271 267 lines = @response.body.chomp.split("\n")
272 268 # Headers
273 s1 = "\xa5\xce\xa4\xe1,2011-11-11,\xa4u\xae\xc9\xc1`\xadp"
274 s2 = "\xa4u\xae\xc9\xc1`\xadp"
275 if s1.respond_to?(:force_encoding)
276 s1.force_encoding('Big5')
277 s2.force_encoding('Big5')
278 end
269 s1 = "\xa5\xce\xa4\xe1,2011-11-11,\xa4u\xae\xc9\xc1`\xadp".force_encoding('Big5')
270 s2 = "\xa4u\xae\xc9\xc1`\xadp".force_encoding('Big5')
279 271 assert_equal s1, lines.first
280 272 # Total row
281 273 assert_equal "#{str_big5} #{user.lastname},7.30,7.30", lines[1]
282 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)"
285 if str_tw.respond_to?(:force_encoding)
286 str_tw.force_encoding('UTF-8')
287 end
276 str_tw = "Traditional Chinese (\xe7\xb9\x81\xe9\xab\x94\xe4\xb8\xad\xe6\x96\x87)".force_encoding('UTF-8')
288 277 assert_equal str_tw, l(:general_lang_name)
289 278 assert_equal 'Big5', l(:general_csv_encoding)
290 279 assert_equal ',', l(:general_csv_separator)
@@ -293,10 +282,7 class TimeEntryReportsControllerTest < ActionController::TestCase
293 282
294 283 def test_csv_cannot_convert_should_be_replaced_big_5
295 284 Setting.default_language = "zh-TW"
296 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85"
297 if str_utf8.respond_to?(:force_encoding)
298 str_utf8.force_encoding('UTF-8')
299 end
285 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85".force_encoding('UTF-8')
300 286 user = User.find_by_id(3)
301 287 user.firstname = str_utf8
302 288 user.lastname = "test-lastname"
@@ -321,21 +307,10 class TimeEntryReportsControllerTest < ActionController::TestCase
321 307 assert_equal 'text/csv; header=present', @response.content_type
322 308 lines = @response.body.chomp.split("\n")
323 309 # Headers
324 s1 = "\xa5\xce\xa4\xe1,2011-11-11,\xa4u\xae\xc9\xc1`\xadp"
325 if s1.respond_to?(:force_encoding)
326 s1.force_encoding('Big5')
327 end
310 s1 = "\xa5\xce\xa4\xe1,2011-11-11,\xa4u\xae\xc9\xc1`\xadp".force_encoding('Big5')
328 311 assert_equal s1, lines.first
329 312 # Total row
330 s2 = ""
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
313 s2 = "\xa5H?".force_encoding('Big5')
339 314 assert_equal "#{s2} #{user.lastname},7.30,7.30", lines[1]
340 315 end
341 316
@@ -362,21 +337,14 class TimeEntryReportsControllerTest < ActionController::TestCase
362 337 assert_equal 'text/csv; header=present', @response.content_type
363 338 lines = @response.body.chomp.split("\n")
364 339 # Headers
365 s1 = "Utilisateur;2011-11-11;Temps total"
366 s2 = "Temps total"
367 if s1.respond_to?(:force_encoding)
368 s1.force_encoding('ISO-8859-1')
369 s2.force_encoding('ISO-8859-1')
370 end
340 s1 = "Utilisateur;2011-11-11;Temps total".force_encoding('ISO-8859-1')
341 s2 = "Temps total".force_encoding('ISO-8859-1')
371 342 assert_equal s1, lines.first
372 343 # Total row
373 344 assert_equal "#{user.firstname} #{user.lastname};7,30;7,30", lines[1]
374 345 assert_equal "#{s2};7,30;7,30", lines[2]
375 346
376 str_fr = "Fran\xc3\xa7ais"
377 if str_fr.respond_to?(:force_encoding)
378 str_fr.force_encoding('UTF-8')
379 end
347 str_fr = "Fran\xc3\xa7ais".force_encoding('UTF-8')
380 348 assert_equal str_fr, l(:general_lang_name)
381 349 assert_equal 'ISO-8859-1', l(:general_csv_encoding)
382 350 assert_equal ';', l(:general_csv_separator)
@@ -702,16 +702,18 class TimelogControllerTest < ActionController::TestCase
702 702 end
703 703
704 704 def test_index_csv_all_projects
705 Setting.date_format = '%m/%d/%Y'
706 get :index, :format => 'csv'
707 assert_response :success
708 assert_equal 'text/csv; header=present', response.content_type
705 with_settings :date_format => '%m/%d/%Y' do
706 get :index, :format => 'csv'
707 assert_response :success
708 assert_equal 'text/csv; header=present', response.content_type
709 end
709 710 end
710 711
711 712 def test_index_csv
712 Setting.date_format = '%m/%d/%Y'
713 get :index, :project_id => 1, :format => 'csv'
714 assert_response :success
715 assert_equal 'text/csv; header=present', response.content_type
713 with_settings :date_format => '%m/%d/%Y' do
714 get :index, :project_id => 1, :format => 'csv'
715 assert_response :success
716 assert_equal 'text/csv; header=present', response.content_type
717 end
716 718 end
717 719 end
@@ -95,8 +95,8 class TimelogCustomFieldsVisibilityTest < ActionController::TestCase
95 95 p1 = Project.generate!
96 96 p2 = Project.generate!
97 97 user = User.generate!
98 User.add_to_project(user, p1, Role.where(:id => [1, 3]).all)
99 User.add_to_project(user, p2, Role.where(:id => 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).to_a)
100 100 TimeEntry.generate!(
101 101 :issue => Issue.generate!(:project => p1, :tracker_id => 1,
102 102 :custom_field_values => {@field2.id => 'ValueA'}))
@@ -108,9 +108,9 class TimelogCustomFieldsVisibilityTest < ActionController::TestCase
108 108 :custom_field_values => {@field2.id => 'ValueC'}))
109 109 @request.session[:user_id] = user.id
110 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 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 115 get :index, :set_filter => '1', "issue.cf_#{@field2.id}" => '*'
116 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 97 @request.session[:user_id] = 2
98 98
99 99 get :index
100 assert_tag 'script',
101 :attributes => {:type => "text/javascript"},
102 :content => %r{warnLeavingUnsaved}
100 assert_select 'script', :text => %r{warnLeavingUnsaved}
103 101 end
104 102
105 103 def test_warn_on_leaving_unsaved_turn_off
@@ -58,7 +58,9 class WikiControllerTest < ActionController::TestCase
58 58 end
59 59
60 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 64 assert_response :success
63 65 assert_template 'show'
64 66
@@ -89,7 +91,9 class WikiControllerTest < ActionController::TestCase
89 91 end
90 92
91 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 97 assert_response :success
94 98 assert_template 'show'
95 99
@@ -248,7 +248,7 class WorkflowsControllerTest < ActionController::TestCase
248 248
249 249 get :permissions, :role_id => 1, :tracker_id => 2, :used_statuses_only => '0'
250 250 assert_response :success
251 assert_equal IssueStatus.sorted.all, assigns(:statuses)
251 assert_equal IssueStatus.sorted.to_a, assigns(:statuses)
252 252 end
253 253
254 254 def test_post_permissions
@@ -17,13 +17,7
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 begin
21 require 'mocha/setup'
22 rescue
23 # Won't run some tests
24 end
25
26 class AccountTest < ActionController::IntegrationTest
20 class AccountTest < ActionDispatch::IntegrationTest
27 21 fixtures :users, :roles
28 22
29 23 def test_login
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 class AdminTest < ActionController::IntegrationTest
20 class AdminTest < ActionDispatch::IntegrationTest
21 21 fixtures :projects, :trackers, :issue_statuses, :issues,
22 22 :enumerations, :users, :issue_categories,
23 23 :projects_trackers,
@@ -41,11 +41,8 class Redmine::ApiTest::AuthenticationTest < Redmine::ApiTest::Base
41 41 end
42 42
43 43 def test_invalid_utf8_credentials_should_not_trigger_an_error
44 invalid_utf8 = "\x82"
45 if invalid_utf8.respond_to?(:force_encoding)
46 invalid_utf8.force_encoding('UTF-8')
47 assert !invalid_utf8.valid_encoding?
48 end
44 invalid_utf8 = "\x82".force_encoding('UTF-8')
45 assert !invalid_utf8.valid_encoding?
49 46 assert_nothing_raised do
50 47 get '/users/current.xml', {}, credentials(invalid_utf8, "foo")
51 48 end
@@ -165,7 +165,7 class Redmine::ApiTest::MembershipsTest < Redmine::ApiTest::Base
165 165
166 166 assert_response :unprocessable_entity
167 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 169 end
170 170
171 171 test "DELETE /memberships/:id.xml should destroy the membership" do
@@ -195,12 +195,12 class Redmine::ApiTest::ProjectsTest < Redmine::ApiTest::Base
195 195 end
196 196
197 197 test "POST /projects.xml with valid parameters should create the project" do
198 Setting.default_projects_modules = ['issue_tracking', 'repository']
199
200 assert_difference('Project.count') do
201 post '/projects.xml',
202 {:project => {:name => 'API test', :identifier => 'api-test'}},
203 credentials('admin')
198 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
199 assert_difference('Project.count') do
200 post '/projects.xml',
201 {:project => {:name => 'API test', :identifier => 'api-test'}},
202 credentials('admin')
203 end
204 204 end
205 205
206 206 project = Project.order('id DESC').first
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 class ApplicationTest < ActionController::IntegrationTest
20 class ApplicationTest < ActionDispatch::IntegrationTest
21 21 include Redmine::I18n
22 22
23 23 fixtures :projects, :trackers, :issue_statuses, :issues,
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 class AttachmentsTest < ActionController::IntegrationTest
20 class AttachmentsTest < ActionDispatch::IntegrationTest
21 21 fixtures :projects, :enabled_modules,
22 22 :users, :roles, :members, :member_roles,
23 23 :trackers, :projects_trackers,
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 class FeedsTest < ActionController::IntegrationTest
20 class FeedsTest < ActionDispatch::IntegrationTest
21 21 fixtures :projects, :trackers, :issue_statuses, :issues,
22 22 :enumerations, :users, :issue_categories,
23 23 :projects_trackers, :enabled_modules,
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 class IssuesTest < ActionController::IntegrationTest
20 class IssuesTest < ActionDispatch::IntegrationTest
21 21 fixtures :projects,
22 22 :users,
23 23 :roles,
@@ -132,27 +132,27 class IssuesTest < ActionController::IntegrationTest
132 132 end
133 133
134 134 def test_pagination_links_on_index
135 Setting.per_page_options = '2'
136 get '/projects/ecookbook/issues'
137
138 assert_tag :a, :content => '2',
139 :attributes => { :href => '/projects/ecookbook/issues?page=2' }
135 with_settings :per_page_options => '2' do
136 get '/projects/ecookbook/issues'
140 137
138 assert_tag :a, :content => '2',
139 :attributes => { :href => '/projects/ecookbook/issues?page=2' }
140 end
141 141 end
142 142
143 143 def test_pagination_links_on_index_without_project_id_in_url
144 Setting.per_page_options = '2'
145 get '/issues', :project_id => 'ecookbook'
146
147 assert_tag :a, :content => '2',
148 :attributes => { :href => '/projects/ecookbook/issues?page=2' }
149
144 with_settings :per_page_options => '2' do
145 get '/issues', :project_id => 'ecookbook'
146
147 assert_tag :a, :content => '2',
148 :attributes => { :href => '/projects/ecookbook/issues?page=2' }
149 end
150 150 end
151 151
152 152 def test_issue_with_user_custom_field
153 153 @field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true, :trackers => Tracker.all)
154 154 Role.anonymous.add_permission! :add_issues, :edit_issues
155 users = Project.find(1).users
155 users = Project.find(1).users.uniq.sort
156 156 tester = users.first
157 157
158 158 # Issue form
@@ -205,8 +205,8 class IssuesTest < ActionController::IntegrationTest
205 205 :issue => {
206 206 :custom_field_values => {@field.id.to_s => new_tester.id.to_s}
207 207 }
208 assert_redirected_to "/issues/#{issue.id}"
208 209 end
209 assert_response 302
210 210
211 211 # Issue view
212 212 follow_redirect!
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 class LayoutTest < ActionController::IntegrationTest
20 class LayoutTest < ActionDispatch::IntegrationTest
21 21 fixtures :projects, :trackers, :issue_statuses, :issues,
22 22 :enumerations, :users, :issue_categories,
23 23 :projects_trackers,
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../../test_helper', __FILE__)
19 19
20 class HookTest < ActionController::IntegrationTest
20 class HookTest < ActionDispatch::IntegrationTest
21 21 fixtures :users, :roles, :projects, :members, :member_roles
22 22
23 23 # Hooks that are manually registered later
@@ -34,6 +34,8 class HookTest < ActionController::IntegrationTest
34 34 end
35 35 end
36 36
37 Redmine::Hook.clear_listeners
38
37 39 class ContentForInsideHook < Redmine::Hook::ViewListener
38 40 render_on :view_welcome_index_left, :inline => <<-VIEW
39 41 <% content_for :header_tags do %>
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../../test_helper', __FILE__)
19 19
20 class MenuManagerTest < ActionController::IntegrationTest
20 class MenuManagerTest < ActionDispatch::IntegrationTest
21 21 include Redmine::I18n
22 22
23 23 fixtures :projects, :trackers, :issue_statuses, :issues,
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../../test_helper', __FILE__)
19 19
20 class ThemesTest < ActionController::IntegrationTest
20 class ThemesTest < ActionDispatch::IntegrationTest
21 21
22 22 def setup
23 23 @theme = Redmine::Themes.themes.last
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 class ProjectsTest < ActionController::IntegrationTest
20 class ProjectsTest < ActionDispatch::IntegrationTest
21 21 fixtures :projects, :users, :members, :enabled_modules
22 22
23 23 def test_archive_project
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 class RepositoriesGitTest < ActionController::IntegrationTest
20 class RepositoriesGitTest < ActionDispatch::IntegrationTest
21 21 fixtures :projects, :users, :roles, :members, :member_roles,
22 22 :repositories, :enabled_modules
23 23
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingAccountTest < ActionController::IntegrationTest
20 class RoutingAccountTest < ActionDispatch::IntegrationTest
21 21 def test_account
22 22 ["get", "post"].each do |method|
23 23 assert_routing(
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingActivitiesTest < ActionController::IntegrationTest
20 class RoutingActivitiesTest < ActionDispatch::IntegrationTest
21 21 def test_activities
22 22 assert_routing(
23 23 { :method => 'get', :path => "/activity" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingAdminTest < ActionController::IntegrationTest
20 class RoutingAdminTest < ActionDispatch::IntegrationTest
21 21 def test_administration_panel
22 22 assert_routing(
23 23 { :method => 'get', :path => "/admin" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingAttachmentsTest < ActionController::IntegrationTest
20 class RoutingAttachmentsTest < ActionDispatch::IntegrationTest
21 21 def test_attachments
22 22 assert_routing(
23 23 { :method => 'get', :path => "/attachments/1" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingAuthSourcesTest < ActionController::IntegrationTest
20 class RoutingAuthSourcesTest < ActionDispatch::IntegrationTest
21 21 def test_auth_sources
22 22 assert_routing(
23 23 { :method => 'get', :path => "/auth_sources" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingAutoCompletesTest < ActionController::IntegrationTest
20 class RoutingAutoCompletesTest < ActionDispatch::IntegrationTest
21 21 def test_auto_completes
22 22 assert_routing(
23 23 { :method => 'get', :path => "/issues/auto_complete" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingBoardsTest < ActionController::IntegrationTest
20 class RoutingBoardsTest < ActionDispatch::IntegrationTest
21 21 def test_boards
22 22 assert_routing(
23 23 { :method => 'get', :path => "/projects/world_domination/boards" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingCalendarsTest < ActionController::IntegrationTest
20 class RoutingCalendarsTest < ActionDispatch::IntegrationTest
21 21 def test_calendars
22 22 assert_routing(
23 23 { :method => 'get', :path => "/issues/calendar" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingCommentsTest < ActionController::IntegrationTest
20 class RoutingCommentsTest < ActionDispatch::IntegrationTest
21 21 def test_comments
22 22 assert_routing(
23 23 { :method => 'post', :path => "/news/567/comments" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingContextMenusTest < ActionController::IntegrationTest
20 class RoutingContextMenusTest < ActionDispatch::IntegrationTest
21 21 def test_context_menus_time_entries
22 22 ["get", "post"].each do |method|
23 23 assert_routing(
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingCustomFieldsTest < ActionController::IntegrationTest
20 class RoutingCustomFieldsTest < ActionDispatch::IntegrationTest
21 21 def test_custom_fields
22 22 assert_routing(
23 23 { :method => 'get', :path => "/custom_fields" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingDocumentsTest < ActionController::IntegrationTest
20 class RoutingDocumentsTest < ActionDispatch::IntegrationTest
21 21 def test_documents_scoped_under_project
22 22 assert_routing(
23 23 { :method => 'get', :path => "/projects/567/documents" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingEnumerationsTest < ActionController::IntegrationTest
20 class RoutingEnumerationsTest < ActionDispatch::IntegrationTest
21 21 def test_enumerations
22 22 assert_routing(
23 23 { :method => 'get', :path => "/enumerations" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingFilesTest < ActionController::IntegrationTest
20 class RoutingFilesTest < ActionDispatch::IntegrationTest
21 21 def test_files
22 22 assert_routing(
23 23 { :method => 'get', :path => "/projects/33/files" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingGanttsTest < ActionController::IntegrationTest
20 class RoutingGanttsTest < ActionDispatch::IntegrationTest
21 21 def test_gantts
22 22 assert_routing(
23 23 { :method => 'get', :path => "/issues/gantt" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingGroupsTest < ActionController::IntegrationTest
20 class RoutingGroupsTest < ActionDispatch::IntegrationTest
21 21 def test_groups_resources
22 22 assert_routing(
23 23 { :method => 'get', :path => "/groups" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingIssueCategoriesTest < ActionController::IntegrationTest
20 class RoutingIssueCategoriesTest < ActionDispatch::IntegrationTest
21 21 def test_issue_categories_scoped_under_project
22 22 assert_routing(
23 23 { :method => 'get', :path => "/projects/foo/issue_categories" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingIssueRelationsTest < ActionController::IntegrationTest
20 class RoutingIssueRelationsTest < ActionDispatch::IntegrationTest
21 21 def test_issue_relations
22 22 assert_routing(
23 23 { :method => 'get', :path => "/issues/1/relations" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingIssueStatusesTest < ActionController::IntegrationTest
20 class RoutingIssueStatusesTest < ActionDispatch::IntegrationTest
21 21 def test_issue_statuses
22 22 assert_routing(
23 23 { :method => 'get', :path => "/issue_statuses" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingIssuesTest < ActionController::IntegrationTest
20 class RoutingIssuesTest < ActionDispatch::IntegrationTest
21 21 def test_issues_rest_actions
22 22 assert_routing(
23 23 { :method => 'get', :path => "/issues" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingJournalsTest < ActionController::IntegrationTest
20 class RoutingJournalsTest < ActionDispatch::IntegrationTest
21 21 def test_journals
22 22 assert_routing(
23 23 { :method => 'post', :path => "/issues/1/quoted" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingMailHandlerTest < ActionController::IntegrationTest
20 class RoutingMailHandlerTest < ActionDispatch::IntegrationTest
21 21 def test_mail_handler
22 22 assert_routing(
23 23 { :method => "post", :path => "/mail_handler" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingMembersTest < ActionController::IntegrationTest
20 class RoutingMembersTest < ActionDispatch::IntegrationTest
21 21 def test_members
22 22 assert_routing(
23 23 { :method => 'get', :path => "/projects/5234/memberships.xml" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingMessagesTest < ActionController::IntegrationTest
20 class RoutingMessagesTest < ActionDispatch::IntegrationTest
21 21 def test_messages
22 22 assert_routing(
23 23 { :method => 'get', :path => "/boards/22/topics/2" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingMyTest < ActionController::IntegrationTest
20 class RoutingMyTest < ActionDispatch::IntegrationTest
21 21 def test_my
22 22 ["get", "post"].each do |method|
23 23 assert_routing(
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingNewsTest < ActionController::IntegrationTest
20 class RoutingNewsTest < ActionDispatch::IntegrationTest
21 21 def test_news_index
22 22 assert_routing(
23 23 { :method => 'get', :path => "/news" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingPreviewsTest < ActionController::IntegrationTest
20 class RoutingPreviewsTest < ActionDispatch::IntegrationTest
21 21 def test_previews
22 22 ["get", "post", "put"].each do |method|
23 23 assert_routing(
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingProjectEnumerationsTest < ActionController::IntegrationTest
20 class RoutingProjectEnumerationsTest < ActionDispatch::IntegrationTest
21 21 def test_project_enumerations
22 22 assert_routing(
23 23 { :method => 'put', :path => "/projects/64/enumerations" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingProjectsTest < ActionController::IntegrationTest
20 class RoutingProjectsTest < ActionDispatch::IntegrationTest
21 21 def test_projects
22 22 assert_routing(
23 23 { :method => 'get', :path => "/projects" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingQueriesTest < ActionController::IntegrationTest
20 class RoutingQueriesTest < ActionDispatch::IntegrationTest
21 21 def test_queries
22 22 assert_routing(
23 23 { :method => 'get', :path => "/queries.xml" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingReportsTest < ActionController::IntegrationTest
20 class RoutingReportsTest < ActionDispatch::IntegrationTest
21 21 def test_reports
22 22 assert_routing(
23 23 { :method => 'get', :path => "/projects/567/issues/report" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingRepositoriesTest < ActionController::IntegrationTest
20 class RoutingRepositoriesTest < ActionDispatch::IntegrationTest
21 21 def setup
22 22 @path_hash = repository_path_hash(%w[path to file.c])
23 23 assert_equal "path/to/file.c", @path_hash[:path]
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingRolesTest < ActionController::IntegrationTest
20 class RoutingRolesTest < ActionDispatch::IntegrationTest
21 21 def test_roles
22 22 assert_routing(
23 23 { :method => 'get', :path => "/roles" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingSearchTest < ActionController::IntegrationTest
20 class RoutingSearchTest < ActionDispatch::IntegrationTest
21 21 def test_search
22 22 assert_routing(
23 23 { :method => 'get', :path => "/search" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingSettingsTest < ActionController::IntegrationTest
20 class RoutingSettingsTest < ActionDispatch::IntegrationTest
21 21 def test_settings
22 22 assert_routing(
23 23 { :method => 'get', :path => "/settings" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingSysTest < ActionController::IntegrationTest
20 class RoutingSysTest < ActionDispatch::IntegrationTest
21 21 def test_sys
22 22 assert_routing(
23 23 { :method => 'get', :path => "/sys/projects" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingTimelogsTest < ActionController::IntegrationTest
20 class RoutingTimelogsTest < ActionDispatch::IntegrationTest
21 21 def test_timelogs_global
22 22 assert_routing(
23 23 { :method => 'get', :path => "/time_entries" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingTrackersTest < ActionController::IntegrationTest
20 class RoutingTrackersTest < ActionDispatch::IntegrationTest
21 21 def test_trackers
22 22 assert_routing(
23 23 { :method => 'get', :path => "/trackers" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingUsersTest < ActionController::IntegrationTest
20 class RoutingUsersTest < ActionDispatch::IntegrationTest
21 21 def test_users
22 22 assert_routing(
23 23 { :method => 'get', :path => "/users" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingVersionsTest < ActionController::IntegrationTest
20 class RoutingVersionsTest < ActionDispatch::IntegrationTest
21 21 def test_roadmap
22 22 # /projects/foo/versions is /projects/foo/roadmap
23 23 assert_routing(
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingWatchersTest < ActionController::IntegrationTest
20 class RoutingWatchersTest < ActionDispatch::IntegrationTest
21 21 def test_watchers
22 22 assert_routing(
23 23 { :method => 'get', :path => "/watchers/new" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingWelcomeTest < ActionController::IntegrationTest
20 class RoutingWelcomeTest < ActionDispatch::IntegrationTest
21 21 def test_welcome
22 22 assert_routing(
23 23 { :method => 'get', :path => "/" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingWikiTest < ActionController::IntegrationTest
20 class RoutingWikiTest < ActionDispatch::IntegrationTest
21 21 def test_wiki_matching
22 22 assert_routing(
23 23 { :method => 'get', :path => "/projects/567/wiki" },
@@ -54,7 +54,7 class RoutingWikiTest < ActionController::IntegrationTest
54 54 :id => 'CookBook_documentation', :version => '2' }
55 55 )
56 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 58 assert_recognizes({}, {:method => 'get', :path => "/projects/1/wiki/CookBook_documentation/whatever"})
59 59 end
60 60 end
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingWikisTest < ActionController::IntegrationTest
20 class RoutingWikisTest < ActionDispatch::IntegrationTest
21 21 def test_wikis_plural_admin_setup
22 22 ["get", "post"].each do |method|
23 23 assert_routing(
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class RoutingWorkflowsTest < ActionController::IntegrationTest
20 class RoutingWorkflowsTest < ActionDispatch::IntegrationTest
21 21 def test_workflows
22 22 assert_routing(
23 23 { :method => 'get', :path => "/workflows" },
@@ -17,7 +17,7
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 class UsersTest < ActionController::IntegrationTest
20 class UsersTest < ActionDispatch::IntegrationTest
21 21 fixtures :users
22 22
23 23 def test_destroy_should_not_accept_get_requests
@@ -14,7 +14,7 module ObjectHelpers
14 14
15 15 def User.add_to_project(user, project, roles=nil)
16 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 18 Member.create!(:principal => user, :project => project, :roles => roles)
19 19 end
20 20
@@ -178,14 +178,6 module ObjectHelpers
178 178 changeset.save!
179 179 changeset
180 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 181 end
190 182
191 183 module IssueObjectHelpers
@@ -26,17 +26,25 include ObjectHelpers
26 26
27 27 require 'awesome_nested_set/version'
28 28
29 class ActionView::TestCase
30 helper :application
31 include ApplicationHelper
32 end
33
29 34 class ActiveSupport::TestCase
30 35 include ActionDispatch::TestProcess
36 include Shoulda::Context::Assertions
37 include Shoulda::Context::InstanceMethods
38 extend Shoulda::Context::ClassMethods
31 39
32 40 self.use_transactional_fixtures = true
33 41 self.use_instantiated_fixtures = false
34 42
35 ESCAPED_CANT = 'can&#x27;t'
36 ESCAPED_UCANT = 'Can&#x27;t'
43 #ESCAPED_CANT = 'can&#x27;t'
44 #ESCAPED_UCANT = 'Can&#x27;t'
37 45 # Rails 4.0.2
38 #ESCAPED_CANT = 'can&#39;t'
39 #ESCAPED_UCANT = 'Can&#39;t'
46 ESCAPED_CANT = 'can&#39;t'
47 ESCAPED_UCANT = 'Can&#39;t'
40 48
41 49 def log_user(login, password)
42 50 User.anonymous
@@ -147,7 +155,9 class ActiveSupport::TestCase
147 155
148 156 # Returns the path to the test +vendor+ repository
149 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 161 end
152 162
153 163 # Returns the url of the subversion test repository
@@ -388,8 +388,7 class ChangesetTest < ActiveSupport::TestCase
388 388 def test_comments_should_be_converted_to_utf8
389 389 proj = Project.find(3)
390 390 # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
391 str = "Texte encod\xe9 en ISO-8859-1."
392 str.force_encoding("ASCII-8BIT") if str.respond_to?(:force_encoding)
391 str = "Texte encod\xe9 en ISO-8859-1.".force_encoding("ASCII-8BIT")
393 392 r = Repository::Bazaar.create!(
394 393 :project => proj,
395 394 :url => '/tmp/test/bazaar',
@@ -401,18 +400,15 class ChangesetTest < ActiveSupport::TestCase
401 400 :scmid => '12345',
402 401 :comments => str)
403 402 assert( c.save )
404 str_utf8 = "Texte encod\xc3\xa9 en ISO-8859-1."
405 str_utf8.force_encoding("UTF-8") if str_utf8.respond_to?(:force_encoding)
403 str_utf8 = "Texte encod\xc3\xa9 en ISO-8859-1.".force_encoding("UTF-8")
406 404 assert_equal str_utf8, c.comments
407 405 end
408 406
409 407 def test_invalid_utf8_sequences_in_comments_should_be_replaced_latin1
410 408 proj = Project.find(3)
411 409 # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
412 str1 = "Texte encod\xe9 en ISO-8859-1."
413 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
414 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
415 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
410 str1 = "Texte encod\xe9 en ISO-8859-1.".force_encoding("UTF-8")
411 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test".force_encoding("ASCII-8BIT")
416 412 r = Repository::Bazaar.create!(
417 413 :project => proj,
418 414 :url => '/tmp/test/bazaar',
@@ -431,10 +427,7 class ChangesetTest < ActiveSupport::TestCase
431 427
432 428 def test_invalid_utf8_sequences_in_comments_should_be_replaced_ja_jis
433 429 proj = Project.find(3)
434 str = "test\xb5\xfetest\xb5\xfe"
435 if str.respond_to?(:force_encoding)
436 str.force_encoding('ASCII-8BIT')
437 end
430 str = "test\xb5\xfetest\xb5\xfe".force_encoding('ASCII-8BIT')
438 431 r = Repository::Bazaar.create!(
439 432 :project => proj,
440 433 :url => '/tmp/test/bazaar',
@@ -453,14 +446,12 class ChangesetTest < ActiveSupport::TestCase
453 446 s1 = "\xC2\x80"
454 447 s2 = "\xc3\x82\xc2\x80"
455 448 s4 = s2.dup
456 if s1.respond_to?(:force_encoding)
457 s3 = s1.dup
458 s1.force_encoding('ASCII-8BIT')
459 s2.force_encoding('ASCII-8BIT')
460 s3.force_encoding('ISO-8859-1')
461 s4.force_encoding('UTF-8')
462 assert_equal s3.encode('UTF-8'), s4
463 end
449 s3 = s1.dup
450 s1.force_encoding('ASCII-8BIT')
451 s2.force_encoding('ASCII-8BIT')
452 s3.force_encoding('ISO-8859-1')
453 s4.force_encoding('UTF-8')
454 assert_equal s3.encode('UTF-8'), s4
464 455 proj = Project.find(3)
465 456 r = Repository::Bazaar.create!(
466 457 :project => proj,
@@ -478,10 +469,8 class ChangesetTest < ActiveSupport::TestCase
478 469
479 470 def test_invalid_utf8_sequences_in_paths_should_be_replaced
480 471 proj = Project.find(3)
481 str1 = "Texte encod\xe9 en ISO-8859-1"
482 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
483 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
484 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
472 str1 = "Texte encod\xe9 en ISO-8859-1".force_encoding("UTF-8")
473 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test".force_encoding("ASCII-8BIT")
485 474 r = Repository::Bazaar.create!(
486 475 :project => proj,
487 476 :url => '/tmp/test/bazaar',
@@ -521,9 +510,7 class ChangesetTest < ActiveSupport::TestCase
521 510 assert( c.save )
522 511 assert_equal "", c.comments
523 512 assert_equal nil, c.committer
524 if c.comments.respond_to?(:force_encoding)
525 assert_equal "UTF-8", c.comments.encoding.to_s
526 end
513 assert_equal "UTF-8", c.comments.encoding.to_s
527 514 end
528 515
529 516 def test_comments_empty
@@ -542,10 +529,8 class ChangesetTest < ActiveSupport::TestCase
542 529 assert( c.save )
543 530 assert_equal "", c.comments
544 531 assert_equal "", c.committer
545 if c.comments.respond_to?(:force_encoding)
546 assert_equal "UTF-8", c.comments.encoding.to_s
547 assert_equal "UTF-8", c.committer.encoding.to_s
548 end
532 assert_equal "UTF-8", c.comments.encoding.to_s
533 assert_equal "UTF-8", c.committer.encoding.to_s
549 534 end
550 535
551 536 def test_comments_should_accept_more_than_64k
@@ -95,14 +95,12 class CustomFieldTest < ActiveSupport::TestCase
95 95 assert_equal ["One value", "And another one"], field.possible_values
96 96 end
97 97
98 if "string".respond_to?(:encoding)
99 def test_possible_values_stored_as_binary_should_be_utf8_encoded
100 field = CustomField.find(11)
101 assert_kind_of Array, field.possible_values
102 assert field.possible_values.size > 0
103 field.possible_values.each do |value|
104 assert_equal "UTF-8", value.encoding.name
105 end
98 def test_possible_values_stored_as_binary_should_be_utf8_encoded
99 field = CustomField.find(11)
100 assert_kind_of Array, field.possible_values
101 assert field.possible_values.size > 0
102 field.possible_values.each do |value|
103 assert_equal "UTF-8", value.encoding.name
106 104 end
107 105 end
108 106
@@ -22,7 +22,7 class EnabledModuleTest < ActiveSupport::TestCase
22 22
23 23 def test_enabling_wiki_should_create_a_wiki
24 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 26 assert_nil project.wiki
27 27 project.enabled_module_names = ['wiki']
28 28 project.reload
@@ -52,8 +52,7 class GroupTest < ActiveSupport::TestCase
52 52
53 53 def test_blank_name_error_message_fr
54 54 set_language_if_valid 'fr'
55 str = "Nom doit \xc3\xaatre renseign\xc3\xa9(e)"
56 str.force_encoding('UTF-8') if str.respond_to?(:force_encoding)
55 str = "Nom doit \xc3\xaatre renseign\xc3\xa9(e)".force_encoding('UTF-8')
57 56 g = Group.new
58 57 assert !g.save
59 58 assert_include str, g.errors.full_messages
@@ -133,20 +132,4 class GroupTest < ActiveSupport::TestCase
133 132
134 133 assert_equal nil, Issue.find(1).assigned_to_id
135 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 135 end
@@ -35,10 +35,7 class ApplicationHelperTest < ActionView::TestCase
35 35 def setup
36 36 super
37 37 set_tmp_attachments_directory
38 @russian_test = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82"
39 if @russian_test.respond_to?(:force_encoding)
40 @russian_test.force_encoding('UTF-8')
41 end
38 @russian_test = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82".force_encoding('UTF-8')
42 39 end
43 40
44 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 96 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
100 97 end
101 98
102 if 'ruby'.respond_to?(:encoding)
103 def test_auto_links_with_non_ascii_characters
104 to_test = {
105 "http://foo.bar/#{@russian_test}" =>
106 %|<a class="external" href="http://foo.bar/#{@russian_test}">http://foo.bar/#{@russian_test}</a>|
107 }
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'
99 def test_auto_links_with_non_ascii_characters
100 to_test = {
101 "http://foo.bar/#{@russian_test}" =>
102 %|<a class="external" href="http://foo.bar/#{@russian_test}">http://foo.bar/#{@russian_test}</a>|
103 }
104 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
112 105 end
113 106
114 107 def test_auto_mailto
@@ -254,16 +247,12 RAW
254 247 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
255 248 end
256 249
257 if 'ruby'.respond_to?(:encoding)
258 def test_textile_external_links_with_non_ascii_characters
259 to_test = {
260 %|This is a "link":http://foo.bar/#{@russian_test}| =>
261 %|This is a <a href="http://foo.bar/#{@russian_test}" class="external">link</a>|
262 }
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'
250 def test_textile_external_links_with_non_ascii_characters
251 to_test = {
252 %|This is a "link":http://foo.bar/#{@russian_test}| =>
253 %|This is a <a href="http://foo.bar/#{@russian_test}" class="external">link</a>|
254 }
255 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
267 256 end
268 257
269 258 def test_redmine_links
@@ -1336,13 +1325,8 RAW
1336 1325 project = Project.find(1)
1337 1326 assert_equal %(<a href="/projects/ecookbook">eCookbook</a>),
1338 1327 link_to_project(project)
1339 assert_equal %(<a href="/projects/ecookbook/settings">eCookbook</a>),
1340 link_to_project(project, :action => 'settings')
1341 1328 assert_equal %(<a href="http://test.host/projects/ecookbook?jump=blah">eCookbook</a>),
1342 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 1330 end
1347 1331
1348 1332 def test_link_to_project_settings
@@ -1433,7 +1417,7 RAW
1433 1417
1434 1418 def test_raw_json_should_escape_closing_tags
1435 1419 s = raw_json(["<foo>bar</foo>"])
1436 assert_equal '["<foo>bar<\/foo>"]', s
1420 assert_include '\/foo', s
1437 1421 end
1438 1422
1439 1423 def test_raw_json_should_be_html_safe
@@ -1508,8 +1492,7 RAW
1508 1492 end
1509 1493
1510 1494 def test_truncate_single_line_non_ascii
1511 ja = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"
1512 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
1495 ja = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e".force_encoding('UTF-8')
1513 1496 result = truncate_single_line_raw("#{ja}\n#{ja}\n#{ja}", 10)
1514 1497 assert_equal "#{ja} #{ja}...", result
1515 1498 assert !result.html_safe?
@@ -42,23 +42,16 class ProjectsHelperTest < ActionView::TestCase
42 42 def test_link_to_version_within_project
43 43 @project = Project.find(2)
44 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 46 end
47 47
48 48 def test_link_to_version
49 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))
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)
50 assert_equal '<a href="/versions/5">OnlineStore - Alpha</a>', link_to_version(Version.find(5))
58 51 end
59 52
60 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 55 end
63 56
64 57 def test_link_to_version_invalid_version
@@ -71,20 +64,11 class ProjectsHelperTest < ActionView::TestCase
71 64 end
72 65
73 66 def test_format_version_name
74 assert_equal "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)
67 assert_equal "eCookbook - 0.1", format_version_name(Version.find(1))
82 68 end
83 69
84 def test_format_version_name_for_shared_version_should_display_project_name
85 version = Version.find(1)
86 version.sharing = 'system'
87 assert_equal "eCookbook - 0.1", format_version_name(version)
70 def test_format_version_name_for_system_version
71 assert_equal "OnlineStore - Systemwide visible version", format_version_name(Version.find(7))
88 72 end
89 73
90 74 def test_version_options_for_select_with_no_versions
@@ -47,21 +47,19 class PatchesTest < ActiveSupport::TestCase
47 47 end
48 48
49 49 # https://github.com/rails/rails/pull/14198/files
50 if RUBY_VERSION >= "1.9"
51 def test_indifferent_select
52 hash = ActiveSupport::HashWithIndifferentAccess.new(@symbols).select { |_ ,v| v == 1 }
53 assert_equal({ 'a' => 1 }, hash)
54 assert_instance_of ((Rails::VERSION::MAJOR < 4 && RUBY_VERSION < "2.1") ?
55 Hash : ActiveSupport::HashWithIndifferentAccess),
56 hash
57 end
50 def test_indifferent_select
51 hash = ActiveSupport::HashWithIndifferentAccess.new(@symbols).select { |_ ,v| v == 1 }
52 assert_equal({ 'a' => 1 }, hash)
53 assert_instance_of ((Rails::VERSION::MAJOR < 4 && RUBY_VERSION < "2.1") ?
54 Hash : ActiveSupport::HashWithIndifferentAccess),
55 hash
56 end
58 57
59 def test_indifferent_select_bang
60 indifferent_strings = ActiveSupport::HashWithIndifferentAccess.new(@symbols)
61 indifferent_strings.select! { |_, v| v == 1 }
62 assert_equal({ 'a' => 1 }, indifferent_strings)
63 assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings
64 end
58 def test_indifferent_select_bang
59 indifferent_strings = ActiveSupport::HashWithIndifferentAccess.new(@symbols)
60 indifferent_strings.select! { |_, v| v == 1 }
61 assert_equal({ 'a' => 1 }, indifferent_strings)
62 assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings
65 63 end
66 64
67 65 def test_indifferent_reject
@@ -77,15 +75,13 class PatchesTest < ActiveSupport::TestCase
77 75 assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings
78 76 end
79 77
80 if RUBY_VERSION >= "1.9"
81 def test_select
82 assert_equal @keys, @ordered_hash.select { true }.map(&:first)
83 new_ordered_hash = @ordered_hash.select { true }
84 assert_equal @keys, new_ordered_hash.map(&:first)
85 assert_instance_of ((Rails::VERSION::MAJOR < 4 && RUBY_VERSION < "2.1") ?
86 Hash : ActiveSupport::OrderedHash),
87 new_ordered_hash
88 end
78 def test_select
79 assert_equal @keys, @ordered_hash.select { true }.map(&:first)
80 new_ordered_hash = @ordered_hash.select { true }
81 assert_equal @keys, new_ordered_hash.map(&:first)
82 assert_instance_of ((Rails::VERSION::MAJOR < 4 && RUBY_VERSION < "2.1") ?
83 Hash : ActiveSupport::OrderedHash),
84 new_ordered_hash
89 85 end
90 86
91 87 def test_reject
@@ -416,7 +416,7 class IssueNestedSetTest < ActiveSupport::TestCase
416 416 c.reload
417 417
418 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 420 assert ic1.root?
421 421 assert_equal ic1, ic2.parent
422 422 assert_equal ic1, ic3.parent
@@ -83,7 +83,7 class IssuePriorityTest < ActiveSupport::TestCase
83 83 IssuePriority.clear_position_names
84 84
85 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 87 end
88 88
89 89 def test_compute_position_names_without_default_priority_should_split_priorities
@@ -91,16 +91,16 class IssuePriorityTest < ActiveSupport::TestCase
91 91 IssuePriority.update_all :is_default => false
92 92
93 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 95 end
96 96
97 97 def test_adding_a_priority_should_update_position_names
98 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 100 end
101 101
102 102 def test_destroying_a_priority_should_update_position_names
103 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 105 end
106 106 end
@@ -112,7 +112,7 class IssueStatusTest < ActiveSupport::TestCase
112 112 end
113 113
114 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 116 end
117 117
118 118 def test_named_scope
@@ -204,7 +204,7 class IssueTest < ActiveSupport::TestCase
204 204
205 205 def test_visible_scope_for_anonymous
206 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 208 assert issues.any?
209 209 assert_nil issues.detect {|issue| !issue.project.is_public?}
210 210 assert_nil issues.detect {|issue| issue.is_private?}
@@ -214,7 +214,7 class IssueTest < ActiveSupport::TestCase
214 214 def test_visible_scope_for_anonymous_without_view_issues_permissions
215 215 # Anonymous user should not see issues without permission
216 216 Role.anonymous.remove_permission!(:view_issues)
217 issues = Issue.visible(User.anonymous).all
217 issues = Issue.visible(User.anonymous).to_a
218 218 assert issues.empty?
219 219 assert_visibility_match User.anonymous, issues
220 220 end
@@ -247,7 +247,7 class IssueTest < ActiveSupport::TestCase
247 247 user = User.find(9)
248 248 assert user.projects.empty?
249 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 251 assert issues.any?
252 252 assert_nil issues.detect {|issue| !issue.project.is_public?}
253 253 assert_nil issues.detect {|issue| issue.is_private?}
@@ -259,7 +259,7 class IssueTest < ActiveSupport::TestCase
259 259 Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 9, :subject => 'Issue by non member')
260 260 user = User.find(9)
261 261
262 issues = Issue.visible(user).all
262 issues = Issue.visible(user).to_a
263 263 assert issues.any?
264 264 assert_nil issues.detect {|issue| issue.author != user}
265 265 assert_visibility_match user, issues
@@ -270,7 +270,7 class IssueTest < ActiveSupport::TestCase
270 270 Role.non_member.remove_permission!(:view_issues)
271 271 user = User.find(9)
272 272 assert user.projects.empty?
273 issues = Issue.visible(user).all
273 issues = Issue.visible(user).to_a
274 274 assert issues.empty?
275 275 assert_visibility_match user, issues
276 276 end
@@ -291,7 +291,7 class IssueTest < ActiveSupport::TestCase
291 291 # User should see issues of projects for which user has view_issues permissions only
292 292 Role.non_member.remove_permission!(:view_issues)
293 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 295 assert issues.any?
296 296 assert_nil issues.detect {|issue| issue.project_id != 3}
297 297 assert_nil issues.detect {|issue| issue.is_private?}
@@ -311,12 +311,12 class IssueTest < ActiveSupport::TestCase
311 311 :is_private => true)
312 312
313 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 315 assert issues.any?
316 316 assert issues.include?(issue)
317 317
318 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 320 assert issues.any?
321 321 assert issues.include?(issue)
322 322 end
@@ -325,7 +325,7 class IssueTest < ActiveSupport::TestCase
325 325 user = User.find(1)
326 326 user.members.each(&:destroy)
327 327 assert user.projects.empty?
328 issues = Issue.visible(user).all
328 issues = Issue.visible(user).to_a
329 329 assert issues.any?
330 330 # Admin should see issues on private projects that admin does not belong to
331 331 assert issues.detect {|issue| !issue.project.is_public?}
@@ -336,7 +336,7 class IssueTest < ActiveSupport::TestCase
336 336
337 337 def test_visible_scope_with_project
338 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 340 projects = issues.collect(&:project).uniq
341 341 assert_equal 1, projects.size
342 342 assert_equal project, projects.first
@@ -344,7 +344,7 class IssueTest < ActiveSupport::TestCase
344 344
345 345 def test_visible_scope_with_project_and_subprojects
346 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 348 projects = issues.collect(&:project).uniq
349 349 assert projects.size > 1
350 350 assert_equal [], projects.select {|p| !p.is_or_is_descendant_of?(project)}
@@ -370,13 +370,20 class IssueTest < ActiveSupport::TestCase
370 370 assert_equal 2, parent.descendants.visible(user).collect{|i| i}.size
371 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 380 def test_open_scope
374 issues = Issue.open.all
381 issues = Issue.open.to_a
375 382 assert_nil issues.detect(&:closed?)
376 383 end
377 384
378 385 def test_open_scope_with_arg
379 issues = Issue.open(false).all
386 issues = Issue.open(false).to_a
380 387 assert_equal issues, issues.select(&:closed?)
381 388 end
382 389
@@ -1289,7 +1296,7 class IssueTest < ActiveSupport::TestCase
1289 1296 end
1290 1297
1291 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 1300 copy.save!
1294 1301 assert_equal 0, copy.reload.journals.size
1295 1302 end
@@ -1325,7 +1332,7 class IssueTest < ActiveSupport::TestCase
1325 1332 test "#copy should create a journal with notes" do
1326 1333 date = Date.today
1327 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 1336 copy.init_journal(User.current, notes)
1330 1337 copy.save!
1331 1338
@@ -1619,13 +1626,15 class IssueTest < ActiveSupport::TestCase
1619 1626 issue2.reload
1620 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',
1623 :project_id => 1, :tracker_id => 1, :status_id => 1, :subject => 'Child', :author_id => 1)
1624 assert !child.valid?
1625 assert_include 'Start date cannot be earlier than 10/18/2012 because of preceding issues', child.errors.full_messages
1626 assert_equal Date.parse('2012-10-18'), child.soonest_start
1627 child.start_date = '2012-10-18'
1628 assert child.save
1629 with_settings :date_format => '%m/%d/%Y' do
1630 child = Issue.new(:parent_issue_id => issue2.id, :start_date => '2012-10-16',
1631 :project_id => 1, :tracker_id => 1, :status_id => 1, :subject => 'Child', :author_id => 1)
1632 assert !child.valid?
1633 assert_include 'Start date cannot be earlier than 10/18/2012 because of preceding issues', child.errors.full_messages
1634 assert_equal Date.parse('2012-10-18'), child.soonest_start
1635 child.start_date = '2012-10-18'
1636 assert child.save
1637 end
1629 1638 end
1630 1639
1631 1640 def test_setting_parent_to_a_dependent_issue_should_not_validate
@@ -27,6 +27,7 class JournalTest < ActiveSupport::TestCase
27 27
28 28 def setup
29 29 @journal = Journal.find 1
30 User.current = nil
30 31 end
31 32
32 33 def test_journalized_is_an_issue
@@ -119,12 +120,12 class JournalTest < ActiveSupport::TestCase
119 120
120 121 def test_visible_scope_for_anonymous
121 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 124 assert journals.any?
124 125 assert_nil journals.detect {|journal| !journal.issue.project.is_public?}
125 126 # Anonymous user should not see issues without permission
126 127 Role.anonymous.remove_permission!(:view_issues)
127 journals = Journal.visible(User.anonymous).all
128 journals = Journal.visible(User.anonymous).to_a
128 129 assert journals.empty?
129 130 end
130 131
@@ -132,18 +133,18 class JournalTest < ActiveSupport::TestCase
132 133 user = User.find(9)
133 134 assert user.projects.empty?
134 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 137 assert journals.any?
137 138 assert_nil journals.detect {|journal| !journal.issue.project.is_public?}
138 139 # Non member user should not see issues without permission
139 140 Role.non_member.remove_permission!(:view_issues)
140 141 user.reload
141 journals = Journal.visible(user).all
142 journals = Journal.visible(user).to_a
142 143 assert journals.empty?
143 144 # User should see issues of projects for which user has view_issues permissions only
144 145 Member.create!(:principal => user, :project_id => 1, :role_ids => [1])
145 146 user.reload
146 journals = Journal.visible(user).all
147 journals = Journal.visible(user).to_a
147 148 assert journals.any?
148 149 assert_nil journals.detect {|journal| journal.issue.project_id != 1}
149 150 end
@@ -152,7 +153,7 class JournalTest < ActiveSupport::TestCase
152 153 user = User.find(1)
153 154 user.members.each(&:destroy)
154 155 assert user.projects.empty?
155 journals = Journal.visible(user).all
156 journals = Journal.visible(user).to_a
156 157 assert journals.any?
157 158 # Admin should see issues on private projects that admin does not belong to
158 159 assert journals.detect {|journal| !journal.issue.project.is_public?}
@@ -21,14 +21,9 class Redmine::CodesetUtilTest < ActiveSupport::TestCase
21 21
22 22 def test_to_utf8_by_setting_from_latin1
23 23 with_settings :repositories_encodings => 'UTF-8,ISO-8859-1' do
24 s1 = "Texte encod\xc3\xa9"
25 s2 = "Texte encod\xe9"
26 s3 = s2.dup
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
24 s1 = "Texte encod\xc3\xa9".force_encoding("UTF-8")
25 s2 = "Texte encod\xe9".force_encoding("ASCII-8BIT")
26 s3 = s2.dup.force_encoding("UTF-8")
32 27 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s2)
33 28 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s3)
34 29 end
@@ -36,14 +31,9 class Redmine::CodesetUtilTest < ActiveSupport::TestCase
36 31
37 32 def test_to_utf8_by_setting_from_euc_jp
38 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"
40 s2 = "\xa5\xec\xa5\xc3\xa5\xc9\xa5\xde\xa5\xa4\xa5\xf3"
41 s3 = s2.dup
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
34 s1 = "\xe3\x83\xac\xe3\x83\x83\xe3\x83\x89\xe3\x83\x9e\xe3\x82\xa4\xe3\x83\xb3".force_encoding("UTF-8")
35 s2 = "\xa5\xec\xa5\xc3\xa5\xc9\xa5\xde\xa5\xa4\xa5\xf3".force_encoding("ASCII-8BIT")
36 s3 = s2.dup.force_encoding("UTF-8")
47 37 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s2)
48 38 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s3)
49 39 end
@@ -51,14 +41,9 class Redmine::CodesetUtilTest < ActiveSupport::TestCase
51 41
52 42 def test_to_utf8_by_setting_should_be_converted_all_latin1
53 43 with_settings :repositories_encodings => 'ISO-8859-1' do
54 s1 = "\xc3\x82\xc2\x80"
55 s2 = "\xC2\x80"
56 s3 = s2.dup
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
44 s1 = "\xc3\x82\xc2\x80".force_encoding("UTF-8")
45 s2 = "\xC2\x80".force_encoding("ASCII-8BIT")
46 s3 = s2.dup.force_encoding("UTF-8")
62 47 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s2)
63 48 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s3)
64 49 end
@@ -70,45 +55,33 class Redmine::CodesetUtilTest < ActiveSupport::TestCase
70 55 end
71 56
72 57 def test_to_utf8_by_setting_returns_ascii_as_utf8
73 s1 = "ASCII"
74 s2 = s1.dup
75 if s1.respond_to?(:force_encoding)
76 s1.force_encoding("UTF-8")
77 s2.force_encoding("ISO-8859-1")
78 end
58 s1 = "ASCII".force_encoding("UTF-8")
59 s2 = s1.dup.force_encoding("ISO-8859-1")
79 60 str1 = Redmine::CodesetUtil.to_utf8_by_setting(s1)
80 61 str2 = Redmine::CodesetUtil.to_utf8_by_setting(s2)
81 62 assert_equal s1, str1
82 63 assert_equal s1, str2
83 if s1.respond_to?(:force_encoding)
84 assert_equal "UTF-8", str1.encoding.to_s
85 assert_equal "UTF-8", str2.encoding.to_s
86 end
64 assert_equal "UTF-8", str1.encoding.to_s
65 assert_equal "UTF-8", str2.encoding.to_s
87 66 end
88 67
89 68 def test_to_utf8_by_setting_invalid_utf8_sequences_should_be_stripped
90 69 with_settings :repositories_encodings => '' do
91 70 # s1 = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
92 s1 = "Texte encod\xe9 en ISO-8859-1."
93 s1.force_encoding("ASCII-8BIT") if s1.respond_to?(:force_encoding)
71 s1 = "Texte encod\xe9 en ISO-8859-1.".force_encoding("ASCII-8BIT")
94 72 str = Redmine::CodesetUtil.to_utf8_by_setting(s1)
95 if str.respond_to?(:force_encoding)
96 assert str.valid_encoding?
97 assert_equal "UTF-8", str.encoding.to_s
98 end
73 assert str.valid_encoding?
74 assert_equal "UTF-8", str.encoding.to_s
99 75 assert_equal "Texte encod? en ISO-8859-1.", str
100 76 end
101 77 end
102 78
103 79 def test_to_utf8_by_setting_invalid_utf8_sequences_should_be_stripped_ja_jis
104 80 with_settings :repositories_encodings => 'ISO-2022-JP' do
105 s1 = "test\xb5\xfetest\xb5\xfe"
106 s1.force_encoding("ASCII-8BIT") if s1.respond_to?(:force_encoding)
81 s1 = "test\xb5\xfetest\xb5\xfe".force_encoding("ASCII-8BIT")
107 82 str = Redmine::CodesetUtil.to_utf8_by_setting(s1)
108 if str.respond_to?(:force_encoding)
109 assert str.valid_encoding?
110 assert_equal "UTF-8", str.encoding.to_s
111 end
83 assert str.valid_encoding?
84 assert_equal "UTF-8", str.encoding.to_s
112 85 assert_equal "test??test??", str
113 86 end
114 87 end
@@ -27,11 +27,10 class PdfTest < ActiveSupport::TestCase
27 27 end
28 28
29 29 def test_rdm_pdf_iconv_cannot_convert_ja_cp932
30 encoding = ( RUBY_PLATFORM == 'java' ? "SJIS" : "CP932" )
31 30 utf8_txt_1 = "\xe7\x8b\x80\xe6\x85\x8b"
32 31 utf8_txt_2 = "\xe7\x8b\x80\xe6\x85\x8b\xe7\x8b\x80"
33 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 34 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_1, encoding)
36 35 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_2, encoding)
37 36 txt_3 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_3, encoding)
@@ -41,88 +40,65 class PdfTest < ActiveSupport::TestCase
41 40 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
42 41 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
43 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 43 end
59 44 end
60 45
61 46 def test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_en
62 str1 = "Texte encod\xe9 en ISO-8859-1"
63 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
64 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
65 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
47 str1 = "Texte encod\xe9 en ISO-8859-1".force_encoding("UTF-8")
48 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test".force_encoding("ASCII-8BIT")
66 49 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str1, 'UTF-8')
67 50 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str2, 'UTF-8')
68 if txt_1.respond_to?(:force_encoding)
69 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
70 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
71 end
51 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
52 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
72 53 assert_equal "Texte encod? en ISO-8859-1", txt_1
73 54 assert_equal "?a?b?c?d?e test", txt_2
74 55 end
75 56
76 57 def test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_ja
77 str1 = "Texte encod\xe9 en ISO-8859-1"
78 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
79 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
80 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
58 str1 = "Texte encod\xe9 en ISO-8859-1".force_encoding("UTF-8")
59 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test".force_encoding("ASCII-8BIT")
81 60 encoding = ( RUBY_PLATFORM == 'java' ? "SJIS" : "CP932" )
82 61 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str1, encoding)
83 62 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str2, encoding)
84 if txt_1.respond_to?(:force_encoding)
85 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
86 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
87 end
63 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
64 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
88 65 assert_equal "Texte encod? en ISO-8859-1", txt_1
89 66 assert_equal "?a?b?c?d?e test", txt_2
90 67 end
91 68
92 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"
96 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
97 encoding = ( RUBY_PLATFORM == 'java' ? "SJIS" : "CP932" )
73 str2 = "\x83e\x83X\x83g".force_encoding("ASCII-8BIT")
98 74
99 a1 = Attachment.find(17)
100 a2 = Attachment.find(19)
75 a1 = Attachment.find(17)
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)
103 assert a1.readable?
104 assert a1.visible?
105 assert a2.readable?
106 assert a2.visible?
83 aa1 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "Testfile.PNG", "UTF-8")
84 assert_not_nil aa1
85 assert_equal 17, aa1.id
107 86
108 aa1 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "Testfile.PNG", "UTF-8")
109 assert_not_nil aa1
110 assert_equal 17, aa1.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
87 aa2 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "test#{str2}.png", encoding)
88 assert_not_nil aa2
89 assert_equal 19, aa2.id
114 90
115 User.current = nil
116 assert a1.readable?
117 assert (! a1.visible?)
118 assert a2.readable?
119 assert (! a2.visible?)
91 User.current = nil
92 assert a1.readable?
93 assert (! a1.visible?)
94 assert a2.readable?
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")
122 assert_equal nil, aa1
123 aa2 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "test#{str2}.png", encoding)
124 assert_equal nil, aa2
125
126 set_tmp_attachments_directory
101 set_tmp_attachments_directory
102 end
127 103 end
128 104 end
@@ -66,6 +66,7 class Redmine::Hook::ManagerTest < ActionView::TestCase
66 66
67 67 def setup
68 68 @hook_module = Redmine::Hook
69 @hook_module.clear_listeners
69 70 end
70 71
71 72 def teardown
@@ -32,47 +32,50 class Redmine::I18nTest < ActiveSupport::TestCase
32 32 def test_date_format_default
33 33 set_language_if_valid 'en'
34 34 today = Date.today
35 Setting.date_format = ''
36 assert_equal I18n.l(today), format_date(today)
35 with_settings :date_format => '' do
36 assert_equal I18n.l(today), format_date(today)
37 end
37 38 end
38 39
39 40 def test_date_format
40 41 set_language_if_valid 'en'
41 42 today = Date.today
42 Setting.date_format = '%d %m %Y'
43 assert_equal today.strftime('%d %m %Y'), format_date(today)
43 with_settings :date_format => '%d %m %Y' do
44 assert_equal today.strftime('%d %m %Y'), format_date(today)
45 end
44 46 end
45 47
46 48 def test_date_format_default_with_user_locale
47 49 set_language_if_valid 'es'
48 50 today = now = Time.parse('2011-02-20 14:00:00')
49 Setting.date_format = '%d %B %Y'
50 User.current.language = 'fr'
51 s1 = "20 f\xc3\xa9vrier 2011"
52 s1.force_encoding("UTF-8") if s1.respond_to?(:force_encoding)
53 assert_equal s1, format_date(today)
54 User.current.language = nil
55 assert_equal '20 Febrero 2011', format_date(today)
51 with_settings :date_format => '%d %B %Y' do
52 User.current.language = 'fr'
53 s1 = "20 f\xc3\xa9vrier 2011".force_encoding("UTF-8")
54 assert_equal s1, format_date(today)
55 User.current.language = nil
56 assert_equal '20 Febrero 2011', format_date(today)
57 end
56 58 end
57 59
58 60 def test_date_and_time_for_each_language
59 Setting.date_format = ''
60 valid_languages.each do |lang|
61 set_language_if_valid lang
62 assert_nothing_raised "#{lang} failure" do
63 format_date(Date.today)
64 format_time(Time.now)
65 format_time(Time.now, false)
66 assert_not_equal 'default', ::I18n.l(Date.today, :format => :default),
67 "date.formats.default missing in #{lang}"
68 assert_not_equal 'time', ::I18n.l(Time.now, :format => :time),
69 "time.formats.time missing in #{lang}"
61 with_settings :date_format => '' do
62 valid_languages.each do |lang|
63 set_language_if_valid lang
64 assert_nothing_raised "#{lang} failure" do
65 format_date(Date.today)
66 format_time(Time.now)
67 format_time(Time.now, false)
68 assert_not_equal 'default', ::I18n.l(Date.today, :format => :default),
69 "date.formats.default missing in #{lang}"
70 assert_not_equal 'time', ::I18n.l(Time.now, :format => :time),
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 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 79 end
77 80 end
78 81
@@ -134,10 +137,10 class Redmine::I18nTest < ActiveSupport::TestCase
134 137 def test_utc_time_format
135 138 set_language_if_valid 'en'
136 139 now = Time.now
137 Setting.date_format = '%d %m %Y'
138 Setting.time_format = '%H %M'
139 assert_equal now.strftime('%d %m %Y %H %M'), format_time(now.utc)
140 assert_equal now.strftime('%H %M'), format_time(now.utc, false)
140 with_settings :date_format => '%d %m %Y', :time_format => '%H %M' do
141 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)
143 end
141 144 end
142 145
143 146 def test_number_to_human_size_for_each_language
@@ -175,8 +178,7 class Redmine::I18nTest < ActiveSupport::TestCase
175 178 set_language_if_valid 'bs'
176 179 assert_equal "KM -1000,20", number_to_currency(-1000.2)
177 180 set_language_if_valid 'de'
178 euro_sign = "\xe2\x82\xac"
179 euro_sign.force_encoding('UTF-8') if euro_sign.respond_to?(:force_encoding)
181 euro_sign = "\xe2\x82\xac".force_encoding('UTF-8')
180 182 assert_equal "-1000,20 #{euro_sign}", number_to_currency(-1000.2)
181 183 end
182 184
@@ -193,8 +195,7 class Redmine::I18nTest < ActiveSupport::TestCase
193 195 assert_nil options.detect {|option| option.size != 2}
194 196 assert_nil options.detect {|option| !option.first.is_a?(String) || !option.last.is_a?(String)}
195 197 assert_include ["English", "en"], options
196 ja = "Japanese (\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e)"
197 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
198 ja = "Japanese (\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e)".force_encoding('UTF-8')
198 199 assert_include [ja, "ja"], options
199 200 end
200 201
@@ -238,30 +239,21 class Redmine::I18nTest < ActiveSupport::TestCase
238 239
239 240 def test_utf8
240 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 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 244 assert_equal str_ja_yes, i18n_ja_yes
245 assert_equal "UTF-8", i18n_ja_yes.encoding.to_s
248 246 end
249 247
250 248 def test_traditional_chinese_locale
251 249 set_language_if_valid 'zh-TW'
252 str_tw = "Traditional Chinese (\xe7\xb9\x81\xe9\xab\x94\xe4\xb8\xad\xe6\x96\x87)"
253 if str_tw.respond_to?(:force_encoding)
254 str_tw.force_encoding('UTF-8')
255 end
250 str_tw = "Traditional Chinese (\xe7\xb9\x81\xe9\xab\x94\xe4\xb8\xad\xe6\x96\x87)".force_encoding('UTF-8')
256 251 assert_equal str_tw, l(:general_lang_name)
257 252 end
258 253
259 254 def test_french_locale
260 255 set_language_if_valid 'fr'
261 str_fr = "Fran\xc3\xa7ais"
262 if str_fr.respond_to?(:force_encoding)
263 str_fr.force_encoding('UTF-8')
264 end
256 str_fr = "Fran\xc3\xa7ais".force_encoding('UTF-8')
265 257 assert_equal str_fr, l(:general_lang_name)
266 258 end
267 259 end
@@ -16,210 +16,203
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 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
34 to_test = { "Bazaar (bzr) 2.1.2\n" => [2,1,2],
35 "2.1.1\n1.7\n1.8" => [2,1,1],
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
20 class BazaarAdapterTest < ActiveSupport::TestCase
21 REPOSITORY_PATH = Rails.root.join('tmp/test/bazaar_repository').to_s
22 REPOSITORY_PATH.gsub!(/\/+/, '/')
41 23
42 def test_cat
43 cat = @adapter.cat('directory/document.txt')
44 assert cat =~ /Write the contents of a file as of a given revision to standard output/
45 end
24 if File.directory?(REPOSITORY_PATH)
25 def setup
26 @adapter = Redmine::Scm::Adapters::BazaarAdapter.new(
27 File.join(REPOSITORY_PATH, "trunk")
28 )
29 end
46 30
47 def test_cat_path_invalid
48 assert_nil @adapter.cat('invalid')
31 def test_scm_version
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 37 end
38 end
50 39
51 def test_cat_revision_invalid
52 assert_nil @adapter.cat('doc-mkdir.txt', '12345678')
53 end
40 def test_cat
41 cat = @adapter.cat('directory/document.txt')
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
56 diff1 = @adapter.diff('doc-mkdir.txt', 3, 2)
57 assert_equal 21, diff1.size
58 buf = diff1[14].gsub(/\r\n|\r|\n/, "")
59 assert_equal "-Display more information.", buf
60 end
45 def test_cat_path_invalid
46 assert_nil @adapter.cat('invalid')
47 end
61 48
62 def test_diff_path_invalid
63 assert_equal [], @adapter.diff('invalid', 1)
64 end
49 def test_cat_revision_invalid
50 assert_nil @adapter.cat('doc-mkdir.txt', '12345678')
51 end
65 52
66 def test_diff_revision_invalid
67 assert_equal [], @adapter.diff(nil, 12345678)
68 assert_equal [], @adapter.diff(nil, 12345678, 87654321)
69 end
53 def test_diff
54 diff1 = @adapter.diff('doc-mkdir.txt', 3, 2)
55 assert_equal 21, diff1.size
56 buf = diff1[14].gsub(/\r\n|\r|\n/, "")
57 assert_equal "-Display more information.", buf
58 end
70 59
71 def test_annotate
72 annotate = @adapter.annotate('doc-mkdir.txt')
73 assert_equal 17, annotate.lines.size
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
60 def test_diff_path_invalid
61 assert_equal [], @adapter.diff('invalid', 1)
62 end
78 63
79 def test_annotate_path_invalid
80 assert_nil @adapter.annotate('invalid')
81 end
64 def test_diff_revision_invalid
65 assert_equal [], @adapter.diff(nil, 12345678)
66 assert_equal [], @adapter.diff(nil, 12345678, 87654321)
67 end
82 68
83 def test_annotate_revision_invalid
84 assert_nil @adapter.annotate('doc-mkdir.txt', '12345678')
85 end
69 def test_annotate
70 annotate = @adapter.annotate('doc-mkdir.txt')
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
88 p = "c:\\test\\test\\"
89 bcp = Redmine::Scm::Adapters::BazaarAdapter.branch_conf_path(p)
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
77 def test_annotate_path_invalid
78 assert_nil @adapter.annotate('invalid')
79 end
104 80
105 def test_append_revisions_only_true
106 assert_equal true, @adapter.append_revisions_only
107 end
81 def test_annotate_revision_invalid
82 assert_nil @adapter.annotate('doc-mkdir.txt', '12345678')
83 end
108 84
109 def test_append_revisions_only_false
110 adpt = Redmine::Scm::Adapters::BazaarAdapter.new(
111 File.join(REPOSITORY_PATH, "empty-branch")
112 )
113 assert_equal false, adpt.append_revisions_only
114 end
85 def test_branch_conf_path
86 p = "c:\\test\\test\\"
87 bcp = Redmine::Scm::Adapters::BazaarAdapter.branch_conf_path(p)
88 assert_equal File.join("c:\\test\\test", ".bzr", "branch", "branch.conf"), bcp
89 p = "c:\\test\\test\\.bzr"
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
117 adpt = Redmine::Scm::Adapters::BazaarAdapter.new(
118 REPOSITORY_PATH
119 )
120 assert_equal false, adpt.append_revisions_only
121 end
103 def test_append_revisions_only_true
104 assert_equal true, @adapter.append_revisions_only
105 end
122 106
123 def test_info_not_nil
124 assert_not_nil @adapter.info
125 end
107 def test_append_revisions_only_false
108 adpt = Redmine::Scm::Adapters::BazaarAdapter.new(
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
128 adpt = Redmine::Scm::Adapters::BazaarAdapter.new(
129 "/invalid/invalid/"
130 )
131 assert_nil adpt.info
132 end
114 def test_append_revisions_only_shared_repo
115 adpt = Redmine::Scm::Adapters::BazaarAdapter.new(
116 REPOSITORY_PATH
117 )
118 assert_equal false, adpt.append_revisions_only
119 end
133 120
134 def test_info
135 info = @adapter.info
136 assert_equal 4, info.lastrev.identifier.to_i
137 end
121 def test_info_not_nil
122 assert_not_nil @adapter.info
123 end
138 124
139 def test_info_emtpy
140 adpt = Redmine::Scm::Adapters::BazaarAdapter.new(
141 File.join(REPOSITORY_PATH, "empty-branch")
142 )
143 assert_equal 0, adpt.info.lastrev.identifier.to_i
144 end
125 def test_info_nil
126 adpt = Redmine::Scm::Adapters::BazaarAdapter.new(
127 "/invalid/invalid/"
128 )
129 assert_nil adpt.info
130 end
145 131
146 def test_entries_path_invalid
147 assert_equal [], @adapter.entries('invalid')
148 end
132 def test_info
133 info = @adapter.info
134 assert_equal 4, info.lastrev.identifier.to_i
135 end
149 136
150 def test_entries_revision_invalid
151 assert_nil @adapter.entries(nil, 12345678)
152 end
137 def test_info_emtpy
138 adpt = Redmine::Scm::Adapters::BazaarAdapter.new(
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
155 revisions = @adapter.revisions(nil, 4, 2)
156 assert_equal 3, revisions.size
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
144 def test_entries_path_invalid
145 assert_equal [], @adapter.entries('invalid')
146 end
169 147
170 def test_revisions_path_invalid
171 assert_nil @adapter.revisions('invalid')
172 end
148 def test_entries_revision_invalid
149 assert_nil @adapter.entries(nil, 12345678)
150 end
173 151
174 def test_revisions_revision_invalid
175 assert_nil @adapter.revisions(nil, 12345678)
176 assert_nil @adapter.revisions(nil, 12345678, 87654321)
177 end
152 def test_revisions
153 revisions = @adapter.revisions(nil, 4, 2)
154 assert_equal 3, revisions.size
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
180 entry = @adapter.entry()
181 assert_equal "", entry.path
182 assert_equal "dir", entry.kind
183 entry = @adapter.entry('')
184 assert_equal "", entry.path
168 def test_revisions_path_invalid
169 assert_nil @adapter.revisions('invalid')
170 end
171
172 def test_revisions_revision_invalid
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 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 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)
213 @adapter.class.expects(:scm_version_from_command_line).returns(scm_command_version)
214 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
210 def test_scm_version_for(scm_command_version, version)
211 @adapter.class.expects(:scm_version_from_command_line).returns(scm_command_version)
212 assert_equal version, @adapter.class.scm_command_version
219 213 end
220 end
221 rescue LoadError
222 class BazaarMochaFake < ActiveSupport::TestCase
223 def test_fake; assert(false, "Requires mocha to run those tests") end
214 else
215 puts "Bazaar test repository NOT FOUND. Skipping unit tests !!!"
216 def test_fake; assert true end
224 217 end
225 218 end
@@ -16,100 +16,91
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../../../../test_helper', __FILE__)
19 begin
20 require 'mocha/setup'
21 19
22 class CvsAdapterTest < ActiveSupport::TestCase
23 REPOSITORY_PATH = Rails.root.join('tmp/test/cvs_repository').to_s
24 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
25 MODULE_NAME = 'test'
20 class CvsAdapterTest < ActiveSupport::TestCase
21 REPOSITORY_PATH = Rails.root.join('tmp/test/cvs_repository').to_s
22 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
23 MODULE_NAME = 'test'
26 24
27 if File.directory?(REPOSITORY_PATH)
28 def setup
29 @adapter = Redmine::Scm::Adapters::CvsAdapter.new(MODULE_NAME, REPOSITORY_PATH)
30 end
25 if File.directory?(REPOSITORY_PATH)
26 def setup
27 @adapter = Redmine::Scm::Adapters::CvsAdapter.new(MODULE_NAME, REPOSITORY_PATH)
28 end
31 29
32 def test_scm_version
33 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],
35 "1.12.11\r\n1.12.10\r\n" => [1,12,11]}
36 to_test.each do |s, v|
37 test_scm_version_for(s, v)
38 end
30 def test_scm_version
31 to_test = { "\nConcurrent Versions System (CVS) 1.12.13 (client/server)\n" => [1,12,13],
32 "\r\n1.12.12\r\n1.12.11" => [1,12,12],
33 "1.12.11\r\n1.12.10\r\n" => [1,12,11]}
34 to_test.each do |s, v|
35 test_scm_version_for(s, v)
39 36 end
37 end
40 38
41 def test_revisions_all
42 cnt = 0
43 @adapter.revisions('', nil, nil, :log_encoding => 'UTF-8') do |revision|
44 cnt += 1
45 end
46 assert_equal 16, cnt
39 def test_revisions_all
40 cnt = 0
41 @adapter.revisions('', nil, nil, :log_encoding => 'UTF-8') do |revision|
42 cnt += 1
47 43 end
44 assert_equal 16, cnt
45 end
48 46
49 def test_revisions_from_rev3
50 rev3_committed_on = Time.gm(2007, 12, 13, 16, 27, 22)
51 cnt = 0
52 @adapter.revisions('', rev3_committed_on, nil, :log_encoding => 'UTF-8') do |revision|
53 cnt += 1
54 end
55 assert_equal 4, cnt
47 def test_revisions_from_rev3
48 rev3_committed_on = Time.gm(2007, 12, 13, 16, 27, 22)
49 cnt = 0
50 @adapter.revisions('', rev3_committed_on, nil, :log_encoding => 'UTF-8') do |revision|
51 cnt += 1
56 52 end
53 assert_equal 4, cnt
54 end
57 55
58 def test_entries_rev3
59 rev3_committed_on = Time.gm(2007, 12, 13, 16, 27, 22)
60 entries = @adapter.entries('sources', rev3_committed_on)
61 assert_equal 2, entries.size
62 assert_equal entries[0].name, "watchers_controller.rb"
63 assert_equal entries[0].lastrev.time, Time.gm(2007, 12, 13, 16, 27, 22)
64 end
56 def test_entries_rev3
57 rev3_committed_on = Time.gm(2007, 12, 13, 16, 27, 22)
58 entries = @adapter.entries('sources', rev3_committed_on)
59 assert_equal 2, entries.size
60 assert_equal entries[0].name, "watchers_controller.rb"
61 assert_equal entries[0].lastrev.time, Time.gm(2007, 12, 13, 16, 27, 22)
62 end
65 63
66 def test_path_encoding_default_utf8
67 adpt1 = Redmine::Scm::Adapters::CvsAdapter.new(
68 MODULE_NAME,
69 REPOSITORY_PATH
70 )
71 assert_equal "UTF-8", adpt1.path_encoding
72 adpt2 = Redmine::Scm::Adapters::CvsAdapter.new(
73 MODULE_NAME,
74 REPOSITORY_PATH,
75 nil,
76 nil,
77 ""
78 )
79 assert_equal "UTF-8", adpt2.path_encoding
80 end
64 def test_path_encoding_default_utf8
65 adpt1 = Redmine::Scm::Adapters::CvsAdapter.new(
66 MODULE_NAME,
67 REPOSITORY_PATH
68 )
69 assert_equal "UTF-8", adpt1.path_encoding
70 adpt2 = Redmine::Scm::Adapters::CvsAdapter.new(
71 MODULE_NAME,
72 REPOSITORY_PATH,
73 nil,
74 nil,
75 ""
76 )
77 assert_equal "UTF-8", adpt2.path_encoding
78 end
81 79
82 def test_root_url_path
83 to_test = {
84 ':pserver:cvs_user:cvs_password@123.456.789.123:9876/repo' => '/repo',
85 ':pserver:cvs_user:cvs_password@123.456.789.123/repo' => '/repo',
86 ':pserver:cvs_user:cvs_password@cvs_server:/repo' => '/repo',
87 ':pserver:cvs_user:cvs_password@cvs_server:9876/repo' => '/repo',
88 ':pserver:cvs_user:cvs_password@cvs_server/repo' => '/repo',
89 ':pserver:cvs_user:cvs_password@cvs_server/path/repo' => '/path/repo',
90 ':ext:cvsservername:/path' => '/path'
91 }
80 def test_root_url_path
81 to_test = {
82 ':pserver:cvs_user:cvs_password@123.456.789.123:9876/repo' => '/repo',
83 ':pserver:cvs_user:cvs_password@123.456.789.123/repo' => '/repo',
84 ':pserver:cvs_user:cvs_password@cvs_server:/repo' => '/repo',
85 ':pserver:cvs_user:cvs_password@cvs_server:9876/repo' => '/repo',
86 ':pserver:cvs_user:cvs_password@cvs_server/repo' => '/repo',
87 ':pserver:cvs_user:cvs_password@cvs_server/path/repo' => '/path/repo',
88 ':ext:cvsservername:/path' => '/path'
89 }
92 90
93 to_test.each do |string, expected|
94 assert_equal expected, Redmine::Scm::Adapters::CvsAdapter.new('foo', string).send(:root_url_path), "#{string} failed"
95 end
91 to_test.each do |string, expected|
92 assert_equal expected, Redmine::Scm::Adapters::CvsAdapter.new('foo', string).send(:root_url_path), "#{string} failed"
96 93 end
94 end
97 95
98 private
96 private
99 97
100 def test_scm_version_for(scm_command_version, version)
101 @adapter.class.expects(:scm_version_from_command_line).returns(scm_command_version)
102 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
98 def test_scm_version_for(scm_command_version, version)
99 @adapter.class.expects(:scm_version_from_command_line).returns(scm_command_version)
100 assert_equal version, @adapter.class.scm_command_version
107 101 end
108 end
109
110 rescue LoadError
111 class CvsMochaFake < ActiveSupport::TestCase
112 def test_fake; assert(false, "Requires mocha to run those tests") end
102 else
103 puts "Cvs test repository NOT FOUND. Skipping unit tests !!!"
104 def test_fake; assert true end
113 105 end
114 106 end
115
@@ -16,54 +16,45
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../../../../test_helper', __FILE__)
19 begin
20 require 'mocha/setup'
21 19
22 class DarcsAdapterTest < ActiveSupport::TestCase
23 REPOSITORY_PATH = Rails.root.join('tmp/test/darcs_repository').to_s
20 class DarcsAdapterTest < ActiveSupport::TestCase
21 REPOSITORY_PATH = Rails.root.join('tmp/test/darcs_repository').to_s
24 22
25 if File.directory?(REPOSITORY_PATH)
26 def setup
27 @adapter = Redmine::Scm::Adapters::DarcsAdapter.new(REPOSITORY_PATH)
28 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
23 if File.directory?(REPOSITORY_PATH)
24 def setup
25 @adapter = Redmine::Scm::Adapters::DarcsAdapter.new(REPOSITORY_PATH)
26 end
37 27
38 def test_revisions
39 id1 = '20080308225258-98289-761f654d669045eabee90b91b53a21ce5593cadf.gz'
40 revs = @adapter.revisions('', nil, nil, {:with_path => true})
41 assert_equal 6, revs.size
42 assert_equal id1, revs[5].scmid
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]
28 def test_darcsversion
29 to_test = { "1.0.9 (release)\n" => [1,0,9] ,
30 "2.2.0 (release)\n" => [2,2,0] }
31 to_test.each do |s, v|
32 test_darcsversion_for(s, v)
49 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)
54 @adapter.class.expects(:darcs_binary_version_from_command_line).returns(darcsversion)
55 assert_equal version, @adapter.class.darcs_binary_version
56 end
49 private
57 50
58 else
59 puts "Darcs test repository NOT FOUND. Skipping unit tests !!!"
60 def test_fake; assert true end
51 def test_darcsversion_for(darcsversion, version)
52 @adapter.class.expects(:darcs_binary_version_from_command_line).returns(darcsversion)
53 assert_equal version, @adapter.class.darcs_binary_version
61 54 end
62 end
63 55
64 rescue LoadError
65 class DarcsMochaFake < ActiveSupport::TestCase
66 def test_fake; assert(false, "Requires mocha to run those tests") end
56 else
57 puts "Darcs test repository NOT FOUND. Skipping unit tests !!!"
58 def test_fake; assert true end
67 59 end
68 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 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 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
72 to_test = { "git version 1.7.3.4\n" => [1,7,3,4],
73 "1.6.1\n1.7\n1.8" => [1,6,1],
74 "1.6.2\r\n1.8.1\r\n1.9.1" => [1,6,2]}
75 to_test.each do |s, v|
76 test_scm_version_for(s, v)
77 end
78 end
20 class GitAdapterTest < ActiveSupport::TestCase
21 REPOSITORY_PATH = Rails.root.join('tmp/test/git_repository').to_s
22
23 FELIX_HEX = "Felix Sch\xC3\xA4fer"
24 CHAR_1_HEX = "\xc3\x9c"
25
26 ## Git, Mercurial and CVS path encodings are binary.
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
81 brs = []
82 @adapter.branches.each do |b|
83 brs << b
84 end
85 assert_equal 6, brs.length
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
65 def test_scm_version
66 to_test = { "git version 1.7.3.4\n" => [1,7,3,4],
67 "1.6.1\n1.7\n1.8" => [1,6,1],
68 "1.6.2\r\n1.8.1\r\n1.9.1" => [1,6,2]}
69 to_test.each do |s, v|
70 test_scm_version_for(s, v)
116 71 end
72 end
117 73
118 def test_default_branch
119 assert_equal 'master-20120212', @adapter.default_branch
120 end
74 def test_branches
75 brs = []
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
123 assert_equal [
124 "tag00.lightweight",
125 "tag01.annotated",
126 ], @adapter.tags
127 end
112 def test_default_branch
113 assert_equal 'master-20120212', @adapter.default_branch
114 end
128 115
129 def test_revisions_master_all
130 revs1 = []
131 @adapter.revisions('', nil, "master",{}) do |rev|
132 revs1 << rev
133 end
134 assert_equal 15, revs1.length
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
116 def test_tags
117 assert_equal [
118 "tag00.lightweight",
119 "tag01.annotated",
120 ], @adapter.tags
121 end
147 122
148 def test_revisions_master_merged_rev
149 revs1 = []
150 @adapter.revisions('',
151 "713f4944648826f558cf548222f813dabe7cbb04",
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
123 def test_revisions_master_all
124 revs1 = []
125 @adapter.revisions('', nil, "master",{}) do |rev|
126 revs1 << rev
179 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
182 revs1 = []
183 @adapter.revisions('', nil, "latin-1-path-encoding",{}) do |rev|
184 revs1 << 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
132 revs2 = []
133 @adapter.revisions('', nil, "master",
134 {:reverse => true}) do |rev|
135 revs2 << rev
198 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
201 revs1 = []
202 @adapter.revisions('',
203 '7234cb2750b63f47bff735edc50a1c0a433c2518',
204 "latin-1-path-encoding",
205 {:reverse => true}) do |rev|
206 revs1 << rev
207 end
208 assert_equal 7, revs1.length
209 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', revs1[ 0].identifier
210 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs1[-1].identifier
142 def test_revisions_master_merged_rev
143 revs1 = []
144 @adapter.revisions('',
145 "713f4944648826f558cf548222f813dabe7cbb04",
146 "master",
147 {:reverse => true}) do |rev|
148 revs1 << rev
149 end
150 assert_equal 8, revs1.length
151 assert_equal 'fba357b886984ee71185ad2065e65fc0417d9b92', revs1[ 0].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 = []
213 @adapter.revisions('',
214 '57ca437c0acbbcb749821fdf3726a1367056d364',
215 "latin-1-path-encoding",
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
175 def test_revisions_branch_latin_1_path_encoding_all
176 revs1 = []
177 @adapter.revisions('', nil, "latin-1-path-encoding",{}) do |rev|
178 revs1 << rev
222 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
225 assert_equal [], @adapter.revisions('', '1234abcd', "master")
226 assert_raise Redmine::Scm::Adapters::CommandFailed do
227 revs1 = []
228 @adapter.revisions('',
229 '1234abcd',
230 "master",
231 {:reverse => true}) do |rev|
232 revs1 << rev
233 end
234 end
184 revs2 = []
185 @adapter.revisions('', nil, "latin-1-path-encoding",
186 {:reverse => true}) do |rev|
187 revs2 << rev
235 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
238 revs1 = []
239 @adapter.revisions('', nil, nil,
240 {:reverse => true,
241 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
242 :excludes => ['4f26664364207fa8b1af9f8722647ab2d4ac5d43']}) do |rev|
243 revs1 << rev
244 end
245 assert_equal 2, revs1.length
246 assert_equal 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b', revs1[ 0].identifier
247 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[-1].identifier
248 end
194 def test_revisions_branch_latin_1_path_encoding_with_rev
195 revs1 = []
196 @adapter.revisions('',
197 '7234cb2750b63f47bff735edc50a1c0a433c2518',
198 "latin-1-path-encoding",
199 {:reverse => true}) do |rev|
200 revs1 << rev
201 end
202 assert_equal 7, revs1.length
203 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', revs1[ 0].identifier
204 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs1[-1].identifier
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 221 revs1 = []
252 @adapter.revisions('', nil, nil,
253 {:reverse => true,
254 :includes => ['899a15dba03a3b350b89c3f537e4bbe02a03cdc9'],
255 :excludes => []}) do |rev|
222 @adapter.revisions('',
223 '1234abcd',
224 "master",
225 {:reverse => true}) do |rev|
256 226 revs1 << rev
257 227 end
258 assert_equal 2, revs1.length
259 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs1[ 0].identifier
260 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', revs1[ 1].identifier
261 228 end
229 end
262 230
263 def test_revisions_includes_merged_revs
264 revs1 = []
265 @adapter.revisions('', nil, nil,
266 {:reverse => true,
267 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
268 :excludes => ['fba357b886984ee71185ad2065e65fc0417d9b92']}) do |rev|
269 revs1 << rev
270 end
271 assert_equal 7, revs1.length
272 assert_equal '7e61ac704deecde634b51e59daa8110435dcb3da', revs1[ 0].identifier
273 assert_equal '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8', revs1[ 1].identifier
274 assert_equal '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf', revs1[ 2].identifier
275 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[-1].identifier
276 end
231 def test_revisions_includes_master_two_revs
232 revs1 = []
233 @adapter.revisions('', nil, nil,
234 {:reverse => true,
235 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
236 :excludes => ['4f26664364207fa8b1af9f8722647ab2d4ac5d43']}) do |rev|
237 revs1 << rev
238 end
239 assert_equal 2, revs1.length
240 assert_equal 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b', revs1[ 0].identifier
241 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[-1].identifier
242 end
277 243
278 def test_revisions_includes_two_heads
279 revs1 = []
280 @adapter.revisions('', nil, nil,
281 {:reverse => true,
282 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c',
283 '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127'],
284 :excludes => ['4f26664364207fa8b1af9f8722647ab2d4ac5d43',
285 '4fc55c43bf3d3dc2efb66145365ddc17639ce81e']}) do |rev|
286 revs1 << rev
287 end
288 assert_equal 4, revs1.length
289 assert_equal 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b', revs1[ 0].identifier
290 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[ 1].identifier
291 assert_equal '64f1f3e89ad1cb57976ff0ad99a107012ba3481d', revs1[-2].identifier
292 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs1[-1].identifier
293 end
244 def test_revisions_includes_master_two_revs_from_origin
245 revs1 = []
246 @adapter.revisions('', nil, nil,
247 {:reverse => true,
248 :includes => ['899a15dba03a3b350b89c3f537e4bbe02a03cdc9'],
249 :excludes => []}) do |rev|
250 revs1 << rev
251 end
252 assert_equal 2, revs1.length
253 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs1[ 0].identifier
254 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', revs1[ 1].identifier
255 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 313 revs1 = []
297 314 @adapter.revisions('', nil, nil,
298 315 {:reverse => true,
299 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c',
300 '92397af84d22f27389c822848ecd5b463c181583'],
301 :excludes => ['95488a44bc25f7d1f97d775a31359539ff333a63',
302 '4f26664364207fa8b1af9f8722647ab2d4ac5d43'] }) do |rev|
316 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
317 :excludes => ['0123abcd4567']}) do |rev|
303 318 revs1 << rev
304 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 320 end
321 end
328 322
329 def test_getting_revisions_with_spaces_in_filename
330 assert_equal 1, @adapter.revisions("filemane with spaces.txt",
331 nil, "master").length
332 end
323 def test_getting_revisions_with_spaces_in_filename
324 assert_equal 1, @adapter.revisions("filemane with spaces.txt",
325 nil, "master").length
326 end
333 327
334 def test_parents
335 revs1 = []
336 @adapter.revisions('',
337 nil,
338 "master",
339 {:reverse => true}) do |rev|
340 revs1 << rev
341 end
342 assert_equal 15, revs1.length
343 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
344 revs1[0].identifier
345 assert_equal nil, revs1[0].parents
346 assert_equal "899a15dba03a3b350b89c3f537e4bbe02a03cdc9",
347 revs1[1].identifier
348 assert_equal 1, revs1[1].parents.length
349 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
350 revs1[1].parents[0]
351 assert_equal "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
352 revs1[10].identifier
353 assert_equal 2, revs1[10].parents.length
354 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8",
355 revs1[10].parents[0]
356 assert_equal "7e61ac704deecde634b51e59daa8110435dcb3da",
357 revs1[10].parents[1]
358 end
328 def test_parents
329 revs1 = []
330 @adapter.revisions('',
331 nil,
332 "master",
333 {:reverse => true}) do |rev|
334 revs1 << rev
335 end
336 assert_equal 15, revs1.length
337 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
338 revs1[0].identifier
339 assert_equal nil, revs1[0].parents
340 assert_equal "899a15dba03a3b350b89c3f537e4bbe02a03cdc9",
341 revs1[1].identifier
342 assert_equal 1, revs1[1].parents.length
343 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
344 revs1[1].parents[0]
345 assert_equal "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
346 revs1[10].identifier
347 assert_equal 2, revs1[10].parents.length
348 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8",
349 revs1[10].parents[0]
350 assert_equal "7e61ac704deecde634b51e59daa8110435dcb3da",
351 revs1[10].parents[1]
352 end
359 353
360 def test_getting_revisions_with_leading_and_trailing_spaces_in_filename
361 assert_equal " filename with a leading space.txt ",
362 @adapter.revisions(" filename with a leading space.txt ",
363 nil, "master")[0].paths[0][:path]
364 end
354 def test_getting_revisions_with_leading_and_trailing_spaces_in_filename
355 assert_equal " filename with a leading space.txt ",
356 @adapter.revisions(" filename with a leading space.txt ",
357 nil, "master")[0].paths[0][:path]
358 end
365 359
366 def test_getting_entries_with_leading_and_trailing_spaces_in_filename
367 assert_equal " filename with a leading space.txt ",
368 @adapter.entries('',
369 '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c')[3].name
370 end
360 def test_getting_entries_with_leading_and_trailing_spaces_in_filename
361 assert_equal " filename with a leading space.txt ",
362 @adapter.entries('',
363 '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c')[3].name
364 end
371 365
372 def test_annotate
373 annotate = @adapter.annotate('sources/watchers_controller.rb')
374 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
375 assert_equal 41, annotate.lines.size
376 assert_equal "# This program is free software; you can redistribute it and/or",
377 annotate.lines[4].strip
378 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
379 annotate.revisions[4].identifier
380 assert_equal "jsmith", annotate.revisions[4].author
381 end
366 def test_annotate
367 annotate = @adapter.annotate('sources/watchers_controller.rb')
368 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
369 assert_equal 41, annotate.lines.size
370 assert_equal "# This program is free software; you can redistribute it and/or",
371 annotate.lines[4].strip
372 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
373 annotate.revisions[4].identifier
374 assert_equal "jsmith", annotate.revisions[4].author
375 end
382 376
383 def test_annotate_moved_file
384 annotate = @adapter.annotate('renamed_test.txt')
385 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
386 assert_equal 2, annotate.lines.size
387 end
377 def test_annotate_moved_file
378 annotate = @adapter.annotate('renamed_test.txt')
379 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
380 assert_equal 2, annotate.lines.size
381 end
388 382
389 def test_last_rev
390 last_rev = @adapter.lastrev("README",
391 "4f26664364207fa8b1af9f8722647ab2d4ac5d43")
392 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.scmid
393 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.identifier
394 assert_equal "Adam Soltys <asoltys@gmail.com>", last_rev.author
395 assert_equal Time.gm(2009, 6, 24, 5, 27, 38), last_rev.time
396 end
383 def test_last_rev
384 last_rev = @adapter.lastrev("README",
385 "4f26664364207fa8b1af9f8722647ab2d4ac5d43")
386 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.scmid
387 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.identifier
388 assert_equal "Adam Soltys <asoltys@gmail.com>", last_rev.author
389 assert_equal Time.gm(2009, 6, 24, 5, 27, 38), last_rev.time
390 end
397 391
398 def test_last_rev_with_spaces_in_filename
399 last_rev = @adapter.lastrev("filemane with spaces.txt",
400 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b")
401 last_rev_author = last_rev.author
402 assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.scmid
403 assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.identifier
404 assert_equal "#{@str_felix_hex} <felix@fachschaften.org>",
405 last_rev.author
406 assert_equal Time.gm(2010, 9, 18, 19, 59, 46), last_rev.time
407 end
392 def test_last_rev_with_spaces_in_filename
393 last_rev = @adapter.lastrev("filemane with spaces.txt",
394 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b")
395 last_rev_author = last_rev.author
396 assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.scmid
397 assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.identifier
398 assert_equal "#{@str_felix_hex} <felix@fachschaften.org>",
399 last_rev.author
400 assert_equal Time.gm(2010, 9, 18, 19, 59, 46), last_rev.time
401 end
408 402
409 def test_latin_1_path
410 if WINDOWS_PASS
411 puts WINDOWS_SKIP_STR
412 elsif JRUBY_SKIP
413 puts JRUBY_SKIP_STR
414 else
415 p2 = "latin-1-dir/test-#{@char_1}-2.txt"
416 ['4fc55c43bf3d3dc2efb66145365ddc17639ce81e', '4fc55c43bf3'].each do |r1|
417 assert @adapter.diff(p2, r1)
418 assert @adapter.cat(p2, r1)
419 assert_equal 1, @adapter.annotate(p2, r1).lines.length
420 ['64f1f3e89ad1cb57976ff0ad99a107012ba3481d', '64f1f3e89ad1cb5797'].each do |r2|
421 assert @adapter.diff(p2, r1, r2)
422 end
403 def test_latin_1_path
404 if WINDOWS_PASS
405 puts WINDOWS_SKIP_STR
406 elsif JRUBY_SKIP
407 puts JRUBY_SKIP_STR
408 else
409 p2 = "latin-1-dir/test-#{@char_1}-2.txt"
410 ['4fc55c43bf3d3dc2efb66145365ddc17639ce81e', '4fc55c43bf3'].each do |r1|
411 assert @adapter.diff(p2, r1)
412 assert @adapter.cat(p2, r1)
413 assert_equal 1, @adapter.annotate(p2, r1).lines.length
414 ['64f1f3e89ad1cb57976ff0ad99a107012ba3481d', '64f1f3e89ad1cb5797'].each do |r2|
415 assert @adapter.diff(p2, r1, r2)
423 416 end
424 417 end
425 418 end
419 end
426 420
427 def test_latin_1_user_annotate
428 ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', '83ca5fd546063a'].each do |r1|
429 annotate = @adapter.annotate(" filename with a leading space.txt ", r1)
430 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
431 assert_equal 1, annotate.lines.size
432 assert_equal "And this is a file with a leading and trailing space...",
433 annotate.lines[0].strip
434 assert_equal "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
435 annotate.revisions[0].identifier
436 assert_equal @str_felix_hex, annotate.revisions[0].author
437 end
421 def test_latin_1_user_annotate
422 ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', '83ca5fd546063a'].each do |r1|
423 annotate = @adapter.annotate(" filename with a leading space.txt ", r1)
424 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
425 assert_equal 1, annotate.lines.size
426 assert_equal "And this is a file with a leading and trailing space...",
427 annotate.lines[0].strip
428 assert_equal "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
429 annotate.revisions[0].identifier
430 assert_equal @str_felix_hex, annotate.revisions[0].author
438 431 end
432 end
439 433
440 def test_entries_tag
441 entries1 = @adapter.entries(nil, 'tag01.annotated',
442 options = {:report_last_commit => true})
443 assert entries1
444 assert_equal 3, entries1.size
445 assert_equal 'sources', entries1[1].name
446 assert_equal 'sources', entries1[1].path
447 assert_equal 'dir', entries1[1].kind
448 readme = entries1[2]
449 assert_equal 'README', readme.name
450 assert_equal 'README', readme.path
451 assert_equal 'file', readme.kind
452 assert_equal 27, readme.size
453 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', readme.lastrev.identifier
454 assert_equal Time.gm(2007, 12, 14, 9, 24, 1), readme.lastrev.time
455 end
434 def test_entries_tag
435 entries1 = @adapter.entries(nil, 'tag01.annotated',
436 options = {:report_last_commit => true})
437 assert entries1
438 assert_equal 3, entries1.size
439 assert_equal 'sources', entries1[1].name
440 assert_equal 'sources', entries1[1].path
441 assert_equal 'dir', entries1[1].kind
442 readme = entries1[2]
443 assert_equal 'README', readme.name
444 assert_equal 'README', readme.path
445 assert_equal 'file', readme.kind
446 assert_equal 27, readme.size
447 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', readme.lastrev.identifier
448 assert_equal Time.gm(2007, 12, 14, 9, 24, 1), readme.lastrev.time
449 end
456 450
457 def test_entries_branch
458 entries1 = @adapter.entries(nil, 'test_branch',
459 options = {:report_last_commit => true})
460 assert entries1
461 assert_equal 4, entries1.size
462 assert_equal 'sources', entries1[1].name
463 assert_equal 'sources', entries1[1].path
464 assert_equal 'dir', entries1[1].kind
465 readme = entries1[2]
466 assert_equal 'README', readme.name
467 assert_equal 'README', readme.path
468 assert_equal 'file', readme.kind
469 assert_equal 159, readme.size
470 assert_equal '713f4944648826f558cf548222f813dabe7cbb04', readme.lastrev.identifier
471 assert_equal Time.gm(2009, 6, 19, 4, 37, 23), readme.lastrev.time
472 end
451 def test_entries_branch
452 entries1 = @adapter.entries(nil, 'test_branch',
453 options = {:report_last_commit => true})
454 assert entries1
455 assert_equal 4, entries1.size
456 assert_equal 'sources', entries1[1].name
457 assert_equal 'sources', entries1[1].path
458 assert_equal 'dir', entries1[1].kind
459 readme = entries1[2]
460 assert_equal 'README', readme.name
461 assert_equal 'README', readme.path
462 assert_equal 'file', readme.kind
463 assert_equal 159, readme.size
464 assert_equal '713f4944648826f558cf548222f813dabe7cbb04', readme.lastrev.identifier
465 assert_equal Time.gm(2009, 6, 19, 4, 37, 23), readme.lastrev.time
466 end
473 467
474 def test_entries_wrong_path_encoding
475 adpt = Redmine::Scm::Adapters::GitAdapter.new(
476 REPOSITORY_PATH,
477 nil,
478 nil,
479 nil,
480 'EUC-JP'
481 )
482 entries1 = adpt.entries('latin-1-dir', '64f1f3e8')
483 assert entries1
484 assert_equal 3, entries1.size
485 f1 = entries1[1]
486 assert_equal nil, f1.name
487 assert_equal nil, f1.path
488 assert_equal 'file', f1.kind
489 end
468 def test_entries_wrong_path_encoding
469 adpt = Redmine::Scm::Adapters::GitAdapter.new(
470 REPOSITORY_PATH,
471 nil,
472 nil,
473 nil,
474 'EUC-JP'
475 )
476 entries1 = adpt.entries('latin-1-dir', '64f1f3e8')
477 assert entries1
478 assert_equal 3, entries1.size
479 f1 = entries1[1]
480 assert_equal nil, f1.name
481 assert_equal nil, f1.path
482 assert_equal 'file', f1.kind
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
492 entries1 = @adapter.entries('latin-1-dir', '64f1f3e8')
495 def test_entries_latin_1_dir
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 503 assert entries1
494 504 assert_equal 3, entries1.size
495 505 f1 = entries1[1]
496 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 508 assert_equal 'file', f1.kind
499 509 end
510 end
500 511
501 def test_entries_latin_1_dir
502 if WINDOWS_PASS
503 puts WINDOWS_SKIP_STR
504 elsif JRUBY_SKIP
505 puts JRUBY_SKIP_STR
506 else
507 entries1 = @adapter.entries("latin-1-dir/test-#{@char_1}-subdir",
508 '1ca7f5ed')
509 assert entries1
510 assert_equal 3, entries1.size
511 f1 = entries1[1]
512 assert_equal "test-#{@char_1}-2.txt", f1.name
513 assert_equal "latin-1-dir/test-#{@char_1}-subdir/test-#{@char_1}-2.txt", f1.path
514 assert_equal 'file', f1.kind
515 end
516 end
517
518 def test_entry
519 entry = @adapter.entry()
520 assert_equal "", entry.path
512 def test_entry
513 entry = @adapter.entry()
514 assert_equal "", entry.path
515 assert_equal "dir", entry.kind
516 entry = @adapter.entry('')
517 assert_equal "", entry.path
518 assert_equal "dir", entry.kind
519 assert_nil @adapter.entry('invalid')
520 assert_nil @adapter.entry('/invalid')
521 assert_nil @adapter.entry('/invalid/')
522 assert_nil @adapter.entry('invalid/invalid')
523 assert_nil @adapter.entry('invalid/invalid/')
524 assert_nil @adapter.entry('/invalid/invalid')
525 assert_nil @adapter.entry('/invalid/invalid/')
526 ["README", "/README"].each do |path|
527 entry = @adapter.entry(path, '7234cb2750b63f')
528 assert_equal "README", entry.path
529 assert_equal "file", entry.kind
530 end
531 ["sources", "/sources", "/sources/"].each do |path|
532 entry = @adapter.entry(path, '7234cb2750b63f')
533 assert_equal "sources", entry.path
521 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 535 end
548
549 def test_path_encoding_default_utf8
550 adpt1 = Redmine::Scm::Adapters::GitAdapter.new(
551 REPOSITORY_PATH
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
536 ["sources/watchers_controller.rb", "/sources/watchers_controller.rb"].each do |path|
537 entry = @adapter.entry(path, '7234cb2750b63f')
538 assert_equal "sources/watchers_controller.rb", entry.path
539 assert_equal "file", entry.kind
562 540 end
541 end
563 542
564 def test_cat_path_invalid
565 assert_nil @adapter.cat('invalid')
566 end
543 def test_path_encoding_default_utf8
544 adpt1 = Redmine::Scm::Adapters::GitAdapter.new(
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
569 assert @adapter.cat('README')
570 assert_nil @adapter.cat('README', '1234abcd5678')
571 end
558 def test_cat_path_invalid
559 assert_nil @adapter.cat('invalid')
560 end
572 561
573 def test_diff_path_invalid
574 assert_equal [], @adapter.diff('invalid', '713f4944648826f5')
575 end
562 def test_cat_revision_invalid
563 assert @adapter.cat('README')
564 assert_nil @adapter.cat('README', '1234abcd5678')
565 end
576 566
577 def test_diff_revision_invalid
578 assert_nil @adapter.diff(nil, '1234abcd5678')
579 assert_nil @adapter.diff(nil, '713f4944648826f5', '1234abcd5678')
580 assert_nil @adapter.diff(nil, '1234abcd5678', '713f4944648826f5')
581 end
567 def test_diff_path_invalid
568 assert_equal [], @adapter.diff('invalid', '713f4944648826f5')
569 end
582 570
583 def test_annotate_path_invalid
584 assert_nil @adapter.annotate('invalid')
585 end
571 def test_diff_revision_invalid
572 assert_nil @adapter.diff(nil, '1234abcd5678')
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
588 assert @adapter.annotate('README')
589 assert_nil @adapter.annotate('README', '1234abcd5678')
590 end
577 def test_annotate_path_invalid
578 assert_nil @adapter.annotate('invalid')
579 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)
595 @adapter.class.expects(:scm_version_from_command_line).returns(scm_command_version)
596 assert_equal version, @adapter.class.scm_command_version
597 end
586 private
598 587
599 else
600 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
601 def test_fake; assert true end
588 def test_scm_version_for(scm_command_version, version)
589 @adapter.class.expects(:scm_version_from_command_line).returns(scm_command_version)
590 assert_equal version, @adapter.class.scm_command_version
602 591 end
603 end
604 592
605 rescue LoadError
606 class GitMochaFake < ActiveSupport::TestCase
607 def test_fake; assert(false, "Requires mocha to run those tests") end
593 else
594 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
595 def test_fake; assert true end
608 596 end
609 597 end
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 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
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 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
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now