diff -Nru redmine-2.3.3/.gitignore redmine-2.4.2/.gitignore
--- redmine-2.3.3/.gitignore 2013-09-14 06:48:49.000000000 +0000
+++ redmine-2.4.2/.gitignore 2013-12-23 08:48:42.000000000 +0000
@@ -1,5 +1,7 @@
/.project
/.loadpath
+/.powrc
+/.rvmrc
/config/additional_environment.rb
/config/configuration.yml
/config/database.yml
@@ -15,8 +17,14 @@
/lib/redmine/scm/adapters/mercurial/redminehelper.pyo
/log/*.log*
/log/mongrel_debug
+/plugins/*
+!/plugins/README
/public/dispatch.*
/public/plugin_assets
+/public/themes/*
+!/public/themes/alternate
+!/public/themes/classic
+!/public/themes/README
/tmp/*
/tmp/cache/*
/tmp/pdf/*
diff -Nru redmine-2.3.3/.hgignore redmine-2.4.2/.hgignore
--- redmine-2.3.3/.hgignore 2013-09-14 06:48:49.000000000 +0000
+++ redmine-2.4.2/.hgignore 2013-12-23 08:48:42.000000000 +0000
@@ -2,6 +2,8 @@
.project
.loadpath
+.powrc
+.rvmrc
config/additional_environment.rb
config/configuration.yml
config/database.yml
diff -Nru redmine-2.3.3/CONTRIBUTING.md redmine-2.4.2/CONTRIBUTING.md
--- redmine-2.3.3/CONTRIBUTING.md 1970-01-01 00:00:00.000000000 +0000
+++ redmine-2.4.2/CONTRIBUTING.md 2013-12-23 08:48:42.000000000 +0000
@@ -0,0 +1,8 @@
+
+**Do not send a pull requst to this github repository**.
+
+For more detail, please see [official website] wiki [Contribute].
+
+[official website]: http://www.redmine.org
+[Contribute]: http://www.redmine.org/projects/redmine/wiki/Contribute
+
diff -Nru redmine-2.3.3/Gemfile redmine-2.4.2/Gemfile
--- redmine-2.3.3/Gemfile 2013-09-14 06:48:49.000000000 +0000
+++ redmine-2.4.2/Gemfile 2013-12-23 08:48:42.000000000 +0000
@@ -1,9 +1,8 @@
source 'https://rubygems.org'
-gem "rails", "3.2.13"
+gem "rails", "3.2.16"
gem "jquery-rails", "~> 2.0.2"
-gem "i18n", "~> 0.6.0"
-gem "coderay", "~> 1.0.9"
+gem "coderay", "~> 1.1.0"
gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby]
gem "builder", "3.0.0"
@@ -14,7 +13,7 @@
# Optional gem for OpenID authentication
group :openid do
- gem "ruby-openid", "~> 2.2.3", :require => "openid"
+ gem "ruby-openid", "~> 2.3.0", :require => "openid"
gem "rack-openid"
end
@@ -31,7 +30,7 @@
platforms :jruby do
# jruby-openssl is bundled with JRuby 1.7.0
gem "jruby-openssl" if Object.const_defined?(:JRUBY_VERSION) && JRUBY_VERSION < '1.7.0'
- gem "activerecord-jdbc-adapter", "1.2.5"
+ gem "activerecord-jdbc-adapter", "~> 1.3.2"
end
# Include database gems for the adapters found in the database
@@ -78,9 +77,12 @@
group :test do
gem "shoulda", "~> 3.3.2"
- gem "mocha", "~> 0.13.3"
- gem 'capybara', '~> 2.0.0'
- gem 'nokogiri', '< 1.6.0'
+ gem "mocha", ">= 0.14", :require => 'mocha/api'
+ if RUBY_VERSION >= '1.9.3'
+ gem "capybara", "~> 2.1.0"
+ gem "selenium-webdriver"
+ gem "database_cleaner"
+ end
end
local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
@@ -92,5 +94,6 @@
# Load plugins' Gemfiles
Dir.glob File.expand_path("../plugins/*/Gemfile", __FILE__) do |file|
puts "Loading #{file} ..." if $DEBUG # `ruby -d` or `bundle -v`
- instance_eval File.read(file)
+ #TODO: switch to "eval_gemfile file" when bundler >= 1.2.0 will be required (rails 4)
+ instance_eval File.read(file), file
end
diff -Nru redmine-2.3.3/app/controllers/account_controller.rb redmine-2.4.2/app/controllers/account_controller.rb
--- redmine-2.3.3/app/controllers/account_controller.rb 2013-09-14 06:48:16.000000000 +0000
+++ redmine-2.4.2/app/controllers/account_controller.rb 2013-12-23 08:48:37.000000000 +0000
@@ -20,7 +20,15 @@
include CustomFieldsHelper
# prevents login action to be filtered by check_if_login_required application scope filter
- skip_before_filter :check_if_login_required
+ skip_before_filter :check_if_login_required, :check_password_change
+
+ # Overrides ApplicationController#verify_authenticity_token to disable
+ # token verification on openid callbacks
+ def verify_authenticity_token
+ unless using_open_id?
+ super
+ end
+ end
# Login request and validation
def login
@@ -75,11 +83,15 @@
else
if request.post?
user = User.find_by_mail(params[:mail].to_s)
- # user not found or not active
- unless user && user.active?
+ # user not found
+ unless user
flash.now[:error] = l(:notice_account_unknown_email)
return
end
+ unless user.active?
+ handle_inactive_user(user, lost_password_path)
+ return
+ end
# user cannot change its password
unless user.change_password_allowed?
flash.now[:error] = l(:notice_can_t_change_password)
@@ -152,6 +164,19 @@
redirect_to signin_path
end
+ # Sends a new account activation email
+ def activation_email
+ if session[:registered_user_id] && Setting.self_registration == '1'
+ user_id = session.delete(:registered_user_id).to_i
+ user = User.find_by_id(user_id)
+ if user && user.registered?
+ register_by_email_activation(user)
+ return
+ end
+ end
+ redirect_to(home_url)
+ end
+
private
def authenticate_user
@@ -163,7 +188,7 @@
end
def password_authentication
- user = User.try_to_login(params[:username], params[:password])
+ user = User.try_to_login(params[:username], params[:password], false)
if user.nil?
invalid_credentials
@@ -171,27 +196,31 @@
onthefly_creation_failed(user, {:login => user.login, :auth_source_id => user.auth_source_id })
else
# Valid user
- successful_authentication(user)
+ if user.active?
+ successful_authentication(user)
+ else
+ handle_inactive_user(user)
+ end
end
end
def open_id_authenticate(openid_url)
back_url = signin_url(:autologin => params[:autologin])
-
- authenticate_with_open_id(openid_url, :required => [:nickname, :fullname, :email], :return_to => back_url, :method => :post) do |result, identity_url, registration|
+ authenticate_with_open_id(
+ openid_url, :required => [:nickname, :fullname, :email],
+ :return_to => back_url, :method => :post
+ ) do |result, identity_url, registration|
if result.successful?
user = User.find_or_initialize_by_identity_url(identity_url)
if user.new_record?
# Self-registration off
(redirect_to(home_url); return) unless Setting.self_registration?
-
# Create on the fly
user.login = registration['nickname'] unless registration['nickname'].nil?
user.mail = registration['email'] unless registration['email'].nil?
user.firstname, user.lastname = registration['fullname'].split(' ') unless registration['fullname'].nil?
user.random_password
user.register
-
case Setting.self_registration
when '1'
register_by_email_activation(user) do
@@ -211,7 +240,7 @@
if user.active?
successful_authentication(user)
else
- account_pending
+ handle_inactive_user(user)
end
end
end
@@ -261,7 +290,7 @@
token = Token.new(:user => user, :action => "register")
if user.save and token.save
Mailer.register(token).deliver
- flash[:notice] = l(:notice_account_register_done)
+ flash[:notice] = l(:notice_account_register_done, :email => user.mail)
redirect_to signin_path
else
yield if block_given?
@@ -291,14 +320,32 @@
if user.save
# Sends an email to the administrators
Mailer.account_activation_request(user).deliver
- account_pending
+ account_pending(user)
else
yield if block_given?
end
end
- def account_pending
- flash[:notice] = l(:notice_account_pending)
- redirect_to signin_path
+ def handle_inactive_user(user, redirect_path=signin_path)
+ if user.registered?
+ account_pending(user, redirect_path)
+ else
+ account_locked(user, redirect_path)
+ end
+ end
+
+ def account_pending(user, redirect_path=signin_path)
+ if Setting.self_registration == '1'
+ flash[:error] = l(:notice_account_not_activated_yet, :url => activation_email_path)
+ session[:registered_user_id] = user.id
+ else
+ flash[:error] = l(:notice_account_pending)
+ end
+ redirect_to redirect_path
+ end
+
+ def account_locked(user, redirect_path=signin_path)
+ flash[:error] = l(:notice_account_locked)
+ redirect_to redirect_path
end
end
diff -Nru redmine-2.3.3/app/controllers/admin_controller.rb redmine-2.4.2/app/controllers/admin_controller.rb
--- redmine-2.3.3/app/controllers/admin_controller.rb 2013-09-14 06:48:16.000000000 +0000
+++ redmine-2.4.2/app/controllers/admin_controller.rb 2013-12-23 08:48:37.000000000 +0000
@@ -65,7 +65,7 @@
@test = Mailer.test_email(User.current).deliver
flash[:notice] = l(:notice_email_sent, User.current.mail)
rescue Exception => e
- flash[:error] = l(:notice_email_error, e.message)
+ flash[:error] = l(:notice_email_error, Redmine::CodesetUtil.replace_invalid_utf8(e.message))
end
ActionMailer::Base.raise_delivery_errors = raise_delivery_errors
redirect_to settings_path(:tab => 'notifications')
@@ -77,7 +77,8 @@
[:text_default_administrator_account_changed, User.default_admin_account_changed?],
[:text_file_repository_writable, File.writable?(Attachment.storage_path)],
[:text_plugin_assets_writable, File.writable?(Redmine::Plugin.public_directory)],
- [:text_rmagick_available, Object.const_defined?(:Magick)]
+ [:text_rmagick_available, Object.const_defined?(:Magick)],
+ [:text_convert_available, Redmine::Thumbnail.convert_available?]
]
end
end
diff -Nru redmine-2.3.3/app/controllers/application_controller.rb redmine-2.4.2/app/controllers/application_controller.rb
--- redmine-2.3.3/app/controllers/application_controller.rb 2013-09-14 06:48:16.000000000 +0000
+++ redmine-2.4.2/app/controllers/application_controller.rb 2013-12-23 08:48:37.000000000 +0000
@@ -33,14 +33,23 @@
layout 'base'
protect_from_forgery
+
+ def verify_authenticity_token
+ unless api_request?
+ super
+ end
+ end
+
def handle_unverified_request
- super
- cookies.delete(autologin_cookie_name)
+ unless api_request?
+ super
+ cookies.delete(autologin_cookie_name)
+ render_error :status => 422, :message => "Invalid form authenticity token."
+ end
end
- before_filter :session_expiration, :user_setup, :check_if_login_required, :set_localization
+ before_filter :session_expiration, :user_setup, :check_if_login_required, :check_password_change, :set_localization
- rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
rescue_from ::Unauthorized, :with => :deny_access
rescue_from ::ActionView::MissingTemplate, :with => :missing_template
@@ -78,6 +87,9 @@
session[:user_id] = user.id
session[:ctime] = Time.now.utc.to_i
session[:atime] = Time.now.utc.to_i
+ if user.must_change_password?
+ session[:pwd] = '1'
+ end
end
def user_setup
@@ -112,6 +124,10 @@
authenticate_with_http_basic do |username, password|
user = User.try_to_login(username, password) || User.find_by_api_key(username)
end
+ if user && user.must_change_password?
+ render_error :message => 'You must change your password', :status => 403
+ return
+ end
end
# Switch user if requested by an admin user
if user && user.admin? && (username = api_switch_user_from_request)
@@ -170,6 +186,16 @@
require_login if Setting.login_required?
end
+ def check_password_change
+ if session[:pwd]
+ if User.current.must_change_password?
+ redirect_to my_password_path
+ else
+ session.delete(:pwd)
+ end
+ end
+ end
+
def set_localization
lang = nil
if User.current.logged?
@@ -195,7 +221,13 @@
url = url_for(:controller => params[:controller], :action => params[:action], :id => params[:id], :project_id => params[:project_id])
end
respond_to do |format|
- format.html { redirect_to :controller => "account", :action => "login", :back_url => url }
+ format.html {
+ if request.xhr?
+ head :unauthorized
+ else
+ redirect_to :controller => "account", :action => "login", :back_url => url
+ end
+ }
format.atom { redirect_to :controller => "account", :action => "login", :back_url => url }
format.xml { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
format.js { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
@@ -298,7 +330,7 @@
# Find issues with a single :id param or :ids array param
# Raises a Unauthorized exception if one of the issues is not visible
def find_issues
- @issues = Issue.find_all_by_id(params[:id] || params[:ids])
+ @issues = Issue.where(:id => (params[:id] || params[:ids])).preload(:project, :status, :tracker, :priority, :author, :assigned_to, :relations_to).to_a
raise ActiveRecord::RecordNotFound if @issues.empty?
raise Unauthorized unless @issues.all?(&:visible?)
@projects = @issues.collect(&:project).compact.uniq
@@ -427,13 +459,6 @@
request.xhr? ? false : 'base'
end
- def invalid_authenticity_token
- if api_request?
- logger.error "Form authenticity token is missing or is invalid. API calls must include a proper Content-type header (text/xml or text/json)."
- end
- render_error "Invalid form authenticity token."
- end
-
def render_feed(items, options={})
@items = items || []
@items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
@@ -555,21 +580,6 @@
flash[:warning] = l(:warning_attachments_not_saved, obj.unsaved_attachments.size) if obj.unsaved_attachments.present?
end
- # Sets the `flash` notice or error based the number of issues that did not save
- #
- # @param [Array, Issue] issues all of the saved and unsaved Issues
- # @param [Array, Integer] unsaved_issue_ids the issue ids that were not saved
- def set_flash_from_bulk_issue_save(issues, unsaved_issue_ids)
- if unsaved_issue_ids.empty?
- flash[:notice] = l(:notice_successful_update) unless issues.empty?
- else
- flash[:error] = l(:notice_failed_to_save_issues,
- :count => unsaved_issue_ids.size,
- :total => issues.size,
- :ids => '#' + unsaved_issue_ids.join(', #'))
- end
- end
-
# Rescues an invalid query statement. Just in case...
def query_statement_invalid(exception)
logger.error "Query::StatementInvalid: #{exception.message}" if logger
diff -Nru redmine-2.3.3/app/controllers/boards_controller.rb redmine-2.4.2/app/controllers/boards_controller.rb
--- redmine-2.3.3/app/controllers/boards_controller.rb 2013-09-14 06:48:16.000000000 +0000
+++ redmine-2.4.2/app/controllers/boards_controller.rb 2013-12-23 08:48:37.000000000 +0000
@@ -25,7 +25,7 @@
helper :watchers
def index
- @boards = @project.boards.includes(:last_message => :author).all
+ @boards = @project.boards.includes(:project, :last_message => :author).all
# show the board if there is only one
if @boards.size == 1
@board = @boards.first
diff -Nru redmine-2.3.3/app/controllers/context_menus_controller.rb redmine-2.4.2/app/controllers/context_menus_controller.rb
--- redmine-2.3.3/app/controllers/context_menus_controller.rb 2013-09-14 06:48:16.000000000 +0000
+++ redmine-2.4.2/app/controllers/context_menus_controller.rb 2013-12-23 08:48:37.000000000 +0000
@@ -19,17 +19,15 @@
helper :watchers
helper :issues
+ before_filter :find_issues, :only => :issues
+
def issues
- @issues = Issue.visible.all(:conditions => {:id => params[:ids]}, :include => :project)
- (render_404; return) unless @issues.present?
if (@issues.size == 1)
@issue = @issues.first
end
@issue_ids = @issues.map(&:id).sort
@allowed_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
- @projects = @issues.collect(&:project).compact.uniq
- @project = @projects.first if @projects.size == 1
@can = {:edit => User.current.allowed_to?(:edit_issues, @projects),
:log_time => (@project && User.current.allowed_to?(:log_time, @project)),
@@ -73,8 +71,7 @@
end
def time_entries
- @time_entries = TimeEntry.all(
- :conditions => {:id => params[:ids]}, :include => :project)
+ @time_entries = TimeEntry.where(:id => params[:ids]).preload(:project).to_a
(render_404; return) unless @time_entries.present?
@projects = @time_entries.collect(&:project).compact.uniq
diff -Nru redmine-2.3.3/app/controllers/custom_fields_controller.rb redmine-2.4.2/app/controllers/custom_fields_controller.rb
--- redmine-2.3.3/app/controllers/custom_fields_controller.rb 2013-09-14 06:48:16.000000000 +0000
+++ redmine-2.4.2/app/controllers/custom_fields_controller.rb 2013-12-23 08:48:37.000000000 +0000
@@ -21,10 +21,18 @@
before_filter :require_admin
before_filter :build_new_custom_field, :only => [:new, :create]
before_filter :find_custom_field, :only => [:edit, :update, :destroy]
+ accept_api_auth :index
def index
- @custom_fields_by_type = CustomField.all.group_by {|f| f.class.name }
- @tab = params[:tab] || 'IssueCustomField'
+ respond_to do |format|
+ format.html {
+ @custom_fields_by_type = CustomField.all.group_by {|f| f.class.name }
+ @tab = params[:tab] || 'IssueCustomField'
+ }
+ format.api {
+ @custom_fields = CustomField.all
+ }
+ end
end
def new
diff -Nru redmine-2.3.3/app/controllers/issue_statuses_controller.rb redmine-2.4.2/app/controllers/issue_statuses_controller.rb
--- redmine-2.3.3/app/controllers/issue_statuses_controller.rb 2013-09-14 06:48:16.000000000 +0000
+++ redmine-2.4.2/app/controllers/issue_statuses_controller.rb 2013-12-23 08:48:37.000000000 +0000
@@ -40,7 +40,7 @@
def create
@issue_status = IssueStatus.new(params[:issue_status])
- if request.post? && @issue_status.save
+ if @issue_status.save
flash[:notice] = l(:notice_successful_create)
redirect_to issue_statuses_path
else
@@ -54,7 +54,7 @@
def update
@issue_status = IssueStatus.find(params[:id])
- if request.put? && @issue_status.update_attributes(params[:issue_status])
+ if @issue_status.update_attributes(params[:issue_status])
flash[:notice] = l(:notice_successful_update)
redirect_to issue_statuses_path
else
diff -Nru redmine-2.3.3/app/controllers/issues_controller.rb redmine-2.4.2/app/controllers/issues_controller.rb
--- redmine-2.3.3/app/controllers/issues_controller.rb 2013-09-14 06:48:16.000000000 +0000
+++ redmine-2.4.2/app/controllers/issues_controller.rb 2013-12-23 08:48:37.000000000 +0000
@@ -103,6 +103,9 @@
@journals = @issue.journals.includes(:user, :details).reorder("#{Journal.table_name}.id ASC").all
@journals.each_with_index {|j,i| j.indice = i+1}
@journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
+ Journal.preload_journals_details_custom_fields(@journals)
+ # TODO: use #select! when ruby1.8 support is dropped
+ @journals.reject! {|journal| !journal.notes? && journal.visible_details.empty?}
@journals.reverse! if User.current.wants_comments_in_reverse_order?
@changesets = @issue.changesets.visible.all
@@ -113,6 +116,8 @@
@edit_allowed = User.current.allowed_to?(:edit_issues, @project)
@priorities = IssuePriority.active
@time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
+ @relation = IssueRelation.new
+
respond_to do |format|
format.html {
retrieve_previous_and_next_issue_ids
@@ -176,7 +181,7 @@
@issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
saved = false
begin
- saved = @issue.save_issue_with_child_records(params, @time_entry)
+ saved = save_issue_with_child_records
rescue ActiveRecord::StaleObjectError
@conflict = true
if params[:last_journal_id]
@@ -228,7 +233,7 @@
else
@available_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
end
- @custom_fields = target_projects.map{|p|p.all_issue_custom_fields}.reduce(:&)
+ @custom_fields = target_projects.map{|p|p.all_issue_custom_fields.visible}.reduce(:&)
@assignables = target_projects.map(&:assignable_users).reduce(:&)
@trackers = target_projects.map(&:trackers).reduce(:&)
@versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&)
@@ -239,7 +244,9 @@
end
@safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
- render :layout => false if request.xhr?
+
+ @issue_params = params[:issue] || {}
+ @issue_params[:custom_field_values] ||= {}
end
def bulk_update
@@ -247,8 +254,8 @@
@copy = params[:copy].present?
attributes = parse_params_for_bulk_issue_attributes(params)
- unsaved_issue_ids = []
- moved_issues = []
+ unsaved_issues = []
+ saved_issues = []
if @copy && params[:copy_subtasks].present?
# Descendant issues will be copied with the parent task
@@ -256,39 +263,48 @@
@issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}}
end
- @issues.each do |issue|
- issue.reload
+ @issues.each do |orig_issue|
+ orig_issue.reload
if @copy
- issue = issue.copy({},
+ issue = orig_issue.copy({},
:attachments => params[:copy_attachments].present?,
:subtasks => params[:copy_subtasks].present?
)
+ else
+ issue = orig_issue
end
journal = issue.init_journal(User.current, params[:notes])
issue.safe_attributes = attributes
call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
if issue.save
- moved_issues << issue
+ saved_issues << issue
else
- # Keep unsaved issue ids to display them in flash error
- unsaved_issue_ids << issue.id
+ unsaved_issues << orig_issue
end
end
- set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
- if params[:follow]
- if @issues.size == 1 && moved_issues.size == 1
- redirect_to issue_path(moved_issues.first)
- elsif moved_issues.map(&:project).uniq.size == 1
- redirect_to project_issues_path(moved_issues.map(&:project).first)
+ if unsaved_issues.empty?
+ flash[:notice] = l(:notice_successful_update) unless saved_issues.empty?
+ if params[:follow]
+ if @issues.size == 1 && saved_issues.size == 1
+ redirect_to issue_path(saved_issues.first)
+ elsif saved_issues.map(&:project).uniq.size == 1
+ redirect_to project_issues_path(saved_issues.map(&:project).first)
+ end
+ else
+ redirect_back_or_default _project_issues_path(@project)
end
else
- redirect_back_or_default _project_issues_path(@project)
+ @saved_issues = @issues
+ @unsaved_issues = unsaved_issues
+ @issues = Issue.visible.find_all_by_id(@unsaved_issues.map(&:id))
+ bulk_edit
+ render :action => 'bulk_edit'
end
end
def destroy
- @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
+ @hours = TimeEntry.where(:issue_id => @issues.map(&:id)).sum(:hours).to_f
if @hours > 0
case params[:todo]
when 'destroy'
@@ -410,7 +426,7 @@
@issue.safe_attributes = params[:issue]
@priorities = IssuePriority.active
- @allowed_statuses = @issue.new_statuses_allowed_to(User.current, true)
+ @allowed_statuses = @issue.new_statuses_allowed_to(User.current, @issue.new_record?)
@available_watchers = (@issue.project.users.sort + @issue.watcher_users).uniq
end
@@ -436,4 +452,26 @@
end
attributes
end
+
+ # Saves @issue and a time_entry from the parameters
+ def save_issue_with_child_records
+ Issue.transaction do
+ if params[:time_entry] && (params[:time_entry][:hours].present? || params[:time_entry][:comments].present?) && User.current.allowed_to?(:log_time, @issue.project)
+ time_entry = @time_entry || TimeEntry.new
+ time_entry.project = @issue.project
+ time_entry.issue = @issue
+ time_entry.user = User.current
+ time_entry.spent_on = User.current.today
+ time_entry.attributes = params[:time_entry]
+ @issue.time_entries << time_entry
+ end
+
+ call_hook(:controller_issues_edit_before_save, { :params => params, :issue => @issue, :time_entry => time_entry, :journal => @issue.current_journal})
+ if @issue.save
+ call_hook(:controller_issues_edit_after_save, { :params => params, :issue => @issue, :time_entry => time_entry, :journal => @issue.current_journal})
+ else
+ raise ActiveRecord::Rollback
+ end
+ end
+ end
end
diff -Nru redmine-2.3.3/app/controllers/messages_controller.rb redmine-2.4.2/app/controllers/messages_controller.rb
--- redmine-2.3.3/app/controllers/messages_controller.rb 2013-09-14 06:48:16.000000000 +0000
+++ redmine-2.4.2/app/controllers/messages_controller.rb 2013-12-23 08:48:37.000000000 +0000
@@ -35,7 +35,7 @@
page = params[:page]
# Find the page of the requested reply
if params[:r] && page.nil?
- offset = @topic.children.count(:conditions => ["#{Message.table_name}.id < ?", params[:r].to_i])
+ offset = @topic.children.where("#{Message.table_name}.id < ?", params[:r].to_i).count
page = 1 + offset / REPLIES_PER_PAGE
end
diff -Nru redmine-2.3.3/app/controllers/my_controller.rb redmine-2.4.2/app/controllers/my_controller.rb
--- redmine-2.3.3/app/controllers/my_controller.rb 2013-09-14 06:48:16.000000000 +0000
+++ redmine-2.4.2/app/controllers/my_controller.rb 2013-12-23 08:48:37.000000000 +0000
@@ -17,6 +17,8 @@
class MyController < ApplicationController
before_filter :require_login
+ # let user change user's password when user has to
+ skip_before_filter :check_password_change, :only => :password
helper :issues
helper :users
@@ -55,7 +57,6 @@
@user.pref.attributes = params[:pref]
if @user.save
@user.pref.save
- @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
set_language_if_valid @user.language
flash[:notice] = l(:notice_account_updated)
redirect_to my_account_path
@@ -91,14 +92,17 @@
return
end
if request.post?
- if @user.check_password?(params[:password])
+ if !@user.check_password?(params[:password])
+ flash.now[:error] = l(:notice_account_wrong_password)
+ elsif params[:password] == params[:new_password]
+ flash.now[:error] = l(:notice_new_password_must_be_different)
+ else
@user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
+ @user.must_change_passwd = false
if @user.save
flash[:notice] = l(:notice_account_password_updated)
redirect_to my_account_path
end
- else
- flash[:error] = l(:notice_account_wrong_password)
end
end
end
diff -Nru redmine-2.3.3/app/controllers/projects_controller.rb redmine-2.4.2/app/controllers/projects_controller.rb
--- redmine-2.3.3/app/controllers/projects_controller.rb 2013-09-14 06:48:16.000000000 +0000
+++ redmine-2.4.2/app/controllers/projects_controller.rb 2013-12-23 08:48:37.000000000 +0000
@@ -82,7 +82,7 @@
if validate_parent_id && @project.save
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
- # Add current user as a project member if he is not admin
+ # Add current user as a project member if current user is not admin
unless User.current.admin?
r = Role.givable.find_by_id(Setting.new_project_user_role_id.to_i) || Role.givable.first
m = Member.new(:user => User.current, :roles => [r])
@@ -155,7 +155,7 @@
@total_issues_by_tracker = Issue.visible.where(cond).count(:group => :tracker)
if User.current.allowed_to?(:view_time_entries, @project)
- @total_hours = TimeEntry.visible.sum(:hours, :include => :project, :conditions => cond).to_f
+ @total_hours = TimeEntry.visible.where(cond).sum(:hours).to_f
end
@key = User.current.rss_key
diff -Nru redmine-2.3.3/app/controllers/queries_controller.rb redmine-2.4.2/app/controllers/queries_controller.rb
--- redmine-2.3.3/app/controllers/queries_controller.rb 2013-09-14 06:48:16.000000000 +0000
+++ redmine-2.4.2/app/controllers/queries_controller.rb 2013-12-23 08:48:37.000000000 +0000
@@ -45,7 +45,7 @@
@query = IssueQuery.new
@query.user = User.current
@query.project = @project
- @query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
+ @query.visibility = IssueQuery::VISIBILITY_PRIVATE unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
@query.build_from_params(params)
end
@@ -53,13 +53,13 @@
@query = IssueQuery.new(params[:query])
@query.user = User.current
@query.project = params[:query_is_for_all] ? nil : @project
- @query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
+ @query.visibility = IssueQuery::VISIBILITY_PRIVATE unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
@query.build_from_params(params)
@query.column_names = nil if params[:default_columns]
if @query.save
flash[:notice] = l(:notice_successful_create)
- redirect_to _project_issues_path(@project, :query_id => @query)
+ redirect_to_issues(:query_id => @query)
else
render :action => 'new', :layout => !request.xhr?
end
@@ -71,13 +71,13 @@
def update
@query.attributes = params[:query]
@query.project = nil if params[:query_is_for_all]
- @query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
+ @query.visibility = IssueQuery::VISIBILITY_PRIVATE unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
@query.build_from_params(params)
@query.column_names = nil if params[:default_columns]
if @query.save
flash[:notice] = l(:notice_successful_update)
- redirect_to _project_issues_path(@project, :query_id => @query)
+ redirect_to_issues(:query_id => @query)
else
render :action => 'edit'
end
@@ -85,7 +85,7 @@
def destroy
@query.destroy
- redirect_to _project_issues_path(@project, :set_filter => 1)
+ redirect_to_issues(:set_filter => 1)
end
private
@@ -103,4 +103,16 @@
rescue ActiveRecord::RecordNotFound
render_404
end
+
+ def redirect_to_issues(options)
+ if params[:gantt]
+ if @project
+ redirect_to project_gantt_path(@project, options)
+ else
+ redirect_to issues_gantt_path(options)
+ end
+ else
+ redirect_to _project_issues_path(@project, options)
+ end
+ end
end
diff -Nru redmine-2.3.3/app/controllers/repositories_controller.rb redmine-2.4.2/app/controllers/repositories_controller.rb
--- redmine-2.3.3/app/controllers/repositories_controller.rb 2013-09-14 06:48:16.000000000 +0000
+++ redmine-2.4.2/app/controllers/repositories_controller.rb 2013-12-23 08:48:37.000000000 +0000
@@ -18,7 +18,7 @@
require 'SVG/Graph/Bar'
require 'SVG/Graph/BarHorizontal'
require 'digest/sha1'
-require 'redmine/scm/adapters/abstract_adapter'
+require 'redmine/scm/adapters'
class ChangesetNotFound < Exception; end
class InvalidRevisionParam < Exception; end
@@ -111,7 +111,7 @@
end
def show
- @repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty?
+ @repository.fetch_changesets if @project.active? && Setting.autofetch_changesets? && @path.empty?
@entries = @repository.entries(@path, @rev)
@changeset = @repository.find_changeset_by_name(@rev)
@@ -229,7 +229,8 @@
# Adds a related issue to a changeset
# POST /projects/:project_id/repository/(:repository_id/)revisions/:rev/issues
def add_related_issue
- @issue = @changeset.find_referenced_issue_by_id(params[:issue_id])
+ issue_id = params[:issue_id].to_s.sub(/^#/,'')
+ @issue = @changeset.find_referenced_issue_by_id(issue_id)
if @issue && (!@issue.visible? || @changeset.issues.include?(@issue))
@issue = nil
end
@@ -352,15 +353,18 @@
@date_to = Date.today
@date_from = @date_to << 11
@date_from = Date.civil(@date_from.year, @date_from.month, 1)
- commits_by_day = Changeset.count(
- :all, :group => :commit_date,
- :conditions => ["repository_id = ? AND commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
+ commits_by_day = Changeset.
+ where("repository_id = ? AND commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to).
+ group(:commit_date).
+ count
commits_by_month = [0] * 12
commits_by_day.each {|c| commits_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last }
- changes_by_day = Change.count(
- :all, :group => :commit_date, :include => :changeset,
- :conditions => ["#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
+ changes_by_day = Change.
+ joins(:changeset).
+ where("#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to).
+ group(:commit_date).
+ count
changes_by_month = [0] * 12
changes_by_day.each {|c| changes_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last }
@@ -393,10 +397,10 @@
end
def graph_commits_per_author(repository)
- commits_by_author = Changeset.count(:all, :group => :committer, :conditions => ["repository_id = ?", repository.id])
+ commits_by_author = Changeset.where("repository_id = ?", repository.id).group(:committer).count
commits_by_author.to_a.sort! {|x, y| x.last <=> y.last}
- changes_by_author = Change.count(:all, :group => :committer, :include => :changeset, :conditions => ["#{Changeset.table_name}.repository_id = ?", repository.id])
+ changes_by_author = Change.joins(:changeset).where("#{Changeset.table_name}.repository_id = ?", repository.id).group(:committer).count
h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
fields = commits_by_author.collect {|r| r.first}
@@ -411,7 +415,7 @@
fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
graph = SVG::Graph::BarHorizontal.new(
- :height => 400,
+ :height => 30 * commits_data.length,
:width => 800,
:fields => fields,
:stack => :side,
diff -Nru redmine-2.3.3/app/controllers/settings_controller.rb redmine-2.4.2/app/controllers/settings_controller.rb
--- redmine-2.3.3/app/controllers/settings_controller.rb 2013-09-14 06:48:16.000000000 +0000
+++ redmine-2.4.2/app/controllers/settings_controller.rb 2013-12-23 08:48:37.000000000 +0000
@@ -33,9 +33,7 @@
if request.post? && params[:settings] && params[:settings].is_a?(Hash)
settings = (params[:settings] || {}).dup.symbolize_keys
settings.each do |name, value|
- # remove blank values in array settings
- value.delete_if {|v| v.blank? } if value.is_a?(Array)
- Setting[name] = value
+ Setting.set_from_params name, value
end
flash[:notice] = l(:notice_successful_update)
redirect_to settings_path(:tab => params[:tab])
@@ -48,6 +46,9 @@
@guessed_host_and_path = request.host_with_port.dup
@guessed_host_and_path << ('/'+ Redmine::Utils.relative_url_root.gsub(%r{^\/}, '')) unless Redmine::Utils.relative_url_root.blank?
+ @commit_update_keywords = Setting.commit_update_keywords.dup
+ @commit_update_keywords = [{}] unless @commit_update_keywords.is_a?(Array) && @commit_update_keywords.any?
+
Redmine::Themes.rescan
end
end
diff -Nru redmine-2.3.3/app/controllers/sys_controller.rb redmine-2.4.2/app/controllers/sys_controller.rb
--- redmine-2.3.3/app/controllers/sys_controller.rb 2013-09-14 06:48:16.000000000 +0000
+++ redmine-2.4.2/app/controllers/sys_controller.rb 2013-12-23 08:48:37.000000000 +0000
@@ -19,11 +19,7 @@
before_filter :check_enabled
def projects
- p = Project.active.has_module(:repository).find(
- :all,
- :include => :repository,
- :order => "#{Project.table_name}.identifier"
- )
+ p = Project.active.has_module(:repository).order("#{Project.table_name}.identifier").preload(:repository).all
# extra_info attribute from repository breaks activeresource client
render :xml => p.to_xml(
:only => [:id, :identifier, :name, :is_public, :status],
diff -Nru redmine-2.3.3/app/controllers/timelog_controller.rb redmine-2.4.2/app/controllers/timelog_controller.rb
--- redmine-2.3.3/app/controllers/timelog_controller.rb 2013-09-14 06:48:16.000000000 +0000
+++ redmine-2.4.2/app/controllers/timelog_controller.rb 2013-12-23 08:48:37.000000000 +0000
@@ -46,18 +46,15 @@
sort_init(@query.sort_criteria.empty? ? [['spent_on', 'desc']] : @query.sort_criteria)
sort_update(@query.sortable_columns)
- scope = time_entry_scope(:order => sort_clause)
+ scope = time_entry_scope(:order => sort_clause).
+ includes(:project, :user, :issue).
+ preload(:issue => [:project, :tracker, :status, :assigned_to, :priority])
respond_to do |format|
format.html {
- # Paginate results
@entry_count = scope.count
@entry_pages = Paginator.new @entry_count, per_page_option, params['page']
- @entries = scope.all(
- :include => [:project, :activity, :user, {:issue => :tracker}],
- :limit => @entry_pages.per_page,
- :offset => @entry_pages.offset
- )
+ @entries = scope.offset(@entry_pages.offset).limit(@entry_pages.per_page).all
@total_hours = scope.sum(:hours).to_f
render :layout => !request.xhr?
@@ -65,24 +62,15 @@
format.api {
@entry_count = scope.count
@offset, @limit = api_offset_and_limit
- @entries = scope.all(
- :include => [:project, :activity, :user, {:issue => :tracker}],
- :limit => @limit,
- :offset => @offset
- )
+ @entries = scope.offset(@offset).limit(@limit).preload(:custom_values => :custom_field).all
}
format.atom {
- entries = scope.reorder("#{TimeEntry.table_name}.created_on DESC").all(
- :include => [:project, :activity, :user, {:issue => :tracker}],
- :limit => Setting.feeds_limit.to_i
- )
+ entries = scope.limit(Setting.feeds_limit.to_i).reorder("#{TimeEntry.table_name}.created_on DESC").all
render_feed(entries, :title => l(:label_spent_time))
}
format.csv {
# Export all entries
- @entries = scope.all(
- :include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}]
- )
+ @entries = scope.all
send_data(query_to_csv(@entries, @query, params), :type => 'text/csv; header=present', :filename => 'timelog.csv')
}
end
@@ -194,6 +182,7 @@
time_entry.safe_attributes = attributes
call_hook(:controller_time_entries_bulk_edit_before_save, { :params => params, :time_entry => time_entry })
unless time_entry.save
+ logger.info "time entry could not be updated: #{time_entry.errors.full_messages}" if logger && logger.info
# Keep unsaved time_entry ids to display them in flash error
unsaved_time_entry_ids << time_entry.id
end
diff -Nru redmine-2.3.3/app/controllers/users_controller.rb redmine-2.4.2/app/controllers/users_controller.rb
--- redmine-2.3.3/app/controllers/users_controller.rb 2013-09-14 06:48:16.000000000 +0000
+++ redmine-2.4.2/app/controllers/users_controller.rb 2013-12-23 08:48:37.000000000 +0000
@@ -60,7 +60,7 @@
def show
# show projects based on current user visibility
- @memberships = @user.memberships.all(:conditions => Project.visible_condition(User.current))
+ @memberships = @user.memberships.where(Project.visible_condition(User.current)).all
events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
@events_by_day = events.group_by(&:event_date)
@@ -80,6 +80,7 @@
def new
@user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
+ @user.safe_attributes = params[:user]
@auth_sources = AuthSource.all
end
@@ -93,15 +94,15 @@
if @user.save
@user.pref.attributes = params[:pref]
@user.pref.save
- @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
- Mailer.account_information(@user, params[:user][:password]).deliver if params[:send_information]
+ Mailer.account_information(@user, @user.password).deliver if params[:send_information]
respond_to do |format|
format.html {
flash[:notice] = l(:notice_user_successful_create, :id => view_context.link_to(@user.login, user_path(@user)))
if params[:continue]
- redirect_to new_user_path
+ attrs = params[:user].slice(:generate_password)
+ redirect_to new_user_path(:user => attrs)
else
redirect_to edit_user_path(@user)
end
@@ -139,12 +140,11 @@
if @user.save
@user.pref.save
- @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
if was_activated
Mailer.account_activated(@user).deliver
- elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.auth_source_id.nil?
- Mailer.account_information(@user, params[:user][:password]).deliver
+ elsif @user.active? && params[:send_information] && @user.password.present? && @user.auth_source_id.nil?
+ Mailer.account_information(@user, @user.password).deliver
end
respond_to do |format|
diff -Nru redmine-2.3.3/app/controllers/versions_controller.rb redmine-2.4.2/app/controllers/versions_controller.rb
--- redmine-2.3.3/app/controllers/versions_controller.rb 2013-09-14 06:48:16.000000000 +0000
+++ redmine-2.4.2/app/controllers/versions_controller.rb 2013-12-23 08:48:37.000000000 +0000
@@ -46,11 +46,11 @@
@issues_by_version = {}
if @selected_tracker_ids.any? && @versions.any?
- issues = Issue.visible.all(
- :include => [:project, :status, :tracker, :priority, :fixed_version],
- :conditions => {:tracker_id => @selected_tracker_ids, :project_id => project_ids, :fixed_version_id => @versions.map(&:id)},
- :order => "#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id"
- )
+ issues = Issue.visible.
+ includes(:project, :tracker).
+ preload(:status, :priority, :fixed_version).
+ where(:tracker_id => @selected_tracker_ids, :project_id => project_ids, :fixed_version_id => @versions.map(&:id)).
+ order("#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id")
@issues_by_version = issues.group_by(&:fixed_version)
end
@versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?}
diff -Nru redmine-2.3.3/app/controllers/wiki_controller.rb redmine-2.4.2/app/controllers/wiki_controller.rb
--- redmine-2.3.3/app/controllers/wiki_controller.rb 2013-09-14 06:48:16.000000000 +0000
+++ redmine-2.4.2/app/controllers/wiki_controller.rb 2013-12-23 08:48:37.000000000 +0000
@@ -15,8 +15,6 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-require 'diff'
-
# The WikiController follows the Rails REST controller pattern but with
# a few differences
#
@@ -64,7 +62,12 @@
# display a page (in editing mode if it doesn't exist)
def show
- if @page.new_record?
+ if params[:version] && !User.current.allowed_to?(:view_wiki_edits, @project)
+ deny_access
+ return
+ end
+ @content = @page.content_for_version(params[:version])
+ if @content.nil?
if User.current.allowed_to?(:edit_wiki_pages, @project) && editable? && !api_request?
edit
render :action => 'edit'
@@ -73,11 +76,6 @@
end
return
end
- if params[:version] && !User.current.allowed_to?(:view_wiki_edits, @project)
- deny_access
- return
- end
- @content = @page.content_for_version(params[:version])
if User.current.allowed_to?(:export_wiki_pages, @project)
if params[:format] == 'pdf'
send_data(wiki_page_to_pdf(@page, @project), :type => 'application/pdf', :filename => "#{@page.title}.pdf")
@@ -106,19 +104,19 @@
def edit
return render_403 unless editable?
if @page.new_record?
- @page.content = WikiContent.new(:page => @page)
if params[:parent].present?
@page.parent = @page.wiki.find_page(params[:parent].to_s)
end
end
@content = @page.content_for_version(params[:version])
+ @content ||= WikiContent.new(:page => @page)
@content.text = initial_page_content(@page) if @content.text.blank?
# don't keep previous comment
@content.comments = nil
# To prevent StaleObjectError exception when reverting to a previous version
- @content.version = @page.content.version
+ @content.version = @page.content.version if @page.content
@text = @content.text
if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
@@ -132,10 +130,9 @@
def update
return render_403 unless editable?
was_new_page = @page.new_record?
- @page.content = WikiContent.new(:page => @page) if @page.new_record?
@page.safe_attributes = params[:wiki_page]
- @content = @page.content
+ @content = @page.content || WikiContent.new(:page => @page)
content_params = params[:content]
if content_params.nil? && params[:wiki_page].is_a?(Hash)
content_params = params[:wiki_page].slice(:text, :comments, :version)
@@ -147,20 +144,23 @@
if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
@section = params[:section].to_i
@section_hash = params[:section_hash]
- @content.text = Redmine::WikiFormatting.formatter.new(@content.text).update_section(params[:section].to_i, @text, @section_hash)
+ @content.text = Redmine::WikiFormatting.formatter.new(@content.text).update_section(@section, @text, @section_hash)
else
@content.version = content_params[:version] if content_params[:version]
@content.text = @text
end
@content.author = User.current
- if @page.save_with_content
+ if @page.save_with_content(@content)
attachments = Attachment.attach_files(@page, params[:attachments])
render_attachment_warning_if_needed(@page)
call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
respond_to do |format|
- format.html { redirect_to project_wiki_page_path(@project, @page.title) }
+ format.html {
+ anchor = @section ? "section-#{@section}" : nil
+ redirect_to project_wiki_page_path(@project, @page.title, :anchor => anchor)
+ }
format.api {
if was_new_page
render :action => 'show', :status => :created, :location => project_wiki_page_path(@project, @page.title)
diff -Nru redmine-2.3.3/app/helpers/application_helper.rb redmine-2.4.2/app/helpers/application_helper.rb
--- redmine-2.3.3/app/helpers/application_helper.rb 2013-09-14 06:48:11.000000000 +0000
+++ redmine-2.4.2/app/helpers/application_helper.rb 2013-12-23 08:48:37.000000000 +0000
@@ -79,7 +79,8 @@
subject = truncate(subject, :length => options[:truncate])
end
end
- s = link_to text, issue_path(issue), :class => issue.css_classes, :title => title
+ only_path = options[:only_path].nil? ? true : options[:only_path]
+ s = link_to text, issue_path(issue, :only_path => only_path), :class => issue.css_classes, :title => title
s << h(": #{subject}") if subject
s = h("#{issue.project} - ") + s if options[:project]
s
@@ -330,7 +331,7 @@
end
groups = ''
collection.sort.each do |element|
- selected_attribute = ' selected="selected"' if option_value_selected?(element, selected)
+ selected_attribute = ' selected="selected"' if option_value_selected?(element, selected) || element.id.to_s == selected
(element.is_a?(Group) ? groups : s) << %()
end
unless groups.empty?
@@ -348,6 +349,10 @@
options
end
+ def option_tag(name, text, value, selected=nil, options={})
+ content_tag 'option', value, options.merge(:value => value, :selected => (value == selected))
+ end
+
# Truncates and returns the string as a single line
def truncate_single_line(string, *args)
truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
@@ -380,7 +385,7 @@
if @project
link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => User.current.time_to_date(time)}, :title => format_time(time))
else
- content_tag('acronym', text, :title => format_time(time))
+ content_tag('abbr', text, :title => format_time(time))
end
end
@@ -445,12 +450,31 @@
end
end
+ # Returns a h2 tag and sets the html title with the given arguments
+ def title(*args)
+ strings = args.map do |arg|
+ if arg.is_a?(Array) && arg.size >= 2
+ link_to(*arg)
+ else
+ h(arg.to_s)
+ end
+ end
+ html_title args.reverse.map {|s| (s.is_a?(Array) ? s.first : s).to_s}
+ content_tag('h2', strings.join(' » ').html_safe)
+ end
+
+ # Sets the html title
+ # Returns the html title when called without arguments
+ # Current project name and app_title and automatically appended
+ # Exemples:
+ # html_title 'Foo', 'Bar'
+ # html_title # => 'Foo - Bar - My Project - Redmine'
def html_title(*args)
if args.empty?
title = @html_title || []
title << @project.name if @project
title << Setting.app_title unless Setting.app_title == title.last
- title.select {|t| !t.blank? }.join(' - ')
+ title.reject(&:blank?).join(' - ')
else
@html_title ||= []
@html_title += args
@@ -465,6 +489,7 @@
css << 'theme-' + theme.name
end
+ css << 'project-' + @project.identifier if @project && @project.identifier.present?
css << 'controller-' + controller_name
css << 'action-' + action_name
css.join(' ')
@@ -615,7 +640,7 @@
else
wiki_page_id = page.present? ? Wiki.titleize(page) : nil
parent = wiki_page.nil? && obj.is_a?(WikiContent) && obj.page && project == link_project ? obj.page.title : nil
- url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project,
+ url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project,
:id => wiki_page_id, :version => nil, :anchor => anchor, :parent => parent)
end
end
@@ -656,6 +681,9 @@
# export:some/file -> Force the download of the file
# Forum messages:
# message#1218 -> Link to message with id 1218
+ # Projects:
+ # project:someproject -> Link to project named "someproject"
+ # project#3 -> Link to project with id 3
#
# Links can refer other objects from other projects, using project identifier:
# identifier:r52
@@ -692,7 +720,7 @@
when nil
if oid.to_s == identifier && issue = Issue.visible.find_by_id(oid, :include => :status)
anchor = comment_id ? "note-#{comment_id}" : nil
- link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid, :anchor => anchor},
+ link = link_to(h("##{oid}#{comment_suffix}"), {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid, :anchor => anchor},
:class => issue.css_classes,
:title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
end
@@ -804,7 +832,8 @@
content_tag('div',
link_to(image_tag('edit.png'), options[:edit_section_links].merge(:section => @current_section)),
:class => 'contextual',
- :title => l(:button_edit_section)) + heading.html_safe
+ :title => l(:button_edit_section),
+ :id => "section-#{@current_section}") + heading.html_safe
else
heading
end
@@ -975,7 +1004,7 @@
html << "\n"
end
html.html_safe
- end
+ end
def delete_link(url, options={})
options = {
@@ -989,8 +1018,8 @@
def preview_link(url, form, target='preview', options={})
content_tag 'a', l(:label_preview), {
- :href => "#",
- :onclick => %|submitPreview("#{escape_javascript url_for(url)}", "#{escape_javascript form}", "#{escape_javascript target}"); return false;|,
+ :href => "#",
+ :onclick => %|submitPreview("#{escape_javascript url_for(url)}", "#{escape_javascript form}", "#{escape_javascript target}"); return false;|,
:accesskey => accesskey(:preview)
}.merge(options)
end
@@ -1035,7 +1064,7 @@
(pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : ''.html_safe) +
(pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : ''.html_safe) +
(pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : ''.html_safe)
- ), :class => 'progress', :style => "width: #{width};").html_safe +
+ ), :class => "progress progress-#{pcts[0]}", :style => "width: #{width};").html_safe +
content_tag('p', legend, :class => 'percent').html_safe
end
@@ -1068,6 +1097,7 @@
def include_calendar_headers_tags
unless @calendar_headers_tags_included
+ tags = javascript_include_tag("datepicker")
@calendar_headers_tags_included = true
content_for :header_tags do
start_of_week = Setting.start_of_week
@@ -1075,15 +1105,16 @@
# Redmine uses 1..7 (monday..sunday) in settings and locales
# JQuery uses 0..6 (sunday..saturday), 7 needs to be changed to 0
start_of_week = start_of_week.to_i % 7
-
- tags = javascript_tag(
+ tags << javascript_tag(
"var datepickerOptions={dateFormat: 'yy-mm-dd', firstDay: #{start_of_week}, " +
- "showOn: 'button', buttonImageOnly: true, buttonImage: '" +
+ "showOn: 'button', buttonImageOnly: true, buttonImage: '" +
path_to_image('/images/calendar.png') +
- "', showButtonPanel: true, showWeek: true, showOtherMonths: true, selectOtherMonths: true};")
+ "', showButtonPanel: true, showWeek: true, showOtherMonths: true, " +
+ "selectOtherMonths: true, changeMonth: true, changeYear: true, " +
+ "beforeShow: beforeShowDatePicker};")
jquery_locale = l('jquery.locale', :default => current_language.to_s)
unless jquery_locale == 'en'
- tags << javascript_include_tag("i18n/jquery.ui.datepicker-#{jquery_locale}.js")
+ tags << javascript_include_tag("i18n/jquery.ui.datepicker-#{jquery_locale}.js")
end
tags
end
@@ -1143,18 +1174,13 @@
super sources, options
end
- def content_for(name, content = nil, &block)
- @has_content ||= {}
- @has_content[name] = true
- super(name, content, &block)
- end
-
+ # TODO: remove this in 2.5.0
def has_content?(name)
- (@has_content && @has_content[name]) || false
+ content_for?(name)
end
def sidebar_content?
- has_content?(:sidebar) || view_layouts_base_sidebar_hook_response.present?
+ content_for?(:sidebar) || view_layouts_base_sidebar_hook_response.present?
end
def view_layouts_base_sidebar_hook_response
diff -Nru redmine-2.3.3/app/helpers/custom_fields_helper.rb redmine-2.4.2/app/helpers/custom_fields_helper.rb
--- redmine-2.3.3/app/helpers/custom_fields_helper.rb 2013-09-14 06:48:11.000000000 +0000
+++ redmine-2.4.2/app/helpers/custom_fields_helper.rb 2013-12-23 08:48:37.000000000 +0000
@@ -19,8 +19,29 @@
module CustomFieldsHelper
+ CUSTOM_FIELDS_TABS = [
+ {:name => 'IssueCustomField', :partial => 'custom_fields/index',
+ :label => :label_issue_plural},
+ {:name => 'TimeEntryCustomField', :partial => 'custom_fields/index',
+ :label => :label_spent_time},
+ {:name => 'ProjectCustomField', :partial => 'custom_fields/index',
+ :label => :label_project_plural},
+ {:name => 'VersionCustomField', :partial => 'custom_fields/index',
+ :label => :label_version_plural},
+ {:name => 'UserCustomField', :partial => 'custom_fields/index',
+ :label => :label_user_plural},
+ {:name => 'GroupCustomField', :partial => 'custom_fields/index',
+ :label => :label_group_plural},
+ {:name => 'TimeEntryActivityCustomField', :partial => 'custom_fields/index',
+ :label => TimeEntryActivity::OptionName},
+ {:name => 'IssuePriorityCustomField', :partial => 'custom_fields/index',
+ :label => IssuePriority::OptionName},
+ {:name => 'DocumentCategoryCustomField', :partial => 'custom_fields/index',
+ :label => DocumentCategory::OptionName}
+ ]
+
def custom_fields_tabs
- CustomField::CUSTOM_FIELDS_TABS
+ CUSTOM_FIELDS_TABS
end
# Return custom field html tag corresponding to its format
@@ -77,32 +98,44 @@
custom_field_label_tag(name, custom_value, options) + custom_field_tag(name, custom_value)
end
- def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil)
+ def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil, value='')
field_name = "#{name}[custom_field_values][#{custom_field.id}]"
field_name << "[]" if custom_field.multiple?
field_id = "#{name}_custom_field_values_#{custom_field.id}"
tag_options = {:id => field_id, :class => "#{custom_field.field_format}_cf"}
+ unset_tag = ''
+ unless custom_field.is_required?
+ unset_tag = content_tag('label',
+ check_box_tag(field_name, '__none__', (value == '__none__'), :id => nil, :data => {:disables => "##{field_id}"}) + l(:button_clear),
+ :class => 'inline'
+ )
+ end
+
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
case field_format.try(:edit_as)
when "date"
- text_field_tag(field_name, '', tag_options.merge(:size => 10)) +
- calendar_for(field_id)
+ text_field_tag(field_name, value, tag_options.merge(:size => 10)) +
+ calendar_for(field_id) +
+ unset_tag
when "text"
- text_area_tag(field_name, '', tag_options.merge(:rows => 3))
+ text_area_tag(field_name, value, tag_options.merge(:rows => 3)) +
+ '
'.html_safe +
+ unset_tag
when "bool"
select_tag(field_name, options_for_select([[l(:label_no_change_option), ''],
[l(:general_text_yes), '1'],
- [l(:general_text_no), '0']]), tag_options)
+ [l(:general_text_no), '0']], value), tag_options)
when "list"
options = []
options << [l(:label_no_change_option), ''] unless custom_field.multiple?
options << [l(:label_none), '__none__'] unless custom_field.is_required?
options += custom_field.possible_values_options(projects)
- select_tag(field_name, options_for_select(options), tag_options.merge(:multiple => custom_field.multiple?))
+ select_tag(field_name, options_for_select(options, value), tag_options.merge(:multiple => custom_field.multiple?))
else
- text_field_tag(field_name, '', tag_options)
+ text_field_tag(field_name, value, tag_options) +
+ unset_tag
end
end
diff -Nru redmine-2.3.3/app/helpers/issues_helper.rb redmine-2.4.2/app/helpers/issues_helper.rb
--- redmine-2.3.3/app/helpers/issues_helper.rb 2013-09-14 06:48:11.000000000 +0000
+++ redmine-2.4.2/app/helpers/issues_helper.rb 2013-12-23 08:48:37.000000000 +0000
@@ -94,6 +94,20 @@
s.html_safe
end
+ # Returns an array of error messages for bulk edited issues
+ def bulk_edit_error_messages(issues)
+ messages = {}
+ issues.each do |issue|
+ issue.errors.full_messages.each do |message|
+ messages[message] ||= []
+ messages[message] << issue
+ end
+ end
+ messages.map { |message, issues|
+ "#{message}: " + issues.map {|i| "##{i.id}"}.join(', ')
+ }
+ end
+
# Returns a link for adding a new subtask to the given issue
def link_to_new_subtask(issue)
attrs = {
@@ -146,12 +160,13 @@
end
def render_custom_fields_rows(issue)
- return if issue.custom_field_values.empty?
+ values = issue.visible_custom_field_values
+ return if values.empty?
ordered_values = []
- half = (issue.custom_field_values.size / 2.0).ceil
+ half = (values.size / 2.0).ceil
half.times do |i|
- ordered_values << issue.custom_field_values[i]
- ordered_values << issue.custom_field_values[i + half]
+ ordered_values << values[i]
+ ordered_values << values[i + half]
end
s = "
- | <%= text_field_tag 'username', params[:username], :tabindex => '1' %> | ++ | <%= text_field_tag 'username', params[:username], :tabindex => '1' %> | |||||
- | <%= password_field_tag 'password', nil, :tabindex => '2' %> | ++ | <%= password_field_tag 'password', nil, :tabindex => '2' %> | |||||
- | <%= text_field_tag "openid_url", nil, :tabindex => '3' %> | ++ | <%= text_field_tag "openid_url", nil, :tabindex => '3' %> | |||||
- | + | <% if Setting.autologin? %> <% end %> | ||||||
+ | <% if Setting.lost_password? %> <%= link_to l(:label_password_lost), lost_password_path %> <% end %> | -+ |
<%= l(label) %> | -<%= image_tag((result ? 'true.png' : 'exclamation.png'), + | <%= l(label) %> | +<%= image_tag((result ? 'true.png' : 'exclamation.png'), :style => "vertical-align:bottom;") %> |
<%=h plugin.name %> + | <%=h plugin.name %> <%= content_tag('span', h(plugin.description), :class => 'description') unless plugin.description.blank? %> <%= content_tag('span', link_to(h(plugin.url), plugin.url), :class => 'url') unless plugin.url.blank? %> | diff -Nru redmine-2.3.3/app/views/admin/projects.html.erb redmine-2.4.2/app/views/admin/projects.html.erb --- redmine-2.3.3/app/views/admin/projects.html.erb 2013-09-14 06:48:27.000000000 +0000 +++ redmine-2.4.2/app/views/admin/projects.html.erb 2013-12-23 08:48:38.000000000 +0000 @@ -2,7 +2,7 @@ <%= link_to l(:label_project_new), {:controller => 'projects', :action => 'new'}, :class => 'icon icon-add' %> -
<%= link_to(h(source.name), :action => 'edit', :id => source)%> | -<%= h source.auth_method_name %> | -<%= h source.host %> | -<%= h source.users.count %> | +<%= link_to(h(source.name), :action => 'edit', :id => source)%> | +<%= h source.auth_method_name %> | +<%= h source.host %> | +<%= h source.users.count %> |
<%= link_to l(:button_test), try_connection_auth_source_path(source), :class => 'icon icon-test' %>
<%= delete_link auth_source_path(source) %>
diff -Nru redmine-2.3.3/app/views/auth_sources/new.html.erb redmine-2.4.2/app/views/auth_sources/new.html.erb
--- redmine-2.3.3/app/views/auth_sources/new.html.erb 2013-09-14 06:48:22.000000000 +0000
+++ redmine-2.4.2/app/views/auth_sources/new.html.erb 2013-12-23 08:48:38.000000000 +0000
@@ -1,4 +1,4 @@
-<%=l(:label_auth_source_new)%> (<%= h(@auth_source.auth_method_name) %>)+<%= title [l(:label_auth_source_plural), auth_sources_path], "#{l(:label_auth_source_new)} (#{@auth_source.auth_method_name})" %> <%= labelled_form_for @auth_source, :as => :auth_source, :url => auth_sources_path, :html => {:id => 'auth_source_form'} do |f| %> <%= hidden_field_tag 'type', @auth_source.type %> diff -Nru redmine-2.3.3/app/views/boards/index.html.erb redmine-2.4.2/app/views/boards/index.html.erb --- redmine-2.3.3/app/views/boards/index.html.erb 2013-09-14 06:48:23.000000000 +0000 +++ redmine-2.4.2/app/views/boards/index.html.erb 2013-12-23 08:48:38.000000000 +0000 @@ -10,7 +10,7 @@ |
+ |
<%= link_to h(board.name), project_board_path(board.project, board), :class => "board" %> <%=h board.description %> |
diff -Nru redmine-2.3.3/app/views/common/_file.html.erb redmine-2.4.2/app/views/common/_file.html.erb
--- redmine-2.3.3/app/views/common/_file.html.erb 2013-09-14 06:48:27.000000000 +0000
+++ redmine-2.4.2/app/views/common/_file.html.erb 2013-12-23 08:48:38.000000000 +0000
@@ -3,8 +3,8 @@
|||||||
+ | ||||||||
---|---|---|---|---|---|---|---|---|
<%= line_num %> |
diff -Nru redmine-2.3.3/app/views/common/_tabs.html.erb redmine-2.4.2/app/views/common/_tabs.html.erb
--- redmine-2.3.3/app/views/common/_tabs.html.erb 2013-09-14 06:48:27.000000000 +0000
+++ redmine-2.4.2/app/views/common/_tabs.html.erb 2013-12-23 08:48:38.000000000 +0000
@@ -6,7 +6,7 @@
|