diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 000000000..998308af4
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,10 @@
+
+____________________________________________________________________
+
+**Do not send a pull request 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 --git a/.gitignore b/.gitignore
index 173b030c2..e19e36c2d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
/.project
+/.idea
/.loadpath
/.powrc
/.rvmrc
diff --git a/.hgignore b/.hgignore
index cbc028a94..e1e895da3 100644
--- a/.hgignore
+++ b/.hgignore
@@ -1,6 +1,7 @@
syntax: glob
.project
+.idea
.loadpath
.powrc
.rvmrc
diff --git a/Gemfile b/Gemfile
index b54c6ed4f..cb5646561 100644
--- a/Gemfile
+++ b/Gemfile
@@ -11,26 +11,24 @@ if RUBY_VERSION < "2.1"
end
gem "jquery-rails", "~> 3.1.4"
gem "coderay", "~> 1.1.1"
-gem "builder", ">= 3.0.4"
gem "request_store", "1.0.5"
gem "mime-types", (RUBY_VERSION >= "2.0" ? "~> 3.0" : "~> 2.99")
gem "protected_attributes"
-gem "actionpack-action_caching"
gem "actionpack-xml_parser"
gem "roadie-rails", "~> 1.1.1"
gem "roadie", "~> 3.2.1"
gem "mimemagic"
gem "mail", "~> 2.6.4"
-gem "nokogiri", (RUBY_VERSION >= "2.1" ? "~> 1.7.2" : "~> 1.6.8")
+gem "nokogiri", (RUBY_VERSION >= "2.1" ? "~> 1.8.1" : "~> 1.6.8")
gem "i18n", "~> 0.7.0"
gem "ffi", "1.9.14", :platforms => :mingw if RUBY_VERSION < "2.0"
-# Request at least rails-html-sanitizer 1.0.3 because of security advisories
+# Request at least rails-html-sanitizer 1.0.3 because of security advisories
gem "rails-html-sanitizer", ">= 1.0.3"
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
-gem 'tzinfo-data', platforms: [:mingw, :x64_mingw, :mswin, :jruby]
+gem 'tzinfo-data', platforms: [:mingw, :x64_mingw, :mswin]
gem "rbpdf", "~> 1.19.6"
# Optional gem for LDAP authentication
@@ -52,16 +50,10 @@ platforms :mri, :mingw, :x64_mingw do
# Optional Markdown support, not for JRuby
group :markdown do
- gem "redcarpet", "~> 3.3.2"
+ gem "redcarpet", "~> 3.4.0"
end
end
-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.3.2"
-end
-
# Include database gems for the adapters found in the database
# configuration file
require 'erb'
@@ -75,19 +67,13 @@ if File.exist?(database_file)
case adapter
when 'mysql2'
gem "mysql2", "~> 0.4.6", :platforms => [:mri, :mingw, :x64_mingw]
- gem "activerecord-jdbcmysql-adapter", :platforms => :jruby
- when 'mysql'
- gem "activerecord-jdbcmysql-adapter", :platforms => :jruby
when /postgresql/
gem "pg", "~> 0.18.1", :platforms => [:mri, :mingw, :x64_mingw]
- gem "activerecord-jdbcpostgresql-adapter", :platforms => :jruby
when /sqlite3/
gem "sqlite3", (RUBY_VERSION < "2.0" && RUBY_PLATFORM =~ /mingw/ ? "1.3.12" : "~>1.3.12"),
:platforms => [:mri, :mingw, :x64_mingw]
- gem "jdbc-sqlite3", ">= 3.8.10.1", :platforms => :jruby
- gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
when /sqlserver/
- gem "tiny_tds", "~> 0.6.2", :platforms => [:mri, :mingw, :x64_mingw]
+ gem "tiny_tds", (RUBY_VERSION >= "2.0" ? "~> 1.0.5" : "~> 0.7.0"), :platforms => [:mri, :mingw, :x64_mingw]
gem "activerecord-sqlserver-adapter", :platforms => [:mri, :mingw, :x64_mingw]
else
warn("Unknown database adapter `#{adapter}` found in config/database.yml, use Gemfile.local to load your own database gems")
@@ -110,8 +96,10 @@ group :test do
gem "rails-dom-testing"
gem "mocha"
gem "simplecov", "~> 0.9.1", :require => false
+ # TODO: remove this after upgrading to Rails 5
+ gem "test_after_commit", "~> 0.4.2"
# For running UI tests
- gem "capybara"
+ gem "capybara", '~> 2.13'
gem "selenium-webdriver", "~> 2.53.4"
end
diff --git a/app/controllers/account_controller.rb b/app/controllers/account_controller.rb
index 81d6a581a..5070295d2 100644
--- a/app/controllers/account_controller.rb
+++ b/app/controllers/account_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -19,8 +19,10 @@ class AccountController < ApplicationController
helper :custom_fields
include CustomFieldsHelper
+ self.main_menu = false
+
# prevents login action to be filtered by check_if_login_required application scope filter
- skip_before_filter :check_if_login_required, :check_password_change
+ skip_before_action :check_if_login_required, :check_password_change
# Overrides ApplicationController#verify_authenticity_token to disable
# token verification on openid callbacks
@@ -40,7 +42,7 @@ class AccountController < ApplicationController
end
end
rescue AuthSourceException => e
- logger.error "An error occured when authenticating #{params[:username]}: #{e.message}"
+ logger.error "An error occurred when authenticating #{params[:username]}: #{e.message}"
render_error :message => e.message
end
@@ -78,20 +80,25 @@ class AccountController < ApplicationController
return
end
if request.post?
- @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
- if @user.save
- @token.destroy
- Mailer.password_updated(@user, { remote_ip: request.remote_ip })
- flash[:notice] = l(:notice_account_password_updated)
- redirect_to signin_path
- return
+ if @user.must_change_passwd? && @user.check_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
+ @token.destroy
+ Mailer.password_updated(@user, { remote_ip: request.remote_ip })
+ flash[:notice] = l(:notice_account_password_updated)
+ redirect_to signin_path
+ return
+ end
end
end
render :template => "account/password_recovery"
return
else
if request.post?
- email = params[:mail].to_s
+ email = params[:mail].to_s.strip
user = User.find_by_mail(email)
# user not found
unless user
@@ -131,7 +138,7 @@ class AccountController < ApplicationController
user_params = params[:user] || {}
@user = User.new
@user.safe_attributes = user_params
- @user.pref.attributes = params[:pref] if params[:pref]
+ @user.pref.safe_attributes = params[:pref]
@user.admin = false
@user.register
if session[:auth_source_registration]
@@ -145,7 +152,6 @@ class AccountController < ApplicationController
redirect_to my_account_path
end
else
- @user.login = params[:user][:login]
unless user_params[:identity_url].present? && user_params[:password].blank? && user_params[:password_confirmation].blank?
@user.password, @user.password_confirmation = user_params[:password], user_params[:password_confirmation]
end
@@ -274,13 +280,13 @@ class AccountController < ApplicationController
end
def set_autologin_cookie(user)
- token = Token.create(:user => user, :action => 'autologin')
+ token = user.generate_autologin_token
secure = Redmine::Configuration['autologin_cookie_secure']
if secure.nil?
secure = request.ssl?
end
cookie_options = {
- :value => token.value,
+ :value => token,
:expires => 1.year.from_now,
:path => (Redmine::Configuration['autologin_cookie_path'] || RedmineApp::Application.config.relative_url_root || '/'),
:secure => secure,
diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb
index 32df00f97..f82f0110a 100644
--- a/app/controllers/activities_controller.rb
+++ b/app/controllers/activities_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,7 +17,7 @@
class ActivitiesController < ApplicationController
menu_item :activity
- before_filter :find_optional_project
+ before_action :find_optional_project
accept_rss_auth :index
def index
diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb
index 944e60ca3..dfc73c5ab 100644
--- a/app/controllers/admin_controller.rb
+++ b/app/controllers/admin_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,13 +17,12 @@
class AdminController < ApplicationController
layout 'admin'
+ self.main_menu = false
menu_item :projects, :only => :projects
menu_item :plugins, :only => :plugins
menu_item :info, :only => :info
- before_filter :require_admin
- helper :sort
- include SortHelper
+ before_action :require_admin
def index
@no_configuration_data = Redmine::DefaultData::Loader::no_data?
@@ -34,7 +33,10 @@ class AdminController < ApplicationController
scope = Project.status(@status).sorted
scope = scope.like(params[:name]) if params[:name].present?
- @projects = scope.to_a
+
+ @project_count = scope.count
+ @project_pages = Paginator.new @project_count, per_page_option, params['page']
+ @projects = scope.limit(@project_pages.per_page).offset(@project_pages.offset).to_a
render :action => "projects", :layout => false if request.xhr?
end
@@ -72,7 +74,6 @@ class AdminController < ApplicationController
end
def info
- @db_adapter_name = ActiveRecord::Base.connection.adapter_name
@checklist = [
[:text_default_administrator_account_changed, User.default_admin_account_changed?],
[:text_file_repository_writable, File.writable?(Attachment.storage_path)],
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 12ea3133e..1d42901f0 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -51,7 +51,7 @@ class ApplicationController < ActionController::Base
end
end
- before_filter :session_expiration, :user_setup, :check_if_login_required, :set_localization, :check_password_change
+ before_action :session_expiration, :user_setup, :check_if_login_required, :set_localization, :check_password_change
rescue_from ::Unauthorized, :with => :deny_access
rescue_from ::ActionView::MissingTemplate, :with => :missing_template
@@ -168,9 +168,10 @@ class ApplicationController < ActionController::Base
# Logs out current user
def logout_user
if User.current.logged?
- cookies.delete(autologin_cookie_name)
- Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin'])
- Token.delete_all(["user_id = ? AND action = ? AND value = ?", User.current.id, 'session', session[:tk]])
+ if autologin = cookies.delete(autologin_cookie_name)
+ User.current.delete_autologin_token(autologin)
+ end
+ User.current.delete_session_token(session[:tk])
self.logged_user = nil
end
end
@@ -213,7 +214,7 @@ class ApplicationController < ActionController::Base
if !User.current.logged?
# Extract only the basic url parameters on non-GET requests
if request.get?
- url = url_for(params)
+ url = request.original_url
else
url = url_for(:controller => params[:controller], :action => params[:action], :id => params[:id], :project_id => params[:project_id])
end
@@ -369,7 +370,7 @@ class ApplicationController < ActionController::Base
end
# make sure that the user is a member of the project (or admin) if project is private
- # used as a before_filter for actions that do not require any particular permission on the project
+ # used as a before_action for actions that do not require any particular permission on the project
def check_project_privacy
if @project && !@project.archived?
if @project.visible?
@@ -434,7 +435,7 @@ class ApplicationController < ActionController::Base
return false
end
- if path.match(%r{/(login|account/register)})
+ if path.match(%r{/(login|account/register|account/lost_password)})
return false
end
@@ -453,14 +454,16 @@ class ApplicationController < ActionController::Base
# Redirects to the request referer if present, redirects to args or call block otherwise.
def redirect_to_referer_or(*args, &block)
- redirect_to :back
- rescue ::ActionController::RedirectBackError
- if args.any?
- redirect_to *args
- elsif block_given?
- block.call
+ if referer = request.headers["Referer"]
+ redirect_to referer
else
- raise "#redirect_to_referer_or takes arguments or a block"
+ if args.any?
+ redirect_to *args
+ elsif block_given?
+ block.call
+ else
+ raise "#redirect_to_referer_or takes arguments or a block"
+ end
end
end
@@ -642,20 +645,18 @@ class ApplicationController < ActionController::Base
# Rescues an invalid query statement. Just in case...
def query_statement_invalid(exception)
logger.error "Query::StatementInvalid: #{exception.message}" if logger
- session.delete(:query)
- sort_clear if respond_to?(:sort_clear)
+ session.delete(:issue_query)
render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
end
- # Renders a 200 response for successfull updates or deletions via the API
+ # Renders a 200 response for successful updates or deletions via the API
def render_api_ok
render_api_head :ok
end
# Renders a head API response
def render_api_head(status)
- # #head would return a response body with one space
- render :text => '', :status => status, :layout => nil
+ head status
end
# Renders API response on validation failure
diff --git a/app/controllers/attachments_controller.rb b/app/controllers/attachments_controller.rb
index a9bc5793a..1b18662ad 100644
--- a/app/controllers/attachments_controller.rb
+++ b/app/controllers/attachments_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,17 +16,18 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class AttachmentsController < ApplicationController
- before_filter :find_attachment, :only => [:show, :download, :thumbnail, :destroy]
- before_filter :find_editable_attachments, :only => [:edit, :update]
- before_filter :file_readable, :read_authorize, :only => [:show, :download, :thumbnail]
- before_filter :delete_authorize, :only => :destroy
- before_filter :authorize_global, :only => :upload
+ before_action :find_attachment, :only => [:show, :download, :thumbnail, :update, :destroy]
+ before_action :find_editable_attachments, :only => [:edit_all, :update_all]
+ before_action :file_readable, :read_authorize, :only => [:show, :download, :thumbnail]
+ before_action :update_authorize, :only => :update
+ before_action :delete_authorize, :only => :destroy
+ before_action :authorize_global, :only => :upload
# Disable check for same origin requests for JS files, i.e. attachments with
# MIME type text/javascript.
- skip_after_filter :verify_same_origin_request, :only => :download
+ skip_after_action :verify_same_origin_request, :only => :download
- accept_api_auth :show, :download, :thumbnail, :upload, :destroy
+ accept_api_auth :show, :download, :thumbnail, :upload, :update, :destroy
def show
respond_to do |format|
@@ -77,7 +78,7 @@ class AttachmentsController < ApplicationController
end
else
# No thumbnail for the attachment or thumbnail could not be created
- render :nothing => true, :status => 404
+ head 404
end
end
@@ -85,7 +86,7 @@ class AttachmentsController < ApplicationController
# Make sure that API users get used to set this content type
# as it won't trigger Rails' automatic parsing of the request body for parameters
unless request.content_type == 'application/octet-stream'
- render :nothing => true, :status => 406
+ head 406
return
end
@@ -107,17 +108,32 @@ class AttachmentsController < ApplicationController
end
end
- def edit
+ # Edit all the attachments of a container
+ def edit_all
+ end
+
+ # Update all the attachments of a container
+ def update_all
+ if Attachment.update_attachments(@attachments, update_all_params)
+ redirect_back_or_default home_path
+ return
+ end
+ render :action => 'edit_all'
end
def update
- if params[:attachments].is_a?(Hash)
- if Attachment.update_attachments(@attachments, params[:attachments])
- redirect_back_or_default home_path
- return
- end
+ @attachment.safe_attributes = params[:attachment]
+ saved = @attachment.save
+
+ respond_to do |format|
+ format.api {
+ if saved
+ render_api_ok
+ else
+ render_validation_errors(@attachment)
+ end
+ }
end
- render :action => 'edit'
end
def destroy
@@ -138,6 +154,22 @@ class AttachmentsController < ApplicationController
end
end
+ # Returns the menu item that should be selected when viewing an attachment
+ def current_menu_item
+ if @attachment
+ case @attachment.container
+ when WikiPage
+ :wiki
+ when Message
+ :boards
+ when Project, Version
+ :files
+ else
+ @attachment.container.class.name.pluralize.downcase.to_sym
+ end
+ end
+ end
+
private
def find_attachment
@@ -184,6 +216,10 @@ class AttachmentsController < ApplicationController
@attachment.visible? ? true : deny_access
end
+ def update_authorize
+ @attachment.editable? ? true : deny_access
+ end
+
def delete_authorize
@attachment.deletable? ? true : deny_access
end
@@ -203,4 +239,9 @@ class AttachmentsController < ApplicationController
'attachment'
end
end
+
+ # Returns attachments param for #update_all
+ def update_all_params
+ params.permit(:attachments => [:filename, :description]).require(:attachments)
+ end
end
diff --git a/app/controllers/auth_sources_controller.rb b/app/controllers/auth_sources_controller.rb
index 38bebc439..dcbba3687 100644
--- a/app/controllers/auth_sources_controller.rb
+++ b/app/controllers/auth_sources_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,10 +17,12 @@
class AuthSourcesController < ApplicationController
layout 'admin'
+ self.main_menu = false
menu_item :ldap_authentication
- before_filter :require_admin
- before_filter :find_auth_source, :only => [:edit, :update, :test_connection, :destroy]
+ before_action :require_admin
+ before_action :build_new_auth_source, :only => [:new, :create]
+ before_action :find_auth_source, :only => [:edit, :update, :test_connection, :destroy]
require_sudo_mode :update, :destroy
def index
@@ -28,13 +30,9 @@ class AuthSourcesController < ApplicationController
end
def new
- klass_name = params[:type] || 'AuthSourceLdap'
- @auth_source = AuthSource.new_subclass_instance(klass_name, params[:auth_source])
- render_404 unless @auth_source
end
def create
- @auth_source = AuthSource.new_subclass_instance(params[:type], params[:auth_source])
if @auth_source.save
flash[:notice] = l(:notice_successful_create)
redirect_to auth_sources_path
@@ -47,7 +45,8 @@ class AuthSourcesController < ApplicationController
end
def update
- if @auth_source.update_attributes(params[:auth_source])
+ @auth_source.safe_attributes = params[:auth_source]
+ if @auth_source.save
flash[:notice] = l(:notice_successful_update)
redirect_to auth_sources_path
else
@@ -89,6 +88,15 @@ class AuthSourcesController < ApplicationController
private
+ def build_new_auth_source
+ @auth_source = AuthSource.new_subclass_instance(params[:type] || 'AuthSourceLdap')
+ if @auth_source
+ @auth_source.safe_attributes = params[:auth_source]
+ else
+ render_404
+ end
+ end
+
def find_auth_source
@auth_source = AuthSource.find(params[:id])
rescue ActiveRecord::RecordNotFound
diff --git a/app/controllers/auto_completes_controller.rb b/app/controllers/auto_completes_controller.rb
index 92c0641d6..293ac201e 100644
--- a/app/controllers/auto_completes_controller.rb
+++ b/app/controllers/auto_completes_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,17 +16,26 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class AutoCompletesController < ApplicationController
- before_filter :find_project
+ before_action :find_project
def issues
@issues = []
q = (params[:q] || params[:term]).to_s.strip
+ status = params[:status].to_s
+ issue_id = params[:issue_id].to_s
if q.present?
scope = Issue.cross_project_scope(@project, params[:scope]).visible
+ if status.present?
+ scope = scope.open(status == 'o')
+ end
+ if issue_id.present?
+ scope = scope.where.not(:id => issue_id.to_i)
+ end
if q.match(/\A#?(\d+)\z/)
@issues << scope.find_by_id($1.to_i)
end
- @issues += scope.where("LOWER(#{Issue.table_name}.subject) LIKE LOWER(?)", "%#{q}%").order("#{Issue.table_name}.id DESC").limit(10).to_a
+
+ @issues += scope.like(q).order(:id => :desc).limit(10).to_a
@issues.compact!
end
render :layout => false
diff --git a/app/controllers/boards_controller.rb b/app/controllers/boards_controller.rb
index 9ab80f4f9..03ca50c35 100644
--- a/app/controllers/boards_controller.rb
+++ b/app/controllers/boards_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,7 +17,7 @@
class BoardsController < ApplicationController
default_search_scope :messages
- before_filter :find_project_by_project_id, :find_board_if_available, :authorize
+ before_action :find_project_by_project_id, :find_board_if_available, :authorize
accept_rss_auth :index, :show
helper :sort
@@ -25,7 +25,7 @@ class BoardsController < ApplicationController
helper :watchers
def index
- @boards = @project.boards.preload(:project, :last_message => :author).to_a
+ @boards = @project.boards.preload(:last_message => :author).to_a
# show the board if there is only one
if @boards.size == 1
@board = @boards.first
@@ -37,15 +37,14 @@ class BoardsController < ApplicationController
respond_to do |format|
format.html {
sort_init 'updated_on', 'desc'
- sort_update 'created_on' => "#{Message.table_name}.created_on",
+ sort_update 'created_on' => "#{Message.table_name}.id",
'replies' => "#{Message.table_name}.replies_count",
- 'updated_on' => "COALESCE(last_replies_messages.created_on, #{Message.table_name}.created_on)"
+ 'updated_on' => "COALESCE(#{Message.table_name}.last_reply_id, #{Message.table_name}.id)"
@topic_count = @board.topics.count
@topic_pages = Paginator.new @topic_count, per_page_option, params['page']
@topics = @board.topics.
- reorder("#{Message.table_name}.sticky DESC").
- joins("LEFT OUTER JOIN #{Message.table_name} last_replies_messages ON last_replies_messages.id = #{Message.table_name}.last_reply_id").
+ reorder(:sticky => :desc).
limit(@topic_pages.per_page).
offset(@topic_pages.offset).
order(sort_clause).
@@ -56,7 +55,7 @@ class BoardsController < ApplicationController
}
format.atom {
@messages = @board.messages.
- reorder('created_on DESC').
+ reorder(:id => :desc).
includes(:author, :board).
limit(Setting.feeds_limit.to_i).
to_a
@@ -92,18 +91,20 @@ class BoardsController < ApplicationController
flash[:notice] = l(:notice_successful_update)
redirect_to_settings_in_projects
}
- format.js { render :nothing => true }
+ format.js { head 200 }
end
else
respond_to do |format|
format.html { render :action => 'edit' }
- format.js { render :nothing => true, :status => 422 }
+ format.js { head 422 }
end
end
end
def destroy
- @board.destroy
+ if @board.destroy
+ flash[:notice] = l(:notice_successful_delete)
+ end
redirect_to_settings_in_projects
end
diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb
index 276d7988d..e07ba2e61 100644
--- a/app/controllers/calendars_controller.rb
+++ b/app/controllers/calendars_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,7 +17,7 @@
class CalendarsController < ApplicationController
menu_item :calendar
- before_filter :find_optional_project
+ before_action :find_optional_project
rescue_from Query::StatementInvalid, :with => :query_statement_invalid
@@ -25,8 +25,6 @@ class CalendarsController < ApplicationController
helper :projects
helper :queries
include QueriesHelper
- helper :sort
- include SortHelper
def show
if params[:year] and params[:year].to_i > 1900
@@ -41,6 +39,7 @@ class CalendarsController < ApplicationController
@calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
retrieve_query
@query.group_by = nil
+ @query.sort_criteria = nil
if @query.valid?
events = []
events += @query.issues(:include => [:tracker, :assigned_to, :priority],
diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb
index 5074ef631..e7974d49e 100644
--- a/app/controllers/comments_controller.rb
+++ b/app/controllers/comments_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -18,9 +18,9 @@
class CommentsController < ApplicationController
default_search_scope :news
model_object News
- before_filter :find_model_object
- before_filter :find_project_from_association
- before_filter :authorize
+ before_action :find_model_object
+ before_action :find_project_from_association
+ before_action :authorize
def create
raise Unauthorized unless @news.commentable?
@@ -43,7 +43,7 @@ class CommentsController < ApplicationController
private
# ApplicationController's find_model_object sets it based on the controller
- # name so it needs to be overriden and set to @news instead
+ # name so it needs to be overridden and set to @news instead
def find_model_object
super
@news = @object
diff --git a/app/controllers/context_menus_controller.rb b/app/controllers/context_menus_controller.rb
index 903f13f00..bd6977503 100644
--- a/app/controllers/context_menus_controller.rb
+++ b/app/controllers/context_menus_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -19,7 +19,7 @@ class ContextMenusController < ApplicationController
helper :watchers
helper :issues
- before_filter :find_issues, :only => :issues
+ before_action :find_issues, :only => :issues
def issues
if (@issues.size == 1)
@@ -45,7 +45,7 @@ class ContextMenusController < ApplicationController
@options_by_custom_field = {}
if @can[:edit]
- custom_fields = @issues.map(&:editable_custom_fields).reduce(:&).reject(&:multiple?)
+ custom_fields = @issues.map(&:editable_custom_fields).reduce(:&).reject(&:multiple?).select {|field| field.format.bulk_edit_supported}
custom_fields.each do |field|
values = field.possible_values_options(@projects)
if values.present?
@@ -78,7 +78,7 @@ class ContextMenusController < ApplicationController
@options_by_custom_field = {}
if @can[:edit]
- custom_fields = @time_entries.map(&:editable_custom_fields).reduce(:&).reject(&:multiple?)
+ custom_fields = @time_entries.map(&:editable_custom_fields).reduce(:&).reject(&:multiple?).select {|field| field.format.bulk_edit_supported}
custom_fields.each do |field|
values = field.possible_values_options(@projects)
if values.present?
diff --git a/app/controllers/custom_field_enumerations_controller.rb b/app/controllers/custom_field_enumerations_controller.rb
index f99f770f4..fc186b98d 100644
--- a/app/controllers/custom_field_enumerations_controller.rb
+++ b/app/controllers/custom_field_enumerations_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,10 +17,11 @@
class CustomFieldEnumerationsController < ApplicationController
layout 'admin'
+ self.main_menu = false
- before_filter :require_admin
- before_filter :find_custom_field
- before_filter :find_enumeration, :only => :destroy
+ before_action :require_admin
+ before_action :find_custom_field
+ before_action :find_enumeration, :only => :destroy
helper :custom_fields
@@ -29,7 +30,8 @@ class CustomFieldEnumerationsController < ApplicationController
end
def create
- @value = @custom_field.enumerations.build(params[:custom_field_enumeration])
+ @value = @custom_field.enumerations.build
+ @value.attributes = enumeration_params
@value.save
respond_to do |format|
format.html { redirect_to custom_field_enumerations_path(@custom_field) }
@@ -38,7 +40,8 @@ class CustomFieldEnumerationsController < ApplicationController
end
def update_each
- if CustomFieldEnumeration.update_each(@custom_field, params[:custom_field_enumerations])
+ saved = CustomFieldEnumeration.update_each(@custom_field, update_each_params)
+ if saved
flash[:notice] = l(:notice_successful_update)
end
redirect_to :action => 'index'
@@ -68,4 +71,14 @@ class CustomFieldEnumerationsController < ApplicationController
rescue ActiveRecord::RecordNotFound
render_404
end
+
+ def enumeration_params
+ params.require(:custom_field_enumeration).permit(:name, :active, :position)
+ end
+
+ def update_each_params
+ # params.require(:custom_field_enumerations).permit(:name, :active, :position) does not work here with param like this:
+ # "custom_field_enumerations":{"0":{"name": ...}, "1":{"name...}}
+ params.permit(:custom_field_enumerations => [:name, :active, :position]).require(:custom_field_enumerations)
+ end
end
diff --git a/app/controllers/custom_fields_controller.rb b/app/controllers/custom_fields_controller.rb
index ab0d5019f..41d4a7301 100644
--- a/app/controllers/custom_fields_controller.rb
+++ b/app/controllers/custom_fields_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,16 +17,19 @@
class CustomFieldsController < ApplicationController
layout 'admin'
+ self.main_menu = false
- before_filter :require_admin
- before_filter :build_new_custom_field, :only => [:new, :create]
- before_filter :find_custom_field, :only => [:edit, :update, :destroy]
+ before_action :require_admin
+ before_action :build_new_custom_field, :only => [:new, :create]
+ before_action :find_custom_field, :only => [:edit, :update, :destroy]
accept_api_auth :index
def index
respond_to do |format|
format.html {
@custom_fields_by_type = CustomField.all.group_by {|f| f.class.name }
+ @custom_fields_projects_count =
+ IssueCustomField.where(is_for_all: false).joins(:projects).group(:custom_field_id).count
}
format.api {
@custom_fields = CustomField.all
@@ -53,26 +56,29 @@ class CustomFieldsController < ApplicationController
end
def update
- if @custom_field.update_attributes(params[:custom_field])
+ @custom_field.safe_attributes = params[:custom_field]
+ if @custom_field.save
call_hook(:controller_custom_fields_edit_after_save, :params => params, :custom_field => @custom_field)
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default edit_custom_field_path(@custom_field)
}
- format.js { render :nothing => true }
+ format.js { head 200 }
end
else
respond_to do |format|
format.html { render :action => 'edit' }
- format.js { render :nothing => true, :status => 422 }
+ format.js { head 422 }
end
end
end
def destroy
begin
- @custom_field.destroy
+ if @custom_field.destroy
+ flash[:notice] = l(:notice_successful_delete)
+ end
rescue
flash[:error] = l(:error_can_not_delete_custom_field)
end
@@ -82,9 +88,11 @@ class CustomFieldsController < ApplicationController
private
def build_new_custom_field
- @custom_field = CustomField.new_subclass_instance(params[:type], params[:custom_field])
+ @custom_field = CustomField.new_subclass_instance(params[:type])
if @custom_field.nil?
render :action => 'select_type'
+ else
+ @custom_field.safe_attributes = params[:custom_field]
end
end
diff --git a/app/controllers/documents_controller.rb b/app/controllers/documents_controller.rb
index 40db8af1e..dbf0e88dc 100644
--- a/app/controllers/documents_controller.rb
+++ b/app/controllers/documents_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -18,10 +18,10 @@
class DocumentsController < ApplicationController
default_search_scope :documents
model_object Document
- before_filter :find_project_by_project_id, :only => [:index, :new, :create]
- before_filter :find_model_object, :except => [:index, :new, :create]
- before_filter :find_project_from_association, :except => [:index, :new, :create]
- before_filter :authorize
+ before_action :find_project_by_project_id, :only => [:index, :new, :create]
+ before_action :find_model_object, :except => [:index, :new, :create]
+ before_action :find_project_from_association, :except => [:index, :new, :create]
+ before_action :authorize
helper :attachments
helper :custom_fields
diff --git a/app/controllers/email_addresses_controller.rb b/app/controllers/email_addresses_controller.rb
index dfc80ad95..22026254a 100644
--- a/app/controllers/email_addresses_controller.rb
+++ b/app/controllers/email_addresses_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,8 +16,9 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class EmailAddressesController < ApplicationController
- before_filter :find_user, :require_admin_or_current_user
- before_filter :find_email_address, :only => [:update, :destroy]
+ self.main_menu = false
+ before_action :find_user, :require_admin_or_current_user
+ before_action :find_email_address, :only => [:update, :destroy]
require_sudo_mode :create, :update, :destroy
def index
@@ -29,10 +30,7 @@ class EmailAddressesController < ApplicationController
saved = false
if @user.email_addresses.count <= Setting.max_additional_emails.to_i
@address = EmailAddress.new(:user => @user, :is_default => false)
- attrs = params[:email_address]
- if attrs.is_a?(Hash)
- @address.address = attrs[:address].to_s
- end
+ @address.safe_attributes = params[:email_address]
saved = @address.save
end
diff --git a/app/controllers/enumerations_controller.rb b/app/controllers/enumerations_controller.rb
index feb360398..a04d7b184 100644
--- a/app/controllers/enumerations_controller.rb
+++ b/app/controllers/enumerations_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,11 +17,12 @@
class EnumerationsController < ApplicationController
layout 'admin'
+ self.main_menu = false
- before_filter :require_admin, :except => :index
- before_filter :require_admin_or_api_request, :only => :index
- before_filter :build_new_enumeration, :only => [:new, :create]
- before_filter :find_enumeration, :only => [:edit, :update, :destroy]
+ before_action :require_admin, :except => :index
+ before_action :require_admin_or_api_request, :only => :index
+ before_action :build_new_enumeration, :only => [:new, :create]
+ before_action :find_enumeration, :only => [:edit, :update, :destroy]
accept_api_auth :index
helper :custom_fields
@@ -56,18 +57,18 @@ class EnumerationsController < ApplicationController
end
def update
- if @enumeration.update_attributes(params[:enumeration])
+ if @enumeration.update_attributes(enumeration_params)
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_to enumerations_path
}
- format.js { render :nothing => true }
+ format.js { head 200 }
end
else
respond_to do |format|
format.html { render :action => 'edit' }
- format.js { render :nothing => true, :status => 422 }
+ format.js { head 422 }
end
end
end
@@ -90,8 +91,10 @@ class EnumerationsController < ApplicationController
def build_new_enumeration
class_name = params[:enumeration] && params[:enumeration][:type] || params[:type]
- @enumeration = Enumeration.new_subclass_instance(class_name, params[:enumeration])
- if @enumeration.nil?
+ @enumeration = Enumeration.new_subclass_instance(class_name)
+ if @enumeration
+ @enumeration.attributes = enumeration_params || {}
+ else
render_404
end
end
@@ -101,4 +104,10 @@ class EnumerationsController < ApplicationController
rescue ActiveRecord::RecordNotFound
render_404
end
+
+ def enumeration_params
+ # can't require enumeration on #new action
+ cf_ids = @enumeration.available_custom_fields.map{|c| c.id.to_s}
+ params.permit(:enumeration => [:name, :active, :is_default, :position, :custom_field_values => cf_ids])[:enumeration]
+ end
end
diff --git a/app/controllers/files_controller.rb b/app/controllers/files_controller.rb
index db061b858..7c596657c 100644
--- a/app/controllers/files_controller.rb
+++ b/app/controllers/files_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -18,9 +18,11 @@
class FilesController < ApplicationController
menu_item :files
- before_filter :find_project_by_project_id
- before_filter :authorize
+ before_action :find_project_by_project_id
+ before_action :authorize
+ accept_api_auth :index, :create
+ helper :attachments
helper :sort
include SortHelper
@@ -35,7 +37,10 @@ class FilesController < ApplicationController
references(:attachments).reorder(sort_clause).find(@project.id)]
@containers += @project.versions.includes(:attachments).
references(:attachments).reorder(sort_clause).to_a.sort.reverse
- render :layout => !request.xhr?
+ respond_to do |format|
+ format.html { render :layout => !request.xhr? }
+ format.api
+ end
end
def new
@@ -43,20 +48,29 @@ class FilesController < ApplicationController
end
def create
- container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id]))
- attachments = Attachment.attach_files(container, params[:attachments])
+ version_id = params[:version_id] || (params[:file] && params[:file][:version_id])
+ container = version_id.blank? ? @project : @project.versions.find_by_id(version_id)
+ attachments = Attachment.attach_files(container, (params[:attachments] || (params[:file] && params[:file][:token] && params)))
render_attachment_warning_if_needed(container)
if attachments[:files].present?
if Setting.notified_events.include?('file_added')
Mailer.attachments_added(attachments[:files]).deliver
end
- flash[:notice] = l(:label_file_added)
- redirect_to project_files_path(@project)
+ respond_to do |format|
+ format.html {
+ flash[:notice] = l(:label_file_added)
+ redirect_to project_files_path(@project) }
+ format.api { render_api_ok }
+ end
else
- flash.now[:error] = l(:label_attachment) + " " + l('activerecord.errors.messages.invalid')
- new
- render :action => 'new'
+ respond_to do |format|
+ format.html {
+ flash.now[:error] = l(:label_attachment) + " " + l('activerecord.errors.messages.invalid')
+ new
+ render :action => 'new' }
+ format.api { render :status => :bad_request }
+ end
end
end
end
diff --git a/app/controllers/gantts_controller.rb b/app/controllers/gantts_controller.rb
index e37d780dd..3283282d5 100644
--- a/app/controllers/gantts_controller.rb
+++ b/app/controllers/gantts_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,7 +17,7 @@
class GanttsController < ApplicationController
menu_item :gantt
- before_filter :find_optional_project
+ before_action :find_optional_project
rescue_from Query::StatementInvalid, :with => :query_statement_invalid
@@ -26,8 +26,6 @@ class GanttsController < ApplicationController
helper :projects
helper :queries
include QueriesHelper
- helper :sort
- include SortHelper
include Redmine::Export::PDF
def show
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index b99e374ff..4379ee39b 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,9 +17,10 @@
class GroupsController < ApplicationController
layout 'admin'
+ self.main_menu = false
- before_filter :require_admin
- before_filter :find_group, :except => [:index, :new, :create]
+ before_action :require_admin
+ before_action :find_group, :except => [:index, :new, :create]
accept_api_auth :index, :show, :create, :update, :destroy, :add_users, :remove_user
require_sudo_mode :add_users, :remove_user, :create, :update, :destroy, :edit_membership, :destroy_membership
@@ -30,7 +31,12 @@ class GroupsController < ApplicationController
def index
respond_to do |format|
format.html {
- @groups = Group.sorted.to_a
+ scope = Group.sorted
+ scope = scope.like(params[:name]) if params[:name].present?
+
+ @group_count = scope.count
+ @group_pages = Paginator.new @group_count, per_page_option, params['page']
+ @groups = scope.limit(@group_pages.per_page).offset(@group_pages.offset).to_a
@user_count_by_group_id = user_count_by_group_id
}
format.api {
@@ -79,7 +85,7 @@ class GroupsController < ApplicationController
respond_to do |format|
if @group.save
flash[:notice] = l(:notice_successful_update)
- format.html { redirect_to(groups_path) }
+ format.html { redirect_to_referer_or(groups_path) }
format.api { render_api_ok }
else
format.html { render :action => "edit" }
@@ -92,7 +98,7 @@ class GroupsController < ApplicationController
@group.destroy
respond_to do |format|
- format.html { redirect_to(groups_path) }
+ format.html { redirect_to_referer_or(groups_path) }
format.api { render_api_ok }
end
end
diff --git a/app/controllers/imports_controller.rb b/app/controllers/imports_controller.rb
index 079bce347..96589ac65 100644
--- a/app/controllers/imports_controller.rb
+++ b/app/controllers/imports_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -18,11 +18,13 @@
require 'csv'
class ImportsController < ApplicationController
+ menu_item :issues
- before_filter :find_import, :only => [:show, :settings, :mapping, :run]
- before_filter :authorize_global
+ before_action :find_import, :only => [:show, :settings, :mapping, :run]
+ before_action :authorize_global
helper :issues
+ helper :queries
def new
end
diff --git a/app/controllers/issue_categories_controller.rb b/app/controllers/issue_categories_controller.rb
index c53f2395d..d635a28f3 100644
--- a/app/controllers/issue_categories_controller.rb
+++ b/app/controllers/issue_categories_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -18,10 +18,10 @@
class IssueCategoriesController < ApplicationController
menu_item :settings
model_object IssueCategory
- before_filter :find_model_object, :except => [:index, :new, :create]
- before_filter :find_project_from_association, :except => [:index, :new, :create]
- before_filter :find_project_by_project_id, :only => [:index, :new, :create]
- before_filter :authorize
+ before_action :find_model_object, :except => [:index, :new, :create]
+ before_action :find_project_from_association, :except => [:index, :new, :create]
+ before_action :find_project_by_project_id, :only => [:index, :new, :create]
+ before_action :authorize
accept_api_auth :index, :show, :create, :update, :destroy
def index
diff --git a/app/controllers/issue_relations_controller.rb b/app/controllers/issue_relations_controller.rb
index 1258928c8..0bcc8c5e3 100644
--- a/app/controllers/issue_relations_controller.rb
+++ b/app/controllers/issue_relations_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,8 +16,10 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class IssueRelationsController < ApplicationController
- before_filter :find_issue, :authorize, :only => [:index, :create]
- before_filter :find_relation, :only => [:show, :destroy]
+ helper :issues
+
+ before_action :find_issue, :authorize, :only => [:index, :create]
+ before_action :find_relation, :only => [:show, :destroy]
accept_api_auth :index, :show, :create, :destroy
@@ -25,7 +27,7 @@ class IssueRelationsController < ApplicationController
@relations = @issue.relations
respond_to do |format|
- format.html { render :nothing => true }
+ format.html { head 200 }
format.api
end
end
@@ -34,7 +36,7 @@ class IssueRelationsController < ApplicationController
raise Unauthorized unless @relation.visible?
respond_to do |format|
- format.html { render :nothing => true }
+ format.html { head 200 }
format.api
end
end
diff --git a/app/controllers/issue_statuses_controller.rb b/app/controllers/issue_statuses_controller.rb
index e49878f1d..92c033783 100644
--- a/app/controllers/issue_statuses_controller.rb
+++ b/app/controllers/issue_statuses_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,9 +17,10 @@
class IssueStatusesController < ApplicationController
layout 'admin'
+ self.main_menu = false
- before_filter :require_admin, :except => :index
- before_filter :require_admin_or_api_request, :only => :index
+ before_action :require_admin, :except => :index
+ before_action :require_admin_or_api_request, :only => :index
accept_api_auth :index
def index
@@ -35,7 +36,8 @@ class IssueStatusesController < ApplicationController
end
def create
- @issue_status = IssueStatus.new(params[:issue_status])
+ @issue_status = IssueStatus.new
+ @issue_status.safe_attributes = params[:issue_status]
if @issue_status.save
flash[:notice] = l(:notice_successful_create)
redirect_to issue_statuses_path
@@ -50,18 +52,19 @@ class IssueStatusesController < ApplicationController
def update
@issue_status = IssueStatus.find(params[:id])
- if @issue_status.update_attributes(params[:issue_status])
+ @issue_status.safe_attributes = params[:issue_status]
+ if @issue_status.save
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_to issue_statuses_path(:page => params[:page])
}
- format.js { render :nothing => true }
+ format.js { head 200 }
end
else
respond_to do |format|
format.html { render :action => 'edit' }
- format.js { render :nothing => true, :status => 422 }
+ format.js { head 422 }
end
end
end
diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb
index 91c196b58..69a947b03 100644
--- a/app/controllers/issues_controller.rb
+++ b/app/controllers/issues_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -18,11 +18,11 @@
class IssuesController < ApplicationController
default_search_scope :issues
- before_filter :find_issue, :only => [:show, :edit, :update]
- before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :destroy]
- before_filter :authorize, :except => [:index, :new, :create]
- before_filter :find_optional_project, :only => [:index, :new, :create]
- before_filter :build_new_issue_from_params, :only => [:new, :create]
+ before_action :find_issue, :only => [:show, :edit, :update]
+ before_action :find_issues, :only => [:bulk_edit, :bulk_update, :destroy]
+ before_action :authorize, :except => [:index, :new, :create]
+ before_action :find_optional_project, :only => [:index, :new, :create]
+ before_action :build_new_issue_from_params, :only => [:new, :create]
accept_rss_auth :index, :show
accept_api_auth :index, :show, :create, :update, :destroy
@@ -37,54 +37,44 @@ class IssuesController < ApplicationController
helper :queries
include QueriesHelper
helper :repositories
- helper :sort
- include SortHelper
helper :timelog
def index
- retrieve_query
- sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
- sort_update(@query.sortable_columns)
- @query.sort_criteria = sort_criteria.to_a
+ use_session = !request.format.csv?
+ retrieve_query(IssueQuery, use_session)
if @query.valid?
- case params[:format]
- when 'csv', 'pdf'
- @limit = Setting.issues_export_limit.to_i
- if params[:columns] == 'all'
- @query.column_names = @query.available_inline_columns.map(&:name)
- end
- when 'atom'
- @limit = Setting.feeds_limit.to_i
- when 'xml', 'json'
- @offset, @limit = api_offset_and_limit
- @query.column_names = %w(author)
- else
- @limit = per_page_option
- end
-
- @issue_count = @query.issue_count
- @issue_pages = Paginator.new @issue_count, @limit, params['page']
- @offset ||= @issue_pages.offset
- @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
- :order => sort_clause,
- :offset => @offset,
- :limit => @limit)
- @issue_count_by_group = @query.issue_count_by_group
-
respond_to do |format|
- format.html { render :template => 'issues/index', :layout => !request.xhr? }
+ format.html {
+ @issue_count = @query.issue_count
+ @issue_pages = Paginator.new @issue_count, per_page_option, params['page']
+ @issues = @query.issues(:offset => @issue_pages.offset, :limit => @issue_pages.per_page)
+ render :layout => !request.xhr?
+ }
format.api {
+ @offset, @limit = api_offset_and_limit
+ @query.column_names = %w(author)
+ @issue_count = @query.issue_count
+ @issues = @query.issues(:offset => @offset, :limit => @limit)
Issue.load_visible_relations(@issues) if include_in_api_response?('relations')
}
- format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
- format.csv { send_data(query_to_csv(@issues, @query, params[:csv]), :type => 'text/csv; header=present', :filename => 'issues.csv') }
- format.pdf { send_file_headers! :type => 'application/pdf', :filename => 'issues.pdf' }
+ format.atom {
+ @issues = @query.issues(:limit => Setting.feeds_limit.to_i)
+ render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}")
+ }
+ format.csv {
+ @issues = @query.issues(:limit => Setting.issues_export_limit.to_i)
+ send_data(query_to_csv(@issues, @query, params[:csv]), :type => 'text/csv; header=present', :filename => 'issues.csv')
+ }
+ format.pdf {
+ @issues = @query.issues(:limit => Setting.issues_export_limit.to_i)
+ send_file_headers! :type => 'application/pdf', :filename => 'issues.pdf'
+ }
end
else
respond_to do |format|
- format.html { render(:template => 'issues/index', :layout => !request.xhr?) }
- format.any(:atom, :csv, :pdf) { render(:nothing => true) }
+ format.html { render :layout => !request.xhr? }
+ format.any(:atom, :csv, :pdf) { head 422 }
format.api { render_validation_errors(@query) }
end
end
@@ -93,23 +83,14 @@ class IssuesController < ApplicationController
end
def show
- @journals = @issue.journals.includes(:user, :details).
- references(:user, :details).
- reorder(:created_on, :id).to_a
- @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)
- @journals.select! {|journal| journal.notes? || journal.visible_details.any?}
- @journals.reverse! if User.current.wants_comments_in_reverse_order?
-
+ @journals = @issue.visible_journals_with_index
@changesets = @issue.changesets.visible.preload(:repository, :user).to_a
- @changesets.reverse! if User.current.wants_comments_in_reverse_order?
-
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
- @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
- @priorities = IssuePriority.active
- @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
- @relation = IssueRelation.new
+
+ if User.current.wants_comments_in_reverse_order?
+ @journals.reverse!
+ @changesets.reverse!
+ end
if User.current.allowed_to?(:view_time_entries, @project)
Issue.load_visible_spent_hours([@issue])
@@ -118,6 +99,10 @@ class IssuesController < ApplicationController
respond_to do |format|
format.html {
+ @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
+ @priorities = IssuePriority.active
+ @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
+ @relation = IssueRelation.new
retrieve_previous_and_next_issue_ids
render :template => 'issues/show'
}
@@ -222,32 +207,69 @@ class IssuesController < ApplicationController
end
end
+ edited_issues = Issue.where(:id => @issues.map(&:id)).to_a
+
+ @values_by_custom_field = {}
+ edited_issues.each do |issue|
+ issue.custom_field_values.each do |c|
+ if c.value_present?
+ @values_by_custom_field[c.custom_field] ||= []
+ @values_by_custom_field[c.custom_field] << issue.id
+ end
+ end
+ end
+
@allowed_projects = Issue.allowed_target_projects
if params[:issue]
@target_project = @allowed_projects.detect {|p| p.id.to_s == params[:issue][:project_id].to_s}
if @target_project
target_projects = [@target_project]
+ edited_issues.each {|issue| issue.project = @target_project}
end
end
target_projects ||= @projects
+ @trackers = target_projects.map {|p| Issue.allowed_target_trackers(p) }.reduce(:&)
+ if params[:issue]
+ @target_tracker = @trackers.detect {|t| t.id.to_s == params[:issue][:tracker_id].to_s}
+ if @target_tracker
+ edited_issues.each {|issue| issue.tracker = @target_tracker}
+ end
+ end
+
if @copy
# Copied issues will get their default statuses
@available_statuses = []
else
- @available_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
+ @available_statuses = edited_issues.map(&:new_statuses_allowed_to).reduce(:&)
end
- @custom_fields = @issues.map{|i|i.editable_custom_fields}.reduce(:&)
+ if params[:issue]
+ @target_status = @available_statuses.detect {|t| t.id.to_s == params[:issue][:status_id].to_s}
+ if @target_status
+ edited_issues.each {|issue| issue.status = @target_status}
+ end
+ end
+
+ edited_issues.each do |issue|
+ issue.custom_field_values.each do |c|
+ if c.value_present? && @values_by_custom_field[c.custom_field]
+ @values_by_custom_field[c.custom_field].delete(issue.id)
+ end
+ end
+ end
+ @values_by_custom_field.delete_if {|k,v| v.blank?}
+
+ @custom_fields = edited_issues.map{|i|i.editable_custom_fields}.reduce(:&).select {|field| field.format.bulk_edit_supported}
@assignables = target_projects.map(&:assignable_users).reduce(:&)
- @trackers = target_projects.map {|p| Issue.allowed_target_trackers(p) }.reduce(:&)
@versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&)
@categories = target_projects.map {|p| p.issue_categories}.reduce(:&)
if @copy
@attachments_present = @issues.detect {|i| i.attachments.any?}.present?
@subtasks_present = @issues.detect {|i| !i.leaf?}.present?
+ @watchers_present = User.current.allowed_to?(:add_issue_watchers, @projects) && Watcher.where(:watchable_type => 'Issue', :watchable_id => @issues.map(&:id)).exists?
end
- @safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
+ @safe_attributes = edited_issues.map(&:safe_attribute_names).reduce(:&)
@issue_params = params[:issue] || {}
@issue_params[:custom_field_values] ||= {}
@@ -260,6 +282,7 @@ class IssuesController < ApplicationController
attributes = parse_params_for_bulk_update(params[:issue])
copy_subtasks = (params[:copy_subtasks] == '1')
copy_attachments = (params[:copy_attachments] == '1')
+ copy_watchers = (params[:copy_watchers] == '1')
if @copy
unless User.current.allowed_to?(:copy_issues, @projects)
@@ -272,6 +295,9 @@ class IssuesController < ApplicationController
unless User.current.allowed_to?(:add_issues, target_projects)
raise ::Unauthorized
end
+ unless User.current.allowed_to?(:add_issue_watchers, @projects)
+ copy_watchers = false
+ end
else
unless @issues.all?(&:attributes_editable?)
raise ::Unauthorized
@@ -293,6 +319,7 @@ class IssuesController < ApplicationController
issue = orig_issue.copy({},
:attachments => copy_attachments,
:subtasks => copy_subtasks,
+ :watchers => copy_watchers,
:link => link_copy?(params[:link_copy])
)
else
@@ -341,7 +368,12 @@ class IssuesController < ApplicationController
when 'destroy'
# nothing to do
when 'nullify'
+ if Setting.timelog_required_fields.include?('issue_id')
+ flash.now[:error] = l(:field_issue) + " " + ::I18n.t('activerecord.errors.messages.blank')
+ return
+ else
time_entries.update_all(:issue_id => nil)
+ end
when 'reassign'
reassign_to = @project && @project.issues.find_by_id(params[:reassign_to_id])
if reassign_to.nil?
@@ -374,7 +406,7 @@ class IssuesController < ApplicationController
# Overrides Redmine::MenuManager::MenuController::ClassMethods for
# when the "New issue" tab is enabled
def current_menu_item
- if Setting.new_item_menu_tab == '1' && [:new, :create].include?(action_name.to_sym)
+ if Setting.new_item_menu_tab == '1' && [:new, :create].include?(action_name.to_sym)
:new_issue
else
super
@@ -392,10 +424,9 @@ class IssuesController < ApplicationController
else
retrieve_query_from_session
if @query
- sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
- sort_update(@query.sortable_columns, 'issues_index_sort')
+ @per_page = per_page_option
limit = 500
- issue_ids = @query.issue_ids(:order => sort_clause, :limit => (limit + 1), :include => [:assigned_to, :tracker, :priority, :category, :fixed_version])
+ issue_ids = @query.issue_ids(:limit => (limit + 1))
if (idx = issue_ids.index(@issue.id)) && idx < limit
if issue_ids.size < 500
@issue_position = idx + 1
@@ -404,6 +435,11 @@ class IssuesController < ApplicationController
@prev_issue_id = issue_ids[idx - 1] if idx > 0
@next_issue_id = issue_ids[idx + 1] if idx < (issue_ids.size - 1)
end
+ query_params = @query.as_params
+ if @issue_position
+ query_params = query_params.merge(:page => (@issue_position / per_page_option) + 1, :per_page => per_page_option)
+ end
+ @query_path = _project_issues_path(@query.project, query_params)
end
end
end
@@ -460,7 +496,8 @@ class IssuesController < ApplicationController
@link_copy = link_copy?(params[:link_copy]) || request.get?
@copy_attachments = params[:copy_attachments].present? || request.get?
@copy_subtasks = params[:copy_subtasks].present? || request.get?
- @issue.copy_from(@copy_from, :attachments => @copy_attachments, :subtasks => @copy_subtasks, :link => @link_copy)
+ @copy_watchers = User.current.allowed_to?(:add_issue_watchers, @project)
+ @issue.copy_from(@copy_from, :attachments => @copy_attachments, :subtasks => @copy_subtasks, :watchers => @copy_watchers, :link => @link_copy)
@issue.parent_issue_id = @copy_from.parent_id
rescue ActiveRecord::RecordNotFound
render_404
@@ -519,7 +556,7 @@ class IssuesController < ApplicationController
time_entry.issue = @issue
time_entry.user = User.current
time_entry.spent_on = User.current.today
- time_entry.attributes = params[:time_entry]
+ time_entry.safe_attributes = params[:time_entry]
@issue.time_entries << time_entry
end
@@ -548,15 +585,18 @@ class IssuesController < ApplicationController
# Redirects user after a successful issue creation
def redirect_after_create
if params[:continue]
- attrs = {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?}
+ url_params = {}
+ url_params[:issue] = {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?}
+ url_params[:back_url] = params[:back_url].presence
+
if params[:project_id]
- redirect_to new_project_issue_path(@issue.project, :issue => attrs)
+ redirect_to new_project_issue_path(@issue.project, url_params)
else
- attrs.merge! :project_id => @issue.project_id
- redirect_to new_issue_path(:issue => attrs)
+ url_params[:issue].merge! :project_id => @issue.project_id
+ redirect_to new_issue_path(url_params)
end
else
- redirect_to issue_path(@issue)
+ redirect_back_or_default issue_path(@issue)
end
end
end
diff --git a/app/controllers/journals_controller.rb b/app/controllers/journals_controller.rb
index a0905aed1..7f07b38a8 100644
--- a/app/controllers/journals_controller.rb
+++ b/app/controllers/journals_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,10 +16,10 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class JournalsController < ApplicationController
- before_filter :find_journal, :only => [:edit, :update, :diff]
- before_filter :find_issue, :only => [:new]
- before_filter :find_optional_project, :only => [:index]
- before_filter :authorize, :only => [:new, :edit, :update, :diff]
+ before_action :find_journal, :only => [:edit, :update, :diff]
+ before_action :find_issue, :only => [:new]
+ before_action :find_optional_project, :only => [:index]
+ before_action :authorize, :only => [:new, :edit, :update, :diff]
accept_rss_auth :index
menu_item :issues
@@ -27,13 +27,9 @@ class JournalsController < ApplicationController
helper :custom_fields
helper :queries
include QueriesHelper
- helper :sort
- include SortHelper
def index
retrieve_query
- sort_init 'id', 'desc'
- sort_update(@query.sortable_columns)
if @query.valid?
@journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC",
:limit => 25)
@@ -90,7 +86,8 @@ class JournalsController < ApplicationController
def update
(render_403; return false) unless @journal.editable_by?(User.current)
- @journal.update_attributes(:notes => params[:notes]) if params[:notes]
+ @journal.safe_attributes = params[:journal]
+ @journal.save
@journal.destroy if @journal.details.empty? && @journal.notes.blank?
call_hook(:controller_journals_edit_post, { :journal => @journal, :params => params})
respond_to do |format|
diff --git a/app/controllers/mail_handler_controller.rb b/app/controllers/mail_handler_controller.rb
index ca08a2449..ccb832e7a 100644
--- a/app/controllers/mail_handler_controller.rb
+++ b/app/controllers/mail_handler_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,7 +16,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class MailHandlerController < ActionController::Base
- before_filter :check_credential
+ before_action :check_credential
# Displays the email submission form
def new
@@ -27,9 +27,9 @@ class MailHandlerController < ActionController::Base
options = params.dup
email = options.delete(:email)
if MailHandler.receive(email, options)
- render :nothing => true, :status => :created
+ head :created
else
- render :nothing => true, :status => :unprocessable_entity
+ head :unprocessable_entity
end
end
@@ -38,7 +38,7 @@ class MailHandlerController < ActionController::Base
def check_credential
User.current = nil
unless Setting.mail_handler_api_enabled? && params[:key].to_s == Setting.mail_handler_api_key
- render :text => 'Access denied. Incoming emails WS is disabled or key is invalid.', :status => 403
+ render :plain => 'Access denied. Incoming emails WS is disabled or key is invalid.', :status => 403
end
end
end
diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb
index 173b3ee66..1583e9e78 100644
--- a/app/controllers/members_controller.rb
+++ b/app/controllers/members_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,10 +17,10 @@
class MembersController < ApplicationController
model_object Member
- before_filter :find_model_object, :except => [:index, :new, :create, :autocomplete]
- before_filter :find_project_from_association, :except => [:index, :new, :create, :autocomplete]
- before_filter :find_project_by_project_id, :only => [:index, :new, :create, :autocomplete]
- before_filter :authorize
+ before_action :find_model_object, :except => [:index, :new, :create, :autocomplete]
+ before_action :find_project_from_association, :except => [:index, :new, :create, :autocomplete]
+ before_action :find_project_by_project_id, :only => [:index, :new, :create, :autocomplete]
+ before_action :authorize
accept_api_auth :index, :show, :create, :update, :destroy
require_sudo_mode :create, :update, :destroy
@@ -80,6 +80,10 @@ class MembersController < ApplicationController
end
end
+ def edit
+ @roles = Role.givable.to_a
+ end
+
def update
if params[:membership]
@member.set_editable_role_ids(params[:membership][:role_ids])
diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb
index 487dcbe07..76bc19cf6 100644
--- a/app/controllers/messages_controller.rb
+++ b/app/controllers/messages_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -18,10 +18,10 @@
class MessagesController < ApplicationController
menu_item :boards
default_search_scope :messages
- before_filter :find_board, :only => [:new, :preview]
- before_filter :find_attachments, :only => [:preview]
- before_filter :find_message, :except => [:new, :preview]
- before_filter :authorize, :except => [:preview, :edit, :destroy]
+ before_action :find_board, :only => [:new, :preview]
+ before_action :find_attachments, :only => [:preview]
+ before_action :find_message, :except => [:new, :preview]
+ before_action :authorize, :except => [:preview, :edit, :destroy]
helper :boards
helper :watchers
diff --git a/app/controllers/my_controller.rb b/app/controllers/my_controller.rb
index 15ec0892a..bf04d55af 100644
--- a/app/controllers/my_controller.rb
+++ b/app/controllers/my_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,9 +16,10 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class MyController < ApplicationController
- before_filter :require_login
+ self.main_menu = false
+ before_action :require_login
# let user change user's password when user has to
- skip_before_filter :check_password_change, :only => :password
+ skip_before_action :check_password_change, :only => :password
require_sudo_mode :account, only: :post
require_sudo_mode :reset_rss_key, :reset_api_key, :show_api_key, :destroy
@@ -26,19 +27,7 @@ class MyController < ApplicationController
helper :issues
helper :users
helper :custom_fields
-
- BLOCKS = { 'issuesassignedtome' => :label_assigned_to_me_issues,
- 'issuesreportedbyme' => :label_reported_issues,
- 'issueswatched' => :label_watched_issues,
- 'news' => :label_news_latest,
- 'calendar' => :label_calendar,
- 'documents' => :label_document_plural,
- 'timelog' => :label_spent_time
- }.merge(Redmine::Views::MyPage::Block.additional_blocks).freeze
-
- DEFAULT_LAYOUT = { 'left' => ['issuesassignedtome'],
- 'right' => ['issuesreportedbyme']
- }.freeze
+ helper :queries
def index
page
@@ -48,7 +37,8 @@ class MyController < ApplicationController
# Show user's page
def page
@user = User.current
- @blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT
+ @groups = @user.pref.my_page_groups
+ @blocks = @user.pref.my_page_layout
end
# Edit user's account
@@ -56,8 +46,8 @@ class MyController < ApplicationController
@user = User.current
@pref = @user.pref
if request.post?
- @user.safe_attributes = params[:user] if params[:user]
- @user.pref.attributes = params[:pref] if params[:pref]
+ @user.safe_attributes = params[:user]
+ @user.pref.safe_attributes = params[:pref]
if @user.save
@user.pref.save
set_language_if_valid @user.language
@@ -143,69 +133,54 @@ class MyController < ApplicationController
redirect_to my_account_path
end
- # User's page layout configuration
- def page_layout
+ def update_page
@user = User.current
- @blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT.dup
- @block_options = []
- BLOCKS.each do |k, v|
- unless @blocks.values.flatten.include?(k)
- @block_options << [l("my.blocks.#{v}", :default => [v, v.to_s.humanize]), k.dasherize]
- end
+ block_settings = params[:settings] || {}
+
+ block_settings.each do |block, settings|
+ @user.pref.update_block_settings(block, settings)
end
+ @user.pref.save
+ @updated_blocks = block_settings.keys
end
# Add a block to user's page
# The block is added on top of the page
# params[:block] : id of the block to add
def add_block
- block = params[:block].to_s.underscore
- if block.present? && BLOCKS.key?(block)
- @user = User.current
- layout = @user.pref[:my_page_layout] || {}
- # remove if already present in a group
- %w(top left right).each {|f| (layout[f] ||= []).delete block }
- # add it on top
- layout['top'].unshift block
- @user.pref[:my_page_layout] = layout
+ @user = User.current
+ @block = params[:block]
+ if @user.pref.add_block @block
@user.pref.save
+ respond_to do |format|
+ format.html { redirect_to my_page_path }
+ format.js
+ end
+ else
+ render_error :status => 422
end
- redirect_to my_page_layout_path
end
# Remove a block to user's page
# params[:block] : id of the block to remove
def remove_block
- block = params[:block].to_s.underscore
@user = User.current
- # remove block in all groups
- layout = @user.pref[:my_page_layout] || {}
- %w(top left right).each {|f| (layout[f] ||= []).delete block }
- @user.pref[:my_page_layout] = layout
+ @block = params[:block]
+ @user.pref.remove_block @block
@user.pref.save
- redirect_to my_page_layout_path
+ respond_to do |format|
+ format.html { redirect_to my_page_path }
+ format.js
+ end
end
# Change blocks order on user's page
# params[:group] : group to order (top, left or right)
- # params[:list-(top|left|right)] : array of block ids of the group
+ # params[:blocks] : array of block ids of the group
def order_blocks
- group = params[:group]
@user = User.current
- if group.is_a?(String)
- group_items = (params["blocks"] || []).collect(&:underscore)
- group_items.each {|s| s.sub!(/^block_/, '')}
- if group_items and group_items.is_a? Array
- layout = @user.pref[:my_page_layout] || {}
- # remove group blocks if they are presents in other groups
- %w(top left right).each {|f|
- layout[f] = (layout[f] || []) - group_items
- }
- layout[group] = group_items
- @user.pref[:my_page_layout] = layout
- @user.pref.save
- end
- end
- render :nothing => true
+ @user.pref.order_blocks params[:group], params[:blocks]
+ @user.pref.save
+ head 200
end
end
diff --git a/app/controllers/news_controller.rb b/app/controllers/news_controller.rb
index 6e429e827..3df9e5e44 100644
--- a/app/controllers/news_controller.rb
+++ b/app/controllers/news_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -18,11 +18,11 @@
class NewsController < ApplicationController
default_search_scope :news
model_object News
- before_filter :find_model_object, :except => [:new, :create, :index]
- before_filter :find_project_from_association, :except => [:new, :create, :index]
- before_filter :find_project_by_project_id, :only => [:new, :create]
- before_filter :authorize, :except => [:index]
- before_filter :find_optional_project, :only => :index
+ before_action :find_model_object, :except => [:new, :create, :index]
+ before_action :find_project_from_association, :except => [:new, :create, :index]
+ before_action :find_project_by_project_id, :only => [:new, :create]
+ before_action :authorize, :except => [:index]
+ before_action :find_optional_project, :only => :index
accept_rss_auth :index
accept_api_auth :index
@@ -98,14 +98,4 @@ class NewsController < ApplicationController
@news.destroy
redirect_to project_news_index_path(@project)
end
-
- private
-
- def find_optional_project
- return true unless params[:project_id]
- @project = Project.find(params[:project_id])
- authorize
- rescue ActiveRecord::RecordNotFound
- render_404
- end
end
diff --git a/app/controllers/previews_controller.rb b/app/controllers/previews_controller.rb
index 48828b199..37cdc4668 100644
--- a/app/controllers/previews_controller.rb
+++ b/app/controllers/previews_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,7 +16,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class PreviewsController < ApplicationController
- before_filter :find_project, :find_attachments
+ before_action :find_project, :find_attachments
def issue
@issue = Issue.visible.find_by_id(params[:id]) unless params[:id].blank?
@@ -25,8 +25,8 @@ class PreviewsController < ApplicationController
if @description && @description.gsub(/(\r?\n|\n\r?)/, "\n") == @issue.description.to_s.gsub(/(\r?\n|\n\r?)/, "\n")
@description = nil
end
- # params[:notes] is useful for preview of notes in issue history
- @notes = params[:notes] || (params[:issue] ? params[:issue][:notes] : nil)
+ @notes = params[:journal] ? params[:journal][:notes] : nil
+ @notes ||= params[:issue] ? params[:issue][:notes] : nil
else
@description = (params[:issue] ? params[:issue][:description] : nil)
end
diff --git a/app/controllers/principal_memberships_controller.rb b/app/controllers/principal_memberships_controller.rb
index 5cf8ff166..924ecbdf9 100644
--- a/app/controllers/principal_memberships_controller.rb
+++ b/app/controllers/principal_memberships_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,10 +17,11 @@
class PrincipalMembershipsController < ApplicationController
layout 'admin'
+ self.main_menu = false
- before_filter :require_admin
- before_filter :find_principal, :only => [:new, :create]
- before_filter :find_membership, :only => [:update, :destroy]
+ before_action :require_admin
+ before_action :find_principal, :only => [:new, :create]
+ before_action :find_membership, :only => [:edit, :update, :destroy]
def new
@projects = Project.active.all
@@ -39,8 +40,12 @@ class PrincipalMembershipsController < ApplicationController
end
end
+ def edit
+ @roles = Role.givable.to_a
+ end
+
def update
- @membership.attributes = params[:membership]
+ @membership.attributes = params.require(:membership).permit(:role_ids => [])
@membership.save
respond_to do |format|
format.html { redirect_to_principal @principal }
diff --git a/app/controllers/project_enumerations_controller.rb b/app/controllers/project_enumerations_controller.rb
index df3d8c2c4..f68d94869 100644
--- a/app/controllers/project_enumerations_controller.rb
+++ b/app/controllers/project_enumerations_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,8 +16,8 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class ProjectEnumerationsController < ApplicationController
- before_filter :find_project_by_project_id
- before_filter :authorize
+ before_action :find_project_by_project_id
+ before_action :authorize
def update
if params[:enumerations]
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 8753badde..3e3fc69f3 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -18,21 +18,16 @@
class ProjectsController < ApplicationController
menu_item :overview
menu_item :settings, :only => :settings
+ menu_item :projects, :only => [:index, :new, :copy, :create]
- before_filter :find_project, :except => [ :index, :list, :new, :create, :copy ]
- before_filter :authorize, :except => [ :index, :list, :new, :create, :copy, :archive, :unarchive, :destroy]
- before_filter :authorize_global, :only => [:new, :create]
- before_filter :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ]
+ before_action :find_project, :except => [ :index, :autocomplete, :list, :new, :create, :copy ]
+ before_action :authorize, :except => [ :index, :autocomplete, :list, :new, :create, :copy, :archive, :unarchive, :destroy]
+ before_action :authorize_global, :only => [:new, :create]
+ before_action :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ]
accept_rss_auth :index
accept_api_auth :index, :show, :create, :update, :destroy
require_sudo_mode :destroy
- after_filter :only => [:create, :edit, :update, :archive, :unarchive, :destroy] do |controller|
- if controller.request.post?
- controller.send :expire_action, :controller => 'welcome', :action => 'robots'
- end
- end
-
helper :custom_fields
helper :issues
helper :queries
@@ -41,6 +36,11 @@ class ProjectsController < ApplicationController
# Lists visible projects
def index
+ # try to redirect to the requested menu item
+ if params[:jump] && redirect_to_menu_item(params[:jump])
+ return
+ end
+
scope = Project.visible.sorted
respond_to do |format|
@@ -62,6 +62,18 @@ class ProjectsController < ApplicationController
end
end
+ def autocomplete
+ respond_to do |format|
+ format.js {
+ if params[:q].present?
+ @projects = Project.visible.like(params[:q]).to_a
+ else
+ @projects = User.current.projects.to_a
+ end
+ }
+ end
+ end
+
def new
@issue_custom_fields = IssueCustomField.sorted.to_a
@trackers = Tracker.sorted.to_a
@@ -161,6 +173,10 @@ class ProjectsController < ApplicationController
@issue_category ||= IssueCategory.new
@member ||= @project.members.new
@trackers = Tracker.sorted.to_a
+
+ @version_status = params[:version_status] || 'open'
+ @version_name = params[:version_name]
+ @versions = @project.shared_versions.status(@version_status).like(@version_name)
@wiki ||= @project.wiki || Wiki.new(:project => @project)
end
@@ -198,14 +214,14 @@ class ProjectsController < ApplicationController
unless @project.archive
flash[:error] = l(:error_can_not_archive_project)
end
- redirect_to admin_projects_path(:status => params[:status])
+ redirect_to_referer_or admin_projects_path(:status => params[:status])
end
def unarchive
unless @project.active?
@project.unarchive
end
- redirect_to admin_projects_path(:status => params[:status])
+ redirect_to_referer_or admin_projects_path(:status => params[:status])
end
def close
diff --git a/app/controllers/queries_controller.rb b/app/controllers/queries_controller.rb
index 9b14d44eb..54f695fd7 100644
--- a/app/controllers/queries_controller.rb
+++ b/app/controllers/queries_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,8 +17,8 @@
class QueriesController < ApplicationController
menu_item :issues
- before_filter :find_query, :except => [:new, :create, :index]
- before_filter :find_optional_project, :only => [:new, :create]
+ before_action :find_query, :only => [:edit, :update, :destroy]
+ before_action :find_optional_project, :only => [:new, :create]
accept_api_auth :index
@@ -31,9 +31,10 @@ class QueriesController < ApplicationController
else
@limit = per_page_option
end
- @query_count = IssueQuery.visible.count
+ scope = query_class.visible
+ @query_count = scope.count
@query_pages = Paginator.new @query_count, @limit, params['page']
- @queries = IssueQuery.visible.
+ @queries = scope.
order("#{Query.table_name}.name").
limit(@limit).
offset(@offset).
@@ -45,21 +46,21 @@ class QueriesController < ApplicationController
end
def new
- @query = IssueQuery.new
+ @query = query_class.new
@query.user = User.current
@query.project = @project
@query.build_from_params(params)
end
def create
- @query = IssueQuery.new
+ @query = query_class.new
@query.user = User.current
@query.project = @project
update_query_from_params
if @query.save
flash[:notice] = l(:notice_successful_create)
- redirect_to_issues(:query_id => @query)
+ redirect_to_items(:query_id => @query)
else
render :action => 'new', :layout => !request.xhr?
end
@@ -73,7 +74,7 @@ class QueriesController < ApplicationController
if @query.save
flash[:notice] = l(:notice_successful_update)
- redirect_to_issues(:query_id => @query)
+ redirect_to_items(:query_id => @query)
else
render :action => 'edit'
end
@@ -81,12 +82,32 @@ class QueriesController < ApplicationController
def destroy
@query.destroy
- redirect_to_issues(:set_filter => 1)
+ redirect_to_items(:set_filter => 1)
end
-private
+ # Returns the values for a query filter
+ def filter
+ q = query_class.new
+ if params[:project_id].present?
+ q.project = Project.find(params[:project_id])
+ end
+
+ unless User.current.allowed_to?(q.class.view_permission, q.project, :global => true)
+ raise Unauthorized
+ end
+
+ filter = q.available_filters[params[:name].to_s]
+ values = filter ? filter.values : []
+
+ render :json => values
+ rescue ActiveRecord::RecordNotFound
+ render_404
+ end
+
+ private
+
def find_query
- @query = IssueQuery.find(params[:id])
+ @query = Query.find(params[:id])
@project = @query.project
render_403 unless @query.editable_by?(User.current)
rescue ActiveRecord::RecordNotFound
@@ -107,15 +128,20 @@ private
@query.sort_criteria = params[:query] && params[:query][:sort_criteria]
@query.name = params[:query] && params[:query][:name]
if User.current.allowed_to?(:manage_public_queries, @query.project) || User.current.admin?
- @query.visibility = (params[:query] && params[:query][:visibility]) || IssueQuery::VISIBILITY_PRIVATE
+ @query.visibility = (params[:query] && params[:query][:visibility]) || Query::VISIBILITY_PRIVATE
@query.role_ids = params[:query] && params[:query][:role_ids]
else
- @query.visibility = IssueQuery::VISIBILITY_PRIVATE
+ @query.visibility = Query::VISIBILITY_PRIVATE
end
@query
end
- def redirect_to_issues(options)
+ def redirect_to_items(options)
+ method = "redirect_to_#{@query.class.name.underscore}"
+ send method, options
+ end
+
+ def redirect_to_issue_query(options)
if params[:gantt]
if @project
redirect_to project_gantt_path(@project, options)
@@ -126,4 +152,14 @@ private
redirect_to _project_issues_path(@project, options)
end
end
+
+ def redirect_to_time_entry_query(options)
+ redirect_to _time_entries_path(@project, nil, options)
+ end
+
+ # Returns the Query subclass, IssueQuery by default
+ # for compatibility with previous behaviour
+ def query_class
+ Query.get_subclass(params[:type] || 'IssueQuery')
+ end
end
diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb
index 20279871e..08c30efe8 100644
--- a/app/controllers/reports_controller.rb
+++ b/app/controllers/reports_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,7 +17,7 @@
class ReportsController < ApplicationController
menu_item :issues
- before_filter :find_project, :authorize, :find_issue_statuses
+ before_action :find_project, :authorize, :find_issue_statuses
def issue_report
@trackers = @project.rolled_up_trackers(false).visible
@@ -76,14 +76,8 @@ class ReportsController < ApplicationController
@rows = @project.descendants.visible
@data = Issue.by_subproject(@project) || []
@report_title = l(:field_subproject)
- end
-
- respond_to do |format|
- if @field
- format.html {}
- else
- format.html { redirect_to :action => 'issue_report', :id => @project }
- end
+ else
+ render_404
end
end
diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb
index 8da996ee2..29a3b5937 100644
--- a/app/controllers/repositories_controller.rb
+++ b/app/controllers/repositories_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -28,31 +28,22 @@ class RepositoriesController < ApplicationController
menu_item :settings, :only => [:new, :create, :edit, :update, :destroy, :committers]
default_search_scope :changesets
- before_filter :find_project_by_project_id, :only => [:new, :create]
- before_filter :find_repository, :only => [:edit, :update, :destroy, :committers]
- before_filter :find_project_repository, :except => [:new, :create, :edit, :update, :destroy, :committers]
- before_filter :find_changeset, :only => [:revision, :add_related_issue, :remove_related_issue]
- before_filter :authorize
+ before_action :find_project_by_project_id, :only => [:new, :create]
+ before_action :build_new_repository_from_params, :only => [:new, :create]
+ before_action :find_repository, :only => [:edit, :update, :destroy, :committers]
+ before_action :find_project_repository, :except => [:new, :create, :edit, :update, :destroy, :committers]
+ before_action :find_changeset, :only => [:revision, :add_related_issue, :remove_related_issue]
+ before_action :authorize
accept_rss_auth :revisions
rescue_from Redmine::Scm::Adapters::CommandFailed, :with => :show_error_command_failed
def new
- scm = params[:repository_scm] || (Redmine::Scm::Base.all & Setting.enabled_scm).first
- @repository = Repository.factory(scm)
@repository.is_default = @project.repository.nil?
- @repository.project = @project
end
def create
- attrs = pickup_extra_info
- @repository = Repository.factory(params[:repository_scm])
- @repository.safe_attributes = params[:repository]
- if attrs[:attrs_extra].keys.any?
- @repository.merge_extra_info(attrs[:attrs_extra])
- end
- @repository.project = @project
- if request.post? && @repository.save
+ if @repository.save
redirect_to settings_project_path(@project, :tab => 'repositories')
else
render :action => 'new'
@@ -63,12 +54,7 @@ class RepositoriesController < ApplicationController
end
def update
- attrs = pickup_extra_info
- @repository.safe_attributes = attrs[:attrs]
- if attrs[:attrs_extra].keys.any?
- @repository.merge_extra_info(attrs[:attrs_extra])
- end
- @repository.project = @project
+ @repository.safe_attributes = params[:repository]
if @repository.save
redirect_to settings_project_path(@project, :tab => 'repositories')
else
@@ -76,20 +62,6 @@ class RepositoriesController < ApplicationController
end
end
- def pickup_extra_info
- p = {}
- p_extra = {}
- params[:repository].each do |k, v|
- if k =~ /^extra_/
- p_extra[k] = v
- else
- p[k] = v
- end
- end
- {:attrs => p, :attrs_extra => p_extra}
- end
- private :pickup_extra_info
-
def committers
@committers = @repository.committers
@users = @project.users.to_a
@@ -97,7 +69,7 @@ class RepositoriesController < ApplicationController
@users += User.where(:id => additional_user_ids).to_a unless additional_user_ids.empty?
@users.compact!
@users.sort!
- if request.post? && params[:committers].is_a?(Hash)
+ if request.post? && params[:committers].present?
# Build a hash with repository usernames as keys and corresponding user ids as values
@repository.committer_ids = params[:committers].values.inject({}) {|h, c| h[c.first] = c.last; h}
flash[:notice] = l(:notice_successful_update)
@@ -116,7 +88,7 @@ class RepositoriesController < ApplicationController
@entries = @repository.entries(@path, @rev)
@changeset = @repository.find_changeset_by_name(@rev)
if request.xhr?
- @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
+ @entries ? render(:partial => 'dir_list_content') : head(200)
else
(show_error_not_found; return) unless @entries
@changesets = @repository.latest_changesets(@path, @rev)
@@ -200,7 +172,7 @@ class RepositoriesController < ApplicationController
return true if Redmine::MimeType.is_type?('text', path)
# Ruby 1.8.6 has a bug of integer divisions.
# http://apidock.com/ruby/v1_8_6_287/String/is_binary_data%3F
- return false if ent.is_binary_data?
+ return false if Redmine::Scm::Adapters::ScmData.binary?(ent)
true
end
private :is_entry_text_data?
@@ -211,14 +183,17 @@ class RepositoriesController < ApplicationController
@annotate = @repository.scm.annotate(@path, @rev)
if @annotate.nil? || @annotate.empty?
- (render_error l(:error_scm_annotate); return)
- end
- ann_buf_size = 0
- @annotate.lines.each do |buf|
- ann_buf_size += buf.size
- end
- if ann_buf_size > Setting.file_max_size_displayed.to_i.kilobyte
- (render_error l(:error_scm_annotate_big_text_file); return)
+ @annotate = nil
+ @error_message = l(:error_scm_annotate)
+ else
+ ann_buf_size = 0
+ @annotate.lines.each do |buf|
+ ann_buf_size += buf.size
+ end
+ if ann_buf_size > Setting.file_max_size_displayed.to_i.kilobyte
+ @annotate = nil
+ @error_message = l(:error_scm_annotate_big_text_file)
+ end
end
@changeset = @repository.find_changeset_by_name(@rev)
end
@@ -305,6 +280,18 @@ class RepositoriesController < ApplicationController
private
+ def build_new_repository_from_params
+ scm = params[:repository_scm] || (Redmine::Scm::Base.all & Setting.enabled_scm).first
+ unless @repository = Repository.factory(scm)
+ render_404
+ return
+ end
+
+ @repository.project = @project
+ @repository.safe_attributes = params[:repository]
+ @repository
+ end
+
def find_repository
@repository = Repository.find(params[:id])
@project = @repository.project
diff --git a/app/controllers/roles_controller.rb b/app/controllers/roles_controller.rb
index 2de185b5b..b85c51b62 100644
--- a/app/controllers/roles_controller.rb
+++ b/app/controllers/roles_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,10 +17,11 @@
class RolesController < ApplicationController
layout 'admin'
+ self.main_menu = false
- before_filter :require_admin, :except => [:index, :show]
- before_filter :require_admin_or_api_request, :only => [:index, :show]
- before_filter :find_role, :only => [:show, :edit, :update, :destroy]
+ before_action :require_admin, :except => [:index, :show]
+ before_action :require_admin_or_api_request, :only => [:index, :show]
+ before_action :find_role, :only => [:show, :edit, :update, :destroy]
accept_api_auth :index, :show
require_sudo_mode :create, :update, :destroy
@@ -45,7 +46,8 @@ class RolesController < ApplicationController
def new
# Prefills the form with 'Non member' role permissions by default
- @role = Role.new(params[:role] || {:permissions => Role.non_member.permissions})
+ @role = Role.new
+ @role.safe_attributes = params[:role] || {:permissions => Role.non_member.permissions}
if params[:copy].present? && @copy_from = Role.find_by_id(params[:copy])
@role.copy_from(@copy_from)
end
@@ -53,11 +55,12 @@ class RolesController < ApplicationController
end
def create
- @role = Role.new(params[:role])
+ @role = Role.new
+ @role.safe_attributes = params[:role]
if request.post? && @role.save
# workflow copy
if !params[:copy_workflow_from].blank? && (copy_from = Role.find_by_id(params[:copy_workflow_from]))
- @role.workflow_rules.copy(copy_from)
+ @role.copy_workflow_rules(copy_from)
end
flash[:notice] = l(:notice_successful_create)
redirect_to roles_path
@@ -71,27 +74,29 @@ class RolesController < ApplicationController
end
def update
- if @role.update_attributes(params[:role])
+ @role.safe_attributes = params[:role]
+ if @role.save
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_to roles_path(:page => params[:page])
}
- format.js { render :nothing => true }
+ format.js { head 200 }
end
else
respond_to do |format|
format.html { render :action => 'edit' }
- format.js { render :nothing => true, :status => 422 }
+ format.js { head 422 }
end
end
end
def destroy
- @role.destroy
- redirect_to roles_path
- rescue
- flash[:error] = l(:error_can_not_remove_role)
+ begin
+ @role.destroy
+ rescue
+ flash[:error] = l(:error_can_not_remove_role)
+ end
redirect_to roles_path
end
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index cc1970ef1..486029fc9 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,7 +16,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class SearchController < ApplicationController
- before_filter :find_optional_project
+ before_action :find_optional_project
accept_api_auth :index
def index
@@ -49,7 +49,7 @@ class SearchController < ApplicationController
when 'my_projects'
User.current.projects
when 'subprojects'
- @project ? (@project.self_and_descendants.active.to_a) : nil
+ @project ? (@project.self_and_descendants.to_a) : nil
else
@project
end
diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb
index 9552efb9b..7b2dceb31 100644
--- a/app/controllers/settings_controller.rb
+++ b/app/controllers/settings_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,11 +17,12 @@
class SettingsController < ApplicationController
layout 'admin'
+ self.main_menu = false
menu_item :plugins, :only => :plugin
helper :queries
- before_filter :require_admin
+ before_action :require_admin
require_sudo_mode :index, :edit, :plugin
@@ -32,24 +33,30 @@ class SettingsController < ApplicationController
def edit
@notifiables = Redmine::Notifiable.all
- if request.post? && params[:settings] && params[:settings].is_a?(Hash)
- Setting.set_all_from_params(params[:settings])
- flash[:notice] = l(:notice_successful_update)
- redirect_to settings_path(:tab => params[:tab])
- else
- @options = {}
- user_format = User::USER_FORMATS.collect{|key, value| [key, value[:setting_order]]}.sort{|a, b| a[1] <=> b[1]}
- @options[:user_format] = user_format.collect{|f| [User.current.name(f[0]), f[0].to_s]}
- @deliveries = ActionMailer::Base.perform_deliveries
-
- @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
+ if request.post?
+ errors = Setting.set_all_from_params(params[:settings])
+ if errors.blank?
+ flash[:notice] = l(:notice_successful_update)
+ redirect_to settings_path(:tab => params[:tab])
+ return
+ else
+ @setting_errors = errors
+ # render the edit form with error messages
+ end
end
+
+ @options = {}
+ user_format = User::USER_FORMATS.collect{|key, value| [key, value[:setting_order]]}.sort{|a, b| a[1] <=> b[1]}
+ @options[:user_format] = user_format.collect{|f| [User.current.name(f[0]), f[0].to_s]}
+ @deliveries = ActionMailer::Base.perform_deliveries
+
+ @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
def plugin
@@ -60,7 +67,8 @@ class SettingsController < ApplicationController
end
if request.post?
- Setting.send "plugin_#{@plugin.id}=", params[:settings]
+ setting = params[:settings] ? params[:settings].permit!.to_h : {}
+ Setting.send "plugin_#{@plugin.id}=", setting
flash[:notice] = l(:notice_successful_update)
redirect_to plugin_settings_path(@plugin)
else
diff --git a/app/controllers/sys_controller.rb b/app/controllers/sys_controller.rb
index 9231153b6..bd8238a6a 100644
--- a/app/controllers/sys_controller.rb
+++ b/app/controllers/sys_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,13 +16,13 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class SysController < ActionController::Base
- before_filter :check_enabled
+ before_action :check_enabled
def projects
p = Project.active.has_module(:repository).
order("#{Project.table_name}.identifier").preload(:repository).to_a
# extra_info attribute from repository breaks activeresource client
- render :xml => p.to_xml(
+ render :json => p.to_json(
:only => [:id, :identifier, :name, :is_public, :status],
:include => {:repository => {:only => [:id, :url]}}
)
@@ -31,15 +31,16 @@ class SysController < ActionController::Base
def create_project_repository
project = Project.find(params[:id])
if project.repository
- render :nothing => true, :status => 409
+ head 409
else
logger.info "Repository for #{project.name} was reported to be created by #{request.remote_ip}."
- repository = Repository.factory(params[:vendor], params[:repository])
+ repository = Repository.factory(params[:vendor])
+ repository.safe_attributes = params[:repository]
repository.project = project
if repository.save
- render :xml => {repository.class.name.underscore.gsub('/', '-') => {:id => repository.id, :url => repository.url}}, :status => 201
+ render :json => {repository.class.name.underscore.gsub('/', '-') => {:id => repository.id, :url => repository.url}}, :status => 201
else
- render :nothing => true, :status => 422
+ head 422
end
end
end
@@ -64,9 +65,9 @@ class SysController < ActionController::Base
repository.fetch_changesets
end
end
- render :nothing => true, :status => 200
+ head 200
rescue ActiveRecord::RecordNotFound
- render :nothing => true, :status => 404
+ head 404
end
protected
@@ -74,7 +75,7 @@ class SysController < ActionController::Base
def check_enabled
User.current = nil
unless Setting.sys_api_enabled? && params[:key].to_s == Setting.sys_api_key
- render :text => 'Access denied. Repository management WS is disabled or key is invalid.', :status => 403
+ render :plain => 'Access denied. Repository management WS is disabled or key is invalid.', :status => 403
return false
end
end
diff --git a/app/controllers/timelog_controller.rb b/app/controllers/timelog_controller.rb
index aecb8fb98..df6b618bd 100644
--- a/app/controllers/timelog_controller.rb
+++ b/app/controllers/timelog_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,23 +16,22 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class TimelogController < ApplicationController
- menu_item :issues
+ menu_item :time_entries
- before_filter :find_time_entry, :only => [:show, :edit, :update]
- before_filter :check_editability, :only => [:edit, :update]
- before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
- before_filter :authorize, :only => [:show, :edit, :update, :bulk_edit, :bulk_update, :destroy]
+ before_action :find_time_entry, :only => [:show, :edit, :update]
+ before_action :check_editability, :only => [:edit, :update]
+ before_action :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
+ before_action :authorize, :only => [:show, :edit, :update, :bulk_edit, :bulk_update, :destroy]
- before_filter :find_optional_project, :only => [:new, :create, :index, :report]
- before_filter :authorize_global, :only => [:new, :create, :index, :report]
+ before_action :find_optional_issue, :only => [:new, :create]
+ before_action :find_optional_project, :only => [:index, :report]
+ before_action :authorize_global, :only => [:new, :create, :index, :report]
accept_rss_auth :index
accept_api_auth :index, :show, :create, :update, :destroy
rescue_from Query::StatementInvalid, :with => :query_statement_invalid
- helper :sort
- include SortHelper
helper :issues
include TimelogHelper
helper :custom_fields
@@ -41,20 +40,16 @@ class TimelogController < ApplicationController
include QueriesHelper
def index
- @query = TimeEntryQuery.build_from_params(params, :project => @project, :name => '_')
-
- sort_init(@query.sort_criteria.empty? ? [['spent_on', 'desc']] : @query.sort_criteria)
- sort_update(@query.sortable_columns)
- scope = time_entry_scope(:order => sort_clause).
- includes(:project, :user, :issue).
- preload(:issue => [:project, :tracker, :status, :assigned_to, :priority])
+ retrieve_time_entry_query
+ scope = time_entry_scope.
+ preload(:issue => [:project, :tracker, :status, :assigned_to, :priority]).
+ preload(:project, :user)
respond_to do |format|
format.html {
@entry_count = scope.count
@entry_pages = Paginator.new @entry_count, per_page_option, params['page']
@entries = scope.offset(@entry_pages.offset).limit(@entry_pages.per_page).to_a
- @total_hours = scope.sum(:hours).to_f
render :layout => !request.xhr?
}
@@ -76,7 +71,7 @@ class TimelogController < ApplicationController
end
def report
- @query = TimeEntryQuery.build_from_params(params, :project => @project, :name => '_')
+ retrieve_time_entry_query
scope = time_entry_scope
@report = Redmine::Helpers::TimeReport.new(@project, @issue, params[:criteria], params[:columns], scope)
@@ -90,7 +85,7 @@ class TimelogController < ApplicationController
def show
respond_to do |format|
# TODO: Implement html response
- format.html { render :nothing => true, :status => 406 }
+ format.html { head 406 }
format.api
end
end
@@ -172,25 +167,39 @@ class TimelogController < ApplicationController
def bulk_edit
@available_activities = @projects.map(&:activities).reduce(:&)
- @custom_fields = TimeEntry.first.available_custom_fields
+ @custom_fields = TimeEntry.first.available_custom_fields.select {|field| field.format.bulk_edit_supported}
end
def bulk_update
attributes = parse_params_for_bulk_update(params[:time_entry])
- unsaved_time_entry_ids = []
+ unsaved_time_entries = []
+ saved_time_entries = []
+
@time_entries.each do |time_entry|
time_entry.reload
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
+ if time_entry.save
+ saved_time_entries << time_entry
+ else
+ unsaved_time_entries << time_entry
end
end
- set_flash_from_bulk_time_entry_save(@time_entries, unsaved_time_entry_ids)
- redirect_back_or_default project_time_entries_path(@projects.first)
+
+ if unsaved_time_entries.empty?
+ flash[:notice] = l(:notice_successful_update) unless saved_time_entries.empty?
+ redirect_back_or_default project_time_entries_path(@projects.first)
+ else
+ @saved_time_entries = @time_entries
+ @unsaved_time_entries = unsaved_time_entries
+ @time_entries = TimeEntry.where(:id => unsaved_time_entries.map(&:id)).
+ preload(:project => :time_entry_activities).
+ preload(:user).to_a
+
+ bulk_edit
+ render :action => 'bulk_edit'
+ end
end
def destroy
@@ -249,22 +258,17 @@ private
render_404
end
- def set_flash_from_bulk_time_entry_save(time_entries, unsaved_time_entry_ids)
- if unsaved_time_entry_ids.empty?
- flash[:notice] = l(:notice_successful_update) unless time_entries.empty?
+ def find_optional_issue
+ if params[:issue_id].present?
+ @issue = Issue.find(params[:issue_id])
+ @project = @issue.project
else
- flash[:error] = l(:notice_failed_to_save_time_entries,
- :count => unsaved_time_entry_ids.size,
- :total => time_entries.size,
- :ids => '#' + unsaved_time_entry_ids.join(', #'))
+ find_optional_project
end
end
def find_optional_project
- if params[:issue_id].present?
- @issue = Issue.find(params[:issue_id])
- @project = @issue.project
- elsif params[:project_id].present?
+ if params[:project_id].present?
@project = Project.find(params[:project_id])
end
rescue ActiveRecord::RecordNotFound
@@ -273,10 +277,10 @@ private
# Returns the TimeEntry scope for index and report actions
def time_entry_scope(options={})
- scope = @query.results_scope(options)
- if @issue
- scope = scope.on_issue(@issue)
- end
- scope
+ @query.results_scope(options)
+ end
+
+ def retrieve_time_entry_query
+ retrieve_query(TimeEntryQuery, false)
end
end
diff --git a/app/controllers/trackers_controller.rb b/app/controllers/trackers_controller.rb
index 5401bae65..caf6b1165 100644
--- a/app/controllers/trackers_controller.rb
+++ b/app/controllers/trackers_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,9 +17,10 @@
class TrackersController < ApplicationController
layout 'admin'
+ self.main_menu = false
- before_filter :require_admin, :except => :index
- before_filter :require_admin_or_api_request, :only => :index
+ before_action :require_admin, :except => :index
+ before_action :require_admin_or_api_request, :only => :index
accept_api_auth :index
def index
@@ -31,17 +32,19 @@ class TrackersController < ApplicationController
end
def new
- @tracker ||= Tracker.new(params[:tracker])
+ @tracker ||= Tracker.new
+ @tracker.safe_attributes = params[:tracker]
@trackers = Tracker.sorted.to_a
@projects = Project.all
end
def create
- @tracker = Tracker.new(params[:tracker])
+ @tracker = Tracker.new
+ @tracker.safe_attributes = params[:tracker]
if @tracker.save
# workflow copy
if !params[:copy_workflow_from].blank? && (copy_from = Tracker.find_by_id(params[:copy_workflow_from]))
- @tracker.workflow_rules.copy(copy_from)
+ @tracker.copy_workflow_rules(copy_from)
end
flash[:notice] = l(:notice_successful_create)
redirect_to trackers_path
@@ -58,13 +61,14 @@ class TrackersController < ApplicationController
def update
@tracker = Tracker.find(params[:id])
- if @tracker.update_attributes(params[:tracker])
+ @tracker.safe_attributes = params[:tracker]
+ if @tracker.save
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_to trackers_path(:page => params[:page])
}
- format.js { render :nothing => true }
+ format.js { head 200 }
end
else
respond_to do |format|
@@ -72,7 +76,7 @@ class TrackersController < ApplicationController
edit
render :action => 'edit'
}
- format.js { render :nothing => true, :status => 422 }
+ format.js { head 422 }
end
end
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 78642c7c7..0133f9797 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,9 +17,11 @@
class UsersController < ApplicationController
layout 'admin'
+ self.main_menu = false
- before_filter :require_admin, :except => :show
- before_filter :find_user, :only => [:show, :edit, :update, :destroy]
+ before_action :require_admin, :except => :show
+ before_action ->{ find_user(false) }, :only => :show
+ before_action :find_user, :only => [:edit, :update, :destroy]
accept_api_auth :index, :show, :create, :update, :destroy
helper :sort
@@ -68,7 +70,7 @@ class UsersController < ApplicationController
end
# show projects based on current user visibility
- @memberships = @user.memberships.where(Project.visible_condition(User.current)).to_a
+ @memberships = @user.memberships.preload(:roles, :project).where(Project.visible_condition(User.current)).to_a
respond_to do |format|
format.html {
@@ -87,12 +89,10 @@ class UsersController < ApplicationController
end
def create
- @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
+ @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option, :admin => false)
@user.safe_attributes = params[:user]
- @user.admin = params[:user][:admin] || false
- @user.login = params[:user][:login]
@user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] unless @user.auth_source_id
- @user.pref.attributes = params[:pref] if params[:pref]
+ @user.pref.safe_attributes = params[:pref]
if @user.save
Mailer.account_information(@user, @user.password).deliver if params[:send_information]
@@ -127,8 +127,6 @@ class UsersController < ApplicationController
end
def update
- @user.admin = params[:user][:admin] if params[:user][:admin]
- @user.login = params[:user][:login] if params[:user][:login]
if params[:user][:password].present? && (@user.auth_source_id.nil? || params[:user][:auth_source_id].blank?)
@user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation]
end
@@ -136,14 +134,14 @@ class UsersController < ApplicationController
# Was the account actived ? (do it before User#save clears the change)
was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
# TODO: Similar to My#account
- @user.pref.attributes = params[:pref] if params[:pref]
+ @user.pref.safe_attributes = params[:pref]
if @user.save
@user.pref.save
if was_activated
Mailer.account_activated(@user).deliver
- elsif @user.active? && params[:send_information] && @user.password.present? && @user.auth_source_id.nil? && @user != User.current
+ elsif @user.active? && params[:send_information] && @user != User.current
Mailer.account_information(@user, @user.password).deliver
end
@@ -177,10 +175,12 @@ class UsersController < ApplicationController
private
- def find_user
+ def find_user(logged = true)
if params[:id] == 'current'
require_login || return
@user = User.current
+ elsif logged
+ @user = User.logged.find(params[:id])
else
@user = User.find(params[:id])
end
diff --git a/app/controllers/versions_controller.rb b/app/controllers/versions_controller.rb
index beb0f16a3..a5364169e 100644
--- a/app/controllers/versions_controller.rb
+++ b/app/controllers/versions_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -18,10 +18,10 @@
class VersionsController < ApplicationController
menu_item :roadmap
model_object Version
- before_filter :find_model_object, :except => [:index, :new, :create, :close_completed]
- before_filter :find_project_from_association, :except => [:index, :new, :create, :close_completed]
- before_filter :find_project_by_project_id, :only => [:index, :new, :create, :close_completed]
- before_filter :authorize
+ before_action :find_model_object, :except => [:index, :new, :create, :close_completed]
+ before_action :find_project_from_association, :except => [:index, :new, :create, :close_completed]
+ before_action :find_project_by_project_id, :only => [:index, :new, :create, :close_completed]
+ before_action :authorize
accept_api_auth :index, :show, :create, :update, :destroy
@@ -38,9 +38,9 @@ class VersionsController < ApplicationController
@versions = @project.shared_versions.preload(:custom_values)
@versions += @project.rolled_up_versions.visible.preload(:custom_values) if @with_subprojects
- @versions = @versions.uniq.sort
+ @versions = @versions.to_a.uniq.sort
unless params[:completed]
- @completed_versions = @versions.select(&:completed?)
+ @completed_versions = @versions.select(&:completed?).reverse
@versions -= @completed_versions
end
@@ -66,6 +66,7 @@ class VersionsController < ApplicationController
format.html {
@issues = @version.fixed_issues.visible.
includes(:status, :tracker, :priority).
+ preload(:project).
reorder("#{Tracker.table_name}.position, #{Issue.table_name}.id").
to_a
}
diff --git a/app/controllers/watchers_controller.rb b/app/controllers/watchers_controller.rb
index d9f2351d0..3f080e96b 100644
--- a/app/controllers/watchers_controller.rb
+++ b/app/controllers/watchers_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,7 +16,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class WatchersController < ApplicationController
- before_filter :require_login, :find_watchables, :only => [:watch, :unwatch]
+ before_action :require_login, :find_watchables, :only => [:watch, :unwatch]
def watch
set_watcher(@watchables, User.current, true)
@@ -26,7 +26,7 @@ class WatchersController < ApplicationController
set_watcher(@watchables, User.current, false)
end
- before_filter :find_project, :authorize, :only => [:new, :create, :append, :destroy, :autocomplete_for_user]
+ before_action :find_project, :authorize, :only => [:new, :create, :append, :destroy, :autocomplete_for_user]
accept_api_auth :create, :destroy
def new
@@ -35,7 +35,7 @@ class WatchersController < ApplicationController
def create
user_ids = []
- if params[:watcher].is_a?(Hash)
+ if params[:watcher]
user_ids << (params[:watcher][:user_ids] || params[:watcher][:user_id])
else
user_ids << params[:user_id]
@@ -47,19 +47,19 @@ class WatchersController < ApplicationController
end
end
respond_to do |format|
- format.html { redirect_to_referer_or {render :text => 'Watcher added.', :layout => true}}
+ format.html { redirect_to_referer_or {render :html => 'Watcher added.', :status => 200, :layout => true}}
format.js { @users = users_for_new_watcher }
format.api { render_api_ok }
end
end
def append
- if params[:watcher].is_a?(Hash)
+ if params[:watcher]
user_ids = params[:watcher][:user_ids] || [params[:watcher][:user_id]]
@users = User.active.visible.where(:id => user_ids).to_a
end
if @users.blank?
- render :nothing => true
+ head 200
end
end
@@ -69,7 +69,7 @@ class WatchersController < ApplicationController
watchable.set_watcher(user, false)
end
respond_to do |format|
- format.html { redirect_to :back }
+ format.html { redirect_to_referer_or {render :html => 'Watcher removed.', :status => 200, :layout => true} }
format.js
format.api { render_api_ok }
end
@@ -108,7 +108,10 @@ class WatchersController < ApplicationController
watchable.set_watcher(user, watching)
end
respond_to do |format|
- format.html { redirect_to_referer_or {render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true}}
+ format.html {
+ text = watching ? 'Watcher added.' : 'Watcher removed.'
+ redirect_to_referer_or {render :html => text, :status => 200, :layout => true}
+ }
format.js { render :partial => 'set_watcher', :locals => {:user => user, :watched => watchables} }
end
end
diff --git a/app/controllers/welcome_controller.rb b/app/controllers/welcome_controller.rb
index 717c72916..341a64df8 100644
--- a/app/controllers/welcome_controller.rb
+++ b/app/controllers/welcome_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,7 +16,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class WelcomeController < ApplicationController
- caches_action :robots
+ self.main_menu = false
def index
@news = News.latest User.current
diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb
index ebf7a1884..5d9a91327 100644
--- a/app/controllers/wiki_controller.rb
+++ b/app/controllers/wiki_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -31,11 +31,11 @@
# TODO: still being worked on
class WikiController < ApplicationController
default_search_scope :wiki_pages
- before_filter :find_wiki, :authorize
- before_filter :find_existing_or_new_page, :only => [:show, :edit, :update]
- before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy, :destroy_version]
+ before_action :find_wiki, :authorize
+ before_action :find_existing_or_new_page, :only => [:show, :edit, :update]
+ before_action :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy, :destroy_version]
+ before_action :find_attachments, :only => [:preview]
accept_api_auth :index, :show, :update, :destroy
- before_filter :find_attachments, :only => [:preview]
helper :attachments
include AttachmentsHelper
@@ -95,6 +95,9 @@ class WikiController < ApplicationController
end
return
end
+
+ call_hook :controller_wiki_show_before_render, content: @content, format: params[:format]
+
if User.current.allowed_to?(:export_wiki_pages, @project)
if params[:format] == 'pdf'
send_file_headers! :type => 'application/pdf', :filename => filename_for_content_disposition("#{@page.title}.pdf")
@@ -153,7 +156,7 @@ class WikiController < ApplicationController
@content = @page.content || WikiContent.new(:page => @page)
content_params = params[:content]
- if content_params.nil? && params[:wiki_page].is_a?(Hash)
+ if content_params.nil? && params[:wiki_page].present?
content_params = params[:wiki_page].slice(:text, :comments, :version)
end
content_params ||= {}
diff --git a/app/controllers/wikis_controller.rb b/app/controllers/wikis_controller.rb
index 61abfaf65..c61c2f946 100644
--- a/app/controllers/wikis_controller.rb
+++ b/app/controllers/wikis_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,7 +17,7 @@
class WikisController < ApplicationController
menu_item :settings
- before_filter :find_project, :authorize
+ before_action :find_project, :authorize
# Create or update a project's wiki
def edit
diff --git a/app/controllers/workflows_controller.rb b/app/controllers/workflows_controller.rb
index 7f92f2546..c831827cb 100644
--- a/app/controllers/workflows_controller.rb
+++ b/app/controllers/workflows_controller.rb
@@ -1,5 +1,5 @@
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,8 +17,9 @@
class WorkflowsController < ApplicationController
layout 'admin'
+ self.main_menu = false
- before_filter :require_admin
+ before_action :require_admin
def index
@roles = Role.sorted.select(&:consider_workflow?)
@@ -137,7 +138,11 @@ class WorkflowsController < ApplicationController
def find_statuses
@used_statuses_only = (params[:used_statuses_only] == '0' ? false : true)
if @trackers && @used_statuses_only
- @statuses = @trackers.map(&:issue_statuses).flatten.uniq.sort.presence
+ role_ids = Role.all.select(&:consider_workflow?).map(&:id)
+ status_ids = WorkflowTransition.where(
+ :tracker_id => @trackers.map(&:id), :role_id => role_ids
+ ).distinct.pluck(:old_status_id, :new_status_id).flatten.uniq
+ @statuses = IssueStatus.where(:id => status_ids).sorted.to_a.presence
end
@statuses ||= IssueStatus.sorted.to_a
end
diff --git a/app/helpers/account_helper.rb b/app/helpers/account_helper.rb
index 0fedbe2ad..e4d8222bf 100644
--- a/app/helpers/account_helper.rb
+++ b/app/helpers/account_helper.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
diff --git a/app/helpers/activities_helper.rb b/app/helpers/activities_helper.rb
index 8721227c2..54e5580a2 100644
--- a/app/helpers/activities_helper.rb
+++ b/app/helpers/activities_helper.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
diff --git a/app/helpers/admin_helper.rb b/app/helpers/admin_helper.rb
index b8ac260e5..e972ef03d 100644
--- a/app/helpers/admin_helper.rb
+++ b/app/helpers/admin_helper.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 76b789b45..792af987c 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -198,6 +198,8 @@ module ApplicationHelper
l(:general_text_No)
when 'Issue'
object.visible? && html ? link_to_issue(object) : "##{object.id}"
+ when 'Attachment'
+ html ? link_to_attachment(object) : object.filename
when 'CustomValue', 'CustomFieldValue'
if object.custom_field
f = object.custom_field.format.formatted_custom_value(self, object, html)
@@ -219,20 +221,32 @@ module ApplicationHelper
end
def thumbnail_tag(attachment)
- link_to image_tag(thumbnail_path(attachment)),
- named_attachment_path(attachment, attachment.filename),
+ thumbnail_size = Setting.thumbnails_size.to_i
+ link_to(
+ image_tag(
+ thumbnail_path(attachment),
+ :srcset => "#{thumbnail_path(attachment, :size => thumbnail_size * 2)} 2x",
+ :style => "max-width: #{thumbnail_size}px; max-height: #{thumbnail_size}px;"
+ ),
+ named_attachment_path(
+ attachment,
+ attachment.filename
+ ),
:title => attachment.filename
+ )
end
def toggle_link(name, id, options={})
onclick = "$('##{id}').toggle(); "
onclick << (options[:focus] ? "$('##{options[:focus]}').focus(); " : "this.blur(); ")
+ onclick << "$(window).scrollTop($('##{options[:focus]}').position().top); " if options[:scroll]
onclick << "return false;"
link_to(name, "#", :onclick => onclick)
end
+ # Used to format item titles on the activity view
def format_activity_title(text)
- h(truncate_single_line_raw(text, 100))
+ text
end
def format_activity_day(date)
@@ -252,6 +266,11 @@ module ApplicationHelper
end
end
+ def format_changeset_comments(changeset, options={})
+ method = options[:short] ? :short_comments : :comments
+ textilizable changeset, method, :formatting => Setting.commit_logs_formatting?
+ end
+
def due_date_distance_in_words(date)
if date
l((date < User.current.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(User.current.today, date))
@@ -329,22 +348,54 @@ module ApplicationHelper
end
end
+ # Returns the default scope for the quick search form
+ # Could be 'all', 'my_projects', 'subprojects' or nil (current project)
+ def default_search_project_scope
+ if @project && !@project.leaf?
+ 'subprojects'
+ end
+ end
+
+ # Returns an array of projects that are displayed in the quick-jump box
+ def projects_for_jump_box(user=User.current)
+ if user.logged?
+ user.projects.active.select(:id, :name, :identifier, :lft, :rgt).to_a
+ else
+ []
+ end
+ end
+
+ def render_projects_for_jump_box(projects, selected=nil)
+ jump = params[:jump].presence || current_menu_item
+ s = ''.html_safe
+ project_tree(projects) do |project, level|
+ padding = level * 16
+ text = content_tag('span', project.name, :style => "padding-left:#{padding}px;")
+ s << link_to(text, project_path(project, :jump => jump), :title => project.name, :class => (project == selected ? 'selected' : nil))
+ end
+ s
+ end
+
# Renders the project quick-jump box
def render_project_jump_box
- return unless User.current.logged?
- projects = User.current.projects.active.select(:id, :name, :identifier, :lft, :rgt).to_a
- if projects.any?
- options =
- ("" +
- '').html_safe
-
- options << project_tree_options_for_select(projects, :selected => @project) do |p|
- { :value => project_path(:id => p, :jump => current_menu_item) }
- end
-
- content_tag( :span, nil, :class => 'jump-box-arrow') +
- select_tag('project_quick_jump_box', options, :onchange => 'if (this.value != \'\') { window.location = this.value; }')
+ projects = projects_for_jump_box(User.current)
+ if @project && @project.persisted?
+ text = @project.name_was
end
+ text ||= l(:label_jump_to_a_project)
+ url = autocomplete_projects_path(:format => 'js', :jump => current_menu_item)
+
+ trigger = content_tag('span', text, :class => 'drdn-trigger')
+ q = text_field_tag('q', '', :id => 'projects-quick-search', :class => 'autocomplete', :data => {:automcomplete_url => url}, :autocomplete => 'off')
+ all = link_to(l(:label_project_all), projects_path(:jump => current_menu_item), :class => (@project.nil? && controller.class.main_menu ? 'selected' : nil))
+ content = content_tag('div',
+ content_tag('div', q, :class => 'quick-search') +
+ content_tag('div', render_projects_for_jump_box(projects, @project), :class => 'drdn-items projects selection') +
+ content_tag('div', all, :class => 'drdn-items all-projects selection'),
+ :class => 'drdn-content'
+ )
+
+ content_tag('div', trigger + content, :id => "project-jump", :class => "drdn")
end
def project_tree_options_for_select(projects, options = {})
@@ -372,8 +423,8 @@ module ApplicationHelper
# Yields the given block for each project with its level in the tree
#
# Wrapper for Project#project_tree
- def project_tree(projects, &block)
- Project.project_tree(projects, &block)
+ def project_tree(projects, options={}, &block)
+ Project.project_tree(projects, options, &block)
end
def principals_check_box_tags(name, principals)
@@ -424,7 +475,7 @@ module ApplicationHelper
end
def html_hours(text)
- text.gsub(%r{(\d+)\.(\d+)}, '\1.\2').html_safe
+ text.gsub(%r{(\d+)([\.:])(\d+)}, '\1\2\3').html_safe
end
def authoring(created, author, options={})
@@ -441,9 +492,7 @@ module ApplicationHelper
end
def syntax_highlight_lines(name, content)
- lines = []
- syntax_highlight(name, content).each_line { |line| lines << line }
- lines
+ syntax_highlight(name, content).each_line.to_a
end
def syntax_highlight(name, content)
@@ -562,6 +611,9 @@ module ApplicationHelper
css << 'project-' + @project.identifier if @project && @project.identifier.present?
css << 'controller-' + controller_name
css << 'action-' + action_name
+ if UserPreference::TEXTAREA_FONT_OPTIONS.include?(User.current.pref.textarea_font)
+ css << "textarea-#{User.current.pref.textarea_font}"
+ end
css.join(' ')
end
@@ -596,7 +648,13 @@ module ApplicationHelper
text = text.dup
macros = catch_macros(text)
- text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr)
+
+ if options[:formatting] == false
+ text = h(text)
+ else
+ formatting = Setting.text_formatting
+ text = Redmine::WikiFormatting.to_html(formatting, text, :object => obj, :attribute => attr)
+ end
@parsed_headings = []
@heading_anchors = {}
@@ -604,7 +662,7 @@ module ApplicationHelper
parse_sections(text, project, obj, attr, only_path, options)
text = parse_non_pre_blocks(text, obj, macros) do |text|
- [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links].each do |method_name|
+ [:parse_inline_attachments, :parse_hires_images, :parse_wiki_links, :parse_redmine_links].each do |method_name|
send method_name, text, project, obj, attr, only_path, options
end
end
@@ -649,6 +707,15 @@ module ApplicationHelper
parsed
end
+ # add srcset attribute to img tags if filename includes @2x, @3x, etc.
+ # to support hires displays
+ def parse_hires_images(text, project, obj, attr, only_path, options)
+ text.gsub!(/src="([^"]+@(\dx)\.(bmp|gif|jpg|jpe|jpeg|png))"/i) do |m|
+ filename, dpr = $1, $2
+ m + " srcset=\"#{filename} #{dpr}\""
+ end
+ end
+
def parse_inline_attachments(text, project, obj, attr, only_path, options)
return if options[:inline_attachments] == false
@@ -751,11 +818,23 @@ module ApplicationHelper
# source:some/file#L120 -> Link to line 120 of the file
# source:some/file@52#L120 -> Link to line 120 of the file's revision 52
# export:some/file -> Force the download of the file
+ # Forums:
+ # forum#1 -> Link to forum with id 1
+ # forum:Support -> Link to forum named "Support"
+ # forum:"Technical Support" -> Link to forum named "Technical Support"
# Forum messages:
# message#1218 -> Link to message with id 1218
- # Projects:
+ # Projects:
# project:someproject -> Link to project named "someproject"
# project#3 -> Link to project with id 3
+ # News:
+ # news#2 -> Link to news item with id 1
+ # news:Greetings -> Link to news item named "Greetings"
+ # news:"First Release" -> Link to news item named "First Release"
+ # Users:
+ # user:jsmith -> Link to user with login jsmith
+ # @jsmith -> Link to user with login jsmith
+ # user#2 -> Link to user with id 2
#
# Links can refer other objects from other projects, using project identifier:
# identifier:r52
@@ -763,8 +842,20 @@ module ApplicationHelper
# identifier:version:1.0.0
# identifier:source:some/file
def parse_redmine_links(text, default_project, obj, attr, only_path, options)
- text.gsub!(%r{]+?)?>(.*?)|([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-_]+):)?(attachment|document|version|forum|news|message|project|commit|source|export)?(((#)|((([a-z0-9\-_]+)\|)?(r)))((\d+)((#note)?-(\d+))?)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]][^A-Za-z0-9_/])|,|\s|\]|<|$)}) do |m|
- tag_content, leading, esc, project_prefix, project_identifier, prefix, repo_prefix, repo_identifier, sep, identifier, comment_suffix, comment_id = $2, $3, $4, $5, $6, $7, $12, $13, $10 || $14 || $20, $16 || $21, $17, $19
+ text.gsub!(LINKS_RE) do |_|
+ tag_content = $~[:tag_content]
+ leading = $~[:leading]
+ esc = $~[:esc]
+ project_prefix = $~[:project_prefix]
+ project_identifier = $~[:project_identifier]
+ prefix = $~[:prefix]
+ repo_prefix = $~[:repo_prefix]
+ repo_identifier = $~[:repo_identifier]
+ sep = $~[:sep1] || $~[:sep2] || $~[:sep3] || $~[:sep4]
+ identifier = $~[:identifier1] || $~[:identifier2] || $~[:identifier3]
+ comment_suffix = $~[:comment_suffix]
+ comment_id = $~[:comment_id]
+
if tag_content
$&
else
@@ -831,11 +922,12 @@ module ApplicationHelper
if p = Project.visible.find_by_id(oid)
link = link_to_project(p, {:only_path => only_path}, :class => 'project')
end
+ when 'user'
+ u = User.visible.where(:id => oid, :type => 'User').first
+ link = link_to_user(u) if u
end
elsif sep == ':'
- # removes the double quotes if any
- name = identifier.gsub(%r{^"(.*)"$}, "\\1")
- name = CGI.unescapeHTML(name)
+ name = remove_double_quotes(identifier)
case prefix
when 'document'
if project && document = project.documents.visible.find_by_title(name)
@@ -885,13 +977,20 @@ module ApplicationHelper
attachments = options[:attachments] || []
attachments += obj.attachments if obj.respond_to?(:attachments)
if attachments && attachment = Attachment.latest_attach(attachments, name)
- link = link_to_attachment(attachment, :only_path => only_path, :download => true, :class => 'attachment')
+ link = link_to_attachment(attachment, :only_path => only_path, :class => 'attachment')
end
when 'project'
if p = Project.visible.where("identifier = :s OR LOWER(name) = :s", :s => name.downcase).first
link = link_to_project(p, {:only_path => only_path}, :class => 'project')
end
+ when 'user'
+ u = User.visible.where(:login => name, :type => 'User').first
+ link = link_to_user(u) if u
end
+ elsif sep == "@"
+ name = remove_double_quotes(identifier)
+ u = User.visible.where(:login => name, :type => 'User').first
+ link = link_to_user(u) if u
end
end
(leading + (link || "#{project_prefix}#{prefix}#{repo_prefix}#{sep}#{identifier}#{comment_suffix}"))
@@ -899,6 +998,45 @@ module ApplicationHelper
end
end
+ LINKS_RE =
+ %r{
+ ]+?)?>(?.*?)|
+ (?[\s\(,\-\[\>]|^)
+ (?!)?
+ (?(?[a-z0-9\-_]+):)?
+ (?attachment|document|version|forum|news|message|project|commit|source|export|user)?
+ (
+ (
+ (?\#)|
+ (
+ (?(?[a-z0-9\-_]+)\|)?
+ (?r)
+ )
+ )
+ (
+ (?\d+)
+ (?
+ (\#note)?
+ -(?\d+)
+ )?
+ )|
+ (
+ (?:)
+ (?[^"\s<>][^\s<>]*?|"[^"]+?")
+ )|
+ (
+ (?@)
+ (?[a-z0-9_\-@\.]*)
+ )
+ )
+ (?=
+ (?=[[:punct:]][^A-Za-z0-9_/])|
+ ,|
+ \s|
+ \]|
+ <|
+ $)
+ }x
HEADING_RE = /(]+)?>(.+?)<\/h(\d)>)/i unless const_defined?(:HEADING_RE)
def parse_sections(text, project, obj, attr, only_path, options)
@@ -1007,7 +1145,7 @@ module ApplicationHelper
div_class = 'toc'
div_class << ' right' if right_align
div_class << ' left' if left_align
- out = "
"
+ out = "
#{l :label_table_of_contents}
"
root = headings.map(&:first).min
current = root
started = false
@@ -1110,6 +1248,11 @@ module ApplicationHelper
url = params[:back_url]
if url.nil? && referer = request.env['HTTP_REFERER']
url = CGI.unescape(referer.to_s)
+ # URLs that contains the utf8=[checkmark] parameter added by Rails are
+ # parsed as invalid by URI.parse so the redirect to the back URL would
+ # not be accepted (ApplicationController#validate_back_url would return
+ # false)
+ url.gsub!(/(\?|&)utf8=\u2713&?/, '\1')
end
url
end
@@ -1129,7 +1272,7 @@ module ApplicationHelper
link_to_function '',
"toggleCheckboxesBySelector('#{selector}')",
:title => "#{l(:button_check_all)} / #{l(:button_uncheck_all)}",
- :class => 'toggle-checkboxes'
+ :class => 'icon icon-checked'
end
def progress_bar(pcts, options={})
@@ -1155,7 +1298,7 @@ module ApplicationHelper
end
end
- def context_menu(url)
+ def context_menu
unless @context_menu_included
content_for :header_tags do
javascript_include_tag('context_menu') +
@@ -1168,7 +1311,7 @@ module ApplicationHelper
end
@context_menu_included = true
end
- javascript_tag "contextMenuInit('#{ url_for(url) }')"
+ nil
end
def calendar_for(field_id)
@@ -1371,7 +1514,9 @@ module ApplicationHelper
return self
end
- def link_to_content_update(text, url_params = {}, html_options = {})
- link_to(text, url_params, html_options)
+ # remove double quotes if any
+ def remove_double_quotes(identifier)
+ name = identifier.gsub(%r{^"(.*)"$}, "\\1")
+ return CGI.unescapeHTML(name)
end
end
diff --git a/app/helpers/attachments_helper.rb b/app/helpers/attachments_helper.rb
index f3969f4b4..36cf0d4fe 100644
--- a/app/helpers/attachments_helper.rb
+++ b/app/helpers/attachments_helper.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -34,7 +34,12 @@ module AttachmentsHelper
def link_to_attachments(container, options = {})
options.assert_valid_keys(:author, :thumbnails)
- attachments = container.attachments.preload(:author).to_a
+ attachments = if container.attachments.loaded?
+ container.attachments
+ else
+ container.attachments.preload(:author).to_a
+ end
+
if attachments.any?
options = {
:editable => container.attachments_editable?,
@@ -51,19 +56,26 @@ module AttachmentsHelper
end
end
- def render_api_attachment(attachment, api)
+ def render_api_attachment(attachment, api, options={})
api.attachment do
- api.id attachment.id
- api.filename attachment.filename
- api.filesize attachment.filesize
- api.content_type attachment.content_type
- api.description attachment.description
- api.content_url download_named_attachment_url(attachment, attachment.filename)
- if attachment.thumbnailable?
- api.thumbnail_url thumbnail_url(attachment)
- end
- api.author(:id => attachment.author.id, :name => attachment.author.name) if attachment.author
- api.created_on attachment.created_on
+ render_api_attachment_attributes(attachment, api)
+ options.each { |key, value| eval("api.#{key} value") }
end
end
+
+ def render_api_attachment_attributes(attachment, api)
+ api.id attachment.id
+ api.filename attachment.filename
+ api.filesize attachment.filesize
+ api.content_type attachment.content_type
+ api.description attachment.description
+ api.content_url download_named_attachment_url(attachment, attachment.filename)
+ if attachment.thumbnailable?
+ api.thumbnail_url thumbnail_url(attachment)
+ end
+ if attachment.author
+ api.author(:id => attachment.author.id, :name => attachment.author.name)
+ end
+ api.created_on attachment.created_on
+ end
end
diff --git a/app/helpers/auth_sources_helper.rb b/app/helpers/auth_sources_helper.rb
index 8c8295a85..4f4b1cd52 100644
--- a/app/helpers/auth_sources_helper.rb
+++ b/app/helpers/auth_sources_helper.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb
index 78a17fe93..0931dd048 100644
--- a/app/helpers/boards_helper.rb
+++ b/app/helpers/boards_helper.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
diff --git a/app/helpers/calendars_helper.rb b/app/helpers/calendars_helper.rb
index cf624d2a8..715667c15 100644
--- a/app/helpers/calendars_helper.rb
+++ b/app/helpers/calendars_helper.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -53,6 +53,6 @@ module CalendarsHelper
end
def link_to_month(link_name, year, month, options={})
- link_to_content_update(h(link_name), params.merge(:year => year, :month => month), options)
+ link_to(link_name, {:params => request.query_parameters.merge(:year => year, :month => month)}, options)
end
end
diff --git a/app/helpers/context_menus_helper.rb b/app/helpers/context_menus_helper.rb
index e3cf728a6..0254d3add 100644
--- a/app/helpers/context_menus_helper.rb
+++ b/app/helpers/context_menus_helper.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
diff --git a/app/helpers/custom_fields_helper.rb b/app/helpers/custom_fields_helper.rb
index 617a6123d..08c8c5887 100644
--- a/app/helpers/custom_fields_helper.rb
+++ b/app/helpers/custom_fields_helper.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
diff --git a/app/helpers/documents_helper.rb b/app/helpers/documents_helper.rb
index 34ab8aec7..a6741b8cd 100644
--- a/app/helpers/documents_helper.rb
+++ b/app/helpers/documents_helper.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
diff --git a/app/helpers/email_addresses_helper.rb b/app/helpers/email_addresses_helper.rb
index 81449edfa..1f44f212d 100644
--- a/app/helpers/email_addresses_helper.rb
+++ b/app/helpers/email_addresses_helper.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
diff --git a/app/helpers/enumerations_helper.rb b/app/helpers/enumerations_helper.rb
index 737d5a513..2759e8457 100644
--- a/app/helpers/enumerations_helper.rb
+++ b/app/helpers/enumerations_helper.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
diff --git a/app/helpers/gantt_helper.rb b/app/helpers/gantt_helper.rb
index 77d3b8364..65585898f 100644
--- a/app/helpers/gantt_helper.rb
+++ b/app/helpers/gantt_helper.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -23,8 +23,8 @@ module GanttHelper
case in_or_out
when :in
if gantt.zoom < 4
- link_to_content_update l(:text_zoom_in),
- params.merge(gantt.params.merge(:zoom => (gantt.zoom + 1))),
+ link_to l(:text_zoom_in),
+ {:params => request.query_parameters.merge(gantt.params.merge(:zoom => (gantt.zoom + 1)))},
:class => 'icon icon-zoom-in'
else
content_tag(:span, l(:text_zoom_in), :class => 'icon icon-zoom-in').html_safe
@@ -32,8 +32,8 @@ module GanttHelper
when :out
if gantt.zoom > 1
- link_to_content_update l(:text_zoom_out),
- params.merge(gantt.params.merge(:zoom => (gantt.zoom - 1))),
+ link_to l(:text_zoom_out),
+ {:params => request.query_parameters.merge(gantt.params.merge(:zoom => (gantt.zoom - 1)))},
:class => 'icon icon-zoom-out'
else
content_tag(:span, l(:text_zoom_out), :class => 'icon icon-zoom-out').html_safe
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index d321a29ef..7eb885f8a 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
diff --git a/app/helpers/imports_helper.rb b/app/helpers/imports_helper.rb
index edbf2d13e..39f3b95ab 100644
--- a/app/helpers/imports_helper.rb
+++ b/app/helpers/imports_helper.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
diff --git a/app/helpers/issue_categories_helper.rb b/app/helpers/issue_categories_helper.rb
index d214d5e3d..8af3452a5 100644
--- a/app/helpers/issue_categories_helper.rb
+++ b/app/helpers/issue_categories_helper.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
diff --git a/app/helpers/issue_relations_helper.rb b/app/helpers/issue_relations_helper.rb
index d6bc7e446..6208e5600 100644
--- a/app/helpers/issue_relations_helper.rb
+++ b/app/helpers/issue_relations_helper.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
diff --git a/app/helpers/issue_statuses_helper.rb b/app/helpers/issue_statuses_helper.rb
index cb466ae0e..49441dde5 100644
--- a/app/helpers/issue_statuses_helper.rb
+++ b/app/helpers/issue_statuses_helper.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index f2b00ccbf..181907e4d 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
-# Copyright (C) 2006-2016 Jean-Philippe Lang
+# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -32,29 +32,14 @@ module IssuesHelper
end
end
- def grouped_issue_list(issues, query, issue_count_by_group, &block)
- previous_group, first = false, true
- totals_by_group = query.totalable_columns.inject({}) do |h, column|
- h[column] = query.total_by_group_for(column)
- h
- end
- issue_list(issues) do |issue, level|
- group_name = group_count = nil
- if query.grouped?
- group = query.group_by_column.value(issue)
- if first || group != previous_group
- if group.blank? && group != false
- group_name = "(#{l(:label_blank_value)})"
- else
- group_name = format_object(group)
- end
- group_name ||= ""
- group_count = issue_count_by_group[group]
- group_totals = totals_by_group.map {|column, t| total_tag(column, t[group] || 0)}.join(" ").html_safe
- end
+ def grouped_issue_list(issues, query, &block)
+ ancestors = []
+ grouped_query_results(issues, query) do |issue, group_name, group_count, group_totals|
+ while (ancestors.any? && !issue.is_descendant_of?(ancestors.last))
+ ancestors.pop
end
- yield issue, level, group_name, group_count, group_totals
- previous_group, first = group, false
+ yield issue, ancestors.size, group_name, group_count, group_totals
+ ancestors << issue unless issue.leaf?
end
end
@@ -105,7 +90,7 @@ module IssuesHelper
end
def render_descendants_tree(issue)
- s = '