Redmine 4.1.1
This commit is contained in:
parent
33e7b881a5
commit
3d976f1b3b
1593 changed files with 36180 additions and 19489 deletions
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -62,9 +64,15 @@ class AccountController < ApplicationController
|
|||
(redirect_to(home_url); return) unless Setting.lost_password?
|
||||
if prt = (params[:token] || session[:password_recovery_token])
|
||||
@token = Token.find_token("recovery", prt.to_s)
|
||||
if @token.nil? || @token.expired?
|
||||
if @token.nil?
|
||||
redirect_to home_url
|
||||
return
|
||||
elsif @token.expired?
|
||||
# remove expired token from session and let user try again
|
||||
session[:password_recovery_token] = nil
|
||||
flash[:error] = l(:error_token_expired)
|
||||
redirect_to lost_password_url
|
||||
return
|
||||
end
|
||||
|
||||
# redirect to remove the token query parameter from the URL and add it to the session
|
||||
|
@ -87,7 +95,7 @@ class AccountController < ApplicationController
|
|||
@user.must_change_passwd = false
|
||||
if @user.save
|
||||
@token.destroy
|
||||
Mailer.password_updated(@user, { remote_ip: request.remote_ip })
|
||||
Mailer.deliver_password_updated(@user, User.current)
|
||||
flash[:notice] = l(:notice_account_password_updated)
|
||||
redirect_to signin_path
|
||||
return
|
||||
|
@ -119,7 +127,7 @@ class AccountController < ApplicationController
|
|||
if token.save
|
||||
# Don't use the param to send the email
|
||||
recipent = user.mails.detect {|e| email.casecmp(e) == 0} || user.mail
|
||||
Mailer.lost_password(token, recipent).deliver
|
||||
Mailer.deliver_lost_password(user, token, recipent)
|
||||
flash[:notice] = l(:notice_account_lost_email_sent)
|
||||
redirect_to signin_path
|
||||
return
|
||||
|
@ -313,7 +321,7 @@ class AccountController < ApplicationController
|
|||
def register_by_email_activation(user, &block)
|
||||
token = Token.new(:user => user, :action => "register")
|
||||
if user.save and token.save
|
||||
Mailer.register(token).deliver
|
||||
Mailer.deliver_register(user, token)
|
||||
flash[:notice] = l(:notice_account_register_done, :email => ERB::Util.h(user.mail))
|
||||
redirect_to signin_path
|
||||
else
|
||||
|
@ -343,7 +351,7 @@ class AccountController < ApplicationController
|
|||
def register_manually_by_administrator(user, &block)
|
||||
if user.save
|
||||
# Sends an email to the administrators
|
||||
Mailer.account_activation_request(user).deliver
|
||||
Mailer.deliver_account_activation_request(user)
|
||||
account_pending(user)
|
||||
else
|
||||
yield if block_given?
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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 +19,7 @@
|
|||
|
||||
class ActivitiesController < ApplicationController
|
||||
menu_item :activity
|
||||
before_action :find_optional_project
|
||||
before_action :find_optional_project_by_id, :authorize_global
|
||||
accept_rss_auth :index
|
||||
|
||||
def index
|
||||
|
@ -76,15 +78,4 @@ class ActivitiesController < ApplicationController
|
|||
rescue ActiveRecord::RecordNotFound
|
||||
render_404
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# TODO: refactor, duplicated in projects_controller
|
||||
def find_optional_project
|
||||
return true unless params[:id]
|
||||
@project = Project.find(params[:id])
|
||||
authorize
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
render_404
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -52,7 +54,7 @@ class AdminController < ApplicationController
|
|||
begin
|
||||
Redmine::DefaultData::Loader::load(params[:lang])
|
||||
flash[:notice] = l(:notice_default_data_loaded)
|
||||
rescue Exception => e
|
||||
rescue => e
|
||||
flash[:error] = l(:error_can_t_load_default_data, ERB::Util.h(e.message))
|
||||
end
|
||||
end
|
||||
|
@ -60,16 +62,12 @@ class AdminController < ApplicationController
|
|||
end
|
||||
|
||||
def test_email
|
||||
raise_delivery_errors = ActionMailer::Base.raise_delivery_errors
|
||||
# Force ActionMailer to raise delivery errors so we can catch it
|
||||
ActionMailer::Base.raise_delivery_errors = true
|
||||
begin
|
||||
@test = Mailer.test_email(User.current).deliver
|
||||
Mailer.deliver_test_email(User.current)
|
||||
flash[:notice] = l(:notice_email_sent, ERB::Util.h(User.current.mail))
|
||||
rescue Exception => e
|
||||
rescue => e
|
||||
flash[:error] = l(:notice_email_error, ERB::Util.h(Redmine::CodesetUtil.replace_invalid_utf8(e.message.dup)))
|
||||
end
|
||||
ActionMailer::Base.raise_delivery_errors = raise_delivery_errors
|
||||
redirect_to settings_path(:tab => 'notifications')
|
||||
end
|
||||
|
||||
|
@ -78,8 +76,9 @@ class AdminController < ApplicationController
|
|||
[:text_default_administrator_account_changed, User.default_admin_account_changed?],
|
||||
[:text_file_repository_writable, File.writable?(Attachment.storage_path)],
|
||||
["#{l :text_plugin_assets_writable} (./public/plugin_assets)", File.writable?(Redmine::Plugin.public_directory)],
|
||||
[:text_rmagick_available, Object.const_defined?(:Magick)],
|
||||
[:text_convert_available, Redmine::Thumbnail.convert_available?]
|
||||
[:text_minimagick_available, Object.const_defined?(:MiniMagick)],
|
||||
[:text_convert_available, Redmine::Thumbnail.convert_available?],
|
||||
[:text_gs_available, Redmine::Thumbnail.gs_available?]
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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,14 +20,17 @@
|
|||
require 'uri'
|
||||
require 'cgi'
|
||||
|
||||
class Unauthorized < Exception; end
|
||||
class Unauthorized < StandardError; end
|
||||
|
||||
class ApplicationController < ActionController::Base
|
||||
include Redmine::I18n
|
||||
include Redmine::Pagination
|
||||
include Redmine::Hook::Helper
|
||||
include RoutesHelper
|
||||
include AvatarsHelper
|
||||
|
||||
helper :routes
|
||||
helper :avatars
|
||||
|
||||
class_attribute :accept_api_auth_actions
|
||||
class_attribute :accept_rss_auth_actions
|
||||
|
@ -52,6 +57,7 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
before_action :session_expiration, :user_setup, :check_if_login_required, :set_localization, :check_password_change
|
||||
after_action :record_project_usage
|
||||
|
||||
rescue_from ::Unauthorized, :with => :deny_access
|
||||
rescue_from ::ActionView::MissingTemplate, :with => :missing_template
|
||||
|
@ -112,7 +118,7 @@ class ApplicationController < ActionController::Base
|
|||
if (key = api_key_from_request)
|
||||
# Use API key
|
||||
user = User.find_by_api_key(key)
|
||||
elsif request.authorization.to_s =~ /\ABasic /i
|
||||
elsif /\ABasic /i.match?(request.authorization.to_s)
|
||||
# HTTP Basic, either username/password or API key/random
|
||||
authenticate_with_http_basic do |username, password|
|
||||
user = User.try_to_login(username, password) || User.find_by_api_key(username)
|
||||
|
@ -229,9 +235,14 @@ class ApplicationController < ActionController::Base
|
|||
format.any(:atom, :pdf, :csv) {
|
||||
redirect_to signin_path(:back_url => url)
|
||||
}
|
||||
format.xml { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
|
||||
format.api {
|
||||
if Setting.rest_api_enabled? && accept_api_auth?
|
||||
head(:unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"')
|
||||
else
|
||||
head(:forbidden)
|
||||
end
|
||||
}
|
||||
format.js { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
|
||||
format.json { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
|
||||
format.any { head :unauthorized }
|
||||
end
|
||||
return false
|
||||
|
@ -259,7 +270,11 @@ class ApplicationController < ActionController::Base
|
|||
true
|
||||
else
|
||||
if @project && @project.archived?
|
||||
@archived_project = @project
|
||||
render_403 :message => :notice_not_authorized_archived_project
|
||||
elsif @project && !@project.allows_to?(:controller => ctrl, :action => action)
|
||||
# Project module is disabled
|
||||
render_403
|
||||
else
|
||||
deny_access
|
||||
end
|
||||
|
@ -272,27 +287,31 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
# Find project of id params[:id]
|
||||
def find_project
|
||||
@project = Project.find(params[:id])
|
||||
def find_project(project_id=params[:id])
|
||||
@project = Project.find(project_id)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
render_404
|
||||
end
|
||||
|
||||
# Find project of id params[:project_id]
|
||||
def find_project_by_project_id
|
||||
@project = Project.find(params[:project_id])
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
render_404
|
||||
find_project(params[:project_id])
|
||||
end
|
||||
|
||||
# Find project of id params[:id] if present
|
||||
def find_optional_project_by_id
|
||||
if params[:id].present?
|
||||
find_project(params[:id])
|
||||
end
|
||||
end
|
||||
|
||||
# Find a project based on params[:project_id]
|
||||
# TODO: some subclasses override this, see about merging their logic
|
||||
# and authorize the user for the requested action
|
||||
def find_optional_project
|
||||
@project = Project.find(params[:project_id]) unless params[:project_id].blank?
|
||||
allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true)
|
||||
allowed ? true : deny_access
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
render_404
|
||||
if params[:project_id].present?
|
||||
find_project(params[:project_id])
|
||||
end
|
||||
authorize_global
|
||||
end
|
||||
|
||||
# Finds and sets @project based on @object.project
|
||||
|
@ -385,18 +404,30 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
end
|
||||
|
||||
def record_project_usage
|
||||
if @project && @project.id && User.current.logged? && User.current.allowed_to?(:view_project, @project)
|
||||
Redmine::ProjectJumpBox.new(User.current).project_used(@project)
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def back_url
|
||||
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
|
||||
helper_method :back_url
|
||||
|
||||
def redirect_back_or_default(default, options={})
|
||||
back_url = params[:back_url].to_s
|
||||
if back_url.present? && valid_url = validate_back_url(back_url)
|
||||
redirect_to(valid_url)
|
||||
if back_url = validate_back_url(params[:back_url].to_s)
|
||||
redirect_to(back_url)
|
||||
return
|
||||
elsif options[:referer]
|
||||
redirect_to_referer_or default
|
||||
|
@ -409,6 +440,8 @@ class ApplicationController < ActionController::Base
|
|||
# Returns a validated URL string if back_url is a valid url for redirection,
|
||||
# otherwise false
|
||||
def validate_back_url(back_url)
|
||||
return false if back_url.blank?
|
||||
|
||||
if CGI.unescape(back_url).include?('..')
|
||||
return false
|
||||
end
|
||||
|
@ -431,11 +464,11 @@ class ApplicationController < ActionController::Base
|
|||
path = uri.to_s
|
||||
# Ensure that the remaining URL starts with a slash, followed by a
|
||||
# non-slash character or the end
|
||||
if path !~ %r{\A/([^/]|\z)}
|
||||
if !%r{\A/([^/]|\z)}.match?(path)
|
||||
return false
|
||||
end
|
||||
|
||||
if path.match(%r{/(login|account/register|account/lost_password)})
|
||||
if %r{/(login|account/register|account/lost_password)}.match?(path)
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -446,11 +479,13 @@ class ApplicationController < ActionController::Base
|
|||
return path
|
||||
end
|
||||
private :validate_back_url
|
||||
helper_method :validate_back_url
|
||||
|
||||
def valid_back_url?(back_url)
|
||||
!!validate_back_url(back_url)
|
||||
end
|
||||
private :valid_back_url?
|
||||
helper_method :valid_back_url?
|
||||
|
||||
# Redirects to the request referer if present, redirects to args or call block otherwise.
|
||||
def redirect_to_referer_or(*args, &block)
|
||||
|
@ -460,7 +495,7 @@ class ApplicationController < ActionController::Base
|
|||
if args.any?
|
||||
redirect_to *args
|
||||
elsif block_given?
|
||||
block.call
|
||||
yield
|
||||
else
|
||||
raise "#redirect_to_referer_or takes arguments or a block"
|
||||
end
|
||||
|
@ -495,8 +530,8 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
# Handler for ActionView::MissingTemplate exception
|
||||
def missing_template
|
||||
logger.warn "Missing template, responding with 404"
|
||||
def missing_template(exception)
|
||||
logger.warn "Missing template, responding with 404: #{exception}"
|
||||
@project = nil
|
||||
render_404
|
||||
end
|
||||
|
@ -616,7 +651,7 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
# Returns a string that can be used as filename value in Content-Disposition header
|
||||
def filename_for_content_disposition(name)
|
||||
request.env['HTTP_USER_AGENT'] =~ %r{(MSIE|Trident|Edge)} ? ERB::Util.url_encode(name) : name
|
||||
%r{(MSIE|Trident|Edge)}.match?(request.env['HTTP_USER_AGENT']) ? ERB::Util.url_encode(name) : name
|
||||
end
|
||||
|
||||
def api_request?
|
||||
|
@ -649,9 +684,9 @@ class ApplicationController < ActionController::Base
|
|||
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 successful updates or deletions via the API
|
||||
# Renders a 204 response for successful updates or deletions via the API
|
||||
def render_api_ok
|
||||
render_api_head :ok
|
||||
render_api_head :no_content
|
||||
end
|
||||
|
||||
# Renders a head API response
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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,6 +34,14 @@ class AttachmentsController < ApplicationController
|
|||
def show
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
if @attachment.container.respond_to?(:attachments)
|
||||
@attachments = @attachment.container.attachments.to_a
|
||||
if index = @attachments.index(@attachment)
|
||||
@paginator = Redmine::Pagination::Paginator.new(
|
||||
@attachments.size, 1, index+1
|
||||
)
|
||||
end
|
||||
end
|
||||
if @attachment.is_diff?
|
||||
@diff = File.read(@attachment.diskfile, :mode => "rb")
|
||||
@diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
|
||||
|
@ -71,10 +81,11 @@ class AttachmentsController < ApplicationController
|
|||
def thumbnail
|
||||
if @attachment.thumbnailable? && tbnail = @attachment.thumbnail(:size => params[:size])
|
||||
if stale?(:etag => tbnail, :template => false)
|
||||
send_file tbnail,
|
||||
send_file(
|
||||
tbnail,
|
||||
:filename => filename_for_content_disposition(@attachment.filename),
|
||||
:type => detect_content_type(@attachment),
|
||||
:disposition => 'inline'
|
||||
:type => detect_content_type(@attachment, true),
|
||||
:disposition => 'inline')
|
||||
end
|
||||
else
|
||||
# No thumbnail for the attachment or thumbnail could not be created
|
||||
|
@ -156,8 +167,10 @@ class AttachmentsController < ApplicationController
|
|||
|
||||
# Returns the menu item that should be selected when viewing an attachment
|
||||
def current_menu_item
|
||||
if @attachment
|
||||
case @attachment.container
|
||||
container = @attachment.try(:container) || @container
|
||||
|
||||
if container
|
||||
case container
|
||||
when WikiPage
|
||||
:wiki
|
||||
when Message
|
||||
|
@ -165,7 +178,7 @@ class AttachmentsController < ApplicationController
|
|||
when Project, Version
|
||||
:files
|
||||
else
|
||||
@attachment.container.class.name.pluralize.downcase.to_sym
|
||||
container.class.name.pluralize.downcase.to_sym
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -224,12 +237,20 @@ class AttachmentsController < ApplicationController
|
|||
@attachment.deletable? ? true : deny_access
|
||||
end
|
||||
|
||||
def detect_content_type(attachment)
|
||||
def detect_content_type(attachment, is_thumb = false)
|
||||
content_type = attachment.content_type
|
||||
if content_type.blank? || content_type == "application/octet-stream"
|
||||
content_type = Redmine::MimeType.of(attachment.filename)
|
||||
content_type =
|
||||
Redmine::MimeType.of(attachment.filename).presence ||
|
||||
"application/octet-stream"
|
||||
end
|
||||
content_type.to_s
|
||||
|
||||
if is_thumb && content_type == "application/pdf"
|
||||
# PDF previews are stored in PNG format
|
||||
content_type = "image/png"
|
||||
end
|
||||
|
||||
content_type
|
||||
end
|
||||
|
||||
def disposition(attachment)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -58,7 +60,7 @@ class AuthSourcesController < ApplicationController
|
|||
begin
|
||||
@auth_source.test_connection
|
||||
flash[:notice] = l(:notice_successful_connection)
|
||||
rescue Exception => e
|
||||
rescue => e
|
||||
flash[:error] = l(:error_unable_to_connect, e.message)
|
||||
end
|
||||
redirect_to auth_sources_path
|
||||
|
@ -68,22 +70,25 @@ class AuthSourcesController < ApplicationController
|
|||
unless @auth_source.users.exists?
|
||||
@auth_source.destroy
|
||||
flash[:notice] = l(:notice_successful_delete)
|
||||
else
|
||||
flash[:error] = l(:error_can_not_delete_auth_source)
|
||||
end
|
||||
redirect_to auth_sources_path
|
||||
end
|
||||
|
||||
def autocomplete_for_new_user
|
||||
results = AuthSource.search(params[:term])
|
||||
|
||||
render :json => results.map {|result| {
|
||||
'value' => result[:login],
|
||||
'label' => "#{result[:login]} (#{result[:firstname]} #{result[:lastname]})",
|
||||
'login' => result[:login].to_s,
|
||||
'firstname' => result[:firstname].to_s,
|
||||
'lastname' => result[:lastname].to_s,
|
||||
'mail' => result[:mail].to_s,
|
||||
'auth_source_id' => result[:auth_source_id].to_s
|
||||
}}
|
||||
render :json => results.map {|result|
|
||||
{
|
||||
'value' => result[:login],
|
||||
'label' => "#{result[:login]} (#{result[:firstname]} #{result[:lastname]})",
|
||||
'login' => result[:login].to_s,
|
||||
'firstname' => result[:firstname].to_s,
|
||||
'lastname' => result[:lastname].to_s,
|
||||
'mail' => result[:mail].to_s,
|
||||
'auth_source_id' => result[:auth_source_id].to_s
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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,20 +25,18 @@ class AutoCompletesController < ApplicationController
|
|||
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
|
||||
|
||||
scope = Issue.cross_project_scope(@project, params[:scope]).visible
|
||||
scope = scope.open(status == 'o') if status.present?
|
||||
scope = scope.where.not(:id => issue_id.to_i) if issue_id.present?
|
||||
if q.present?
|
||||
if q =~ /\A#?(\d+)\z/
|
||||
issues << scope.find_by(:id => $1.to_i)
|
||||
end
|
||||
issues += scope.like(q).order(:id => :desc).limit(10).to_a
|
||||
issues.compact!
|
||||
else
|
||||
issues += scope.order(:id => :desc).limit(10).to_a
|
||||
end
|
||||
|
||||
render :json => format_issues_json(issues)
|
||||
|
@ -53,10 +53,11 @@ class AutoCompletesController < ApplicationController
|
|||
end
|
||||
|
||||
def format_issues_json(issues)
|
||||
issues.map {|issue| {
|
||||
'id' => issue.id,
|
||||
'label' => "#{issue.tracker} ##{issue.id}: #{issue.subject.to_s.truncate(60)}",
|
||||
'value' => issue.id
|
||||
issues.map {|issue|
|
||||
{
|
||||
'id' => issue.id,
|
||||
'label' => "#{issue.tracker} ##{issue.id}: #{issue.subject.to_s.truncate(60)}",
|
||||
'value' => issue.id
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -108,7 +110,8 @@ class BoardsController < ApplicationController
|
|||
redirect_to_settings_in_projects
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def redirect_to_settings_in_projects
|
||||
redirect_to settings_project_path(@project, :tab => 'boards')
|
||||
end
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -22,7 +24,7 @@ class ContextMenusController < ApplicationController
|
|||
before_action :find_issues, :only => :issues
|
||||
|
||||
def issues
|
||||
if (@issues.size == 1)
|
||||
if @issues.size == 1
|
||||
@issue = @issues.first
|
||||
end
|
||||
@issue_ids = @issues.map(&:id).sort
|
||||
|
@ -43,6 +45,8 @@ class ContextMenusController < ApplicationController
|
|||
@priorities = IssuePriority.active.reverse
|
||||
@back = back_url
|
||||
|
||||
@columns = params[:c]
|
||||
|
||||
@options_by_custom_field = {}
|
||||
if @can[:edit]
|
||||
custom_fields = @issues.map(&:editable_custom_fields).reduce(:&).reject(&:multiple?).select {|field| field.format.bulk_edit_supported}
|
||||
|
@ -64,7 +68,7 @@ class ContextMenusController < ApplicationController
|
|||
preload(:user).to_a
|
||||
|
||||
(render_404; return) unless @time_entries.present?
|
||||
if (@time_entries.size == 1)
|
||||
if @time_entries.size == 1
|
||||
@time_entry = @time_entries.first
|
||||
end
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -46,7 +48,11 @@ class CustomFieldsController < ApplicationController
|
|||
if @custom_field.save
|
||||
flash[:notice] = l(:notice_successful_create)
|
||||
call_hook(:controller_custom_fields_new_after_save, :params => params, :custom_field => @custom_field)
|
||||
redirect_to edit_custom_field_path(@custom_field)
|
||||
if params[:continue]
|
||||
redirect_to new_custom_field_path({:type => @custom_field.type})
|
||||
else
|
||||
redirect_to edit_custom_field_path(@custom_field)
|
||||
end
|
||||
else
|
||||
render :action => 'new'
|
||||
end
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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,7 +33,8 @@ class DocumentsController < ApplicationController
|
|||
documents = @project.documents.includes(:attachments, :category).to_a
|
||||
case @sort_by
|
||||
when 'date'
|
||||
@grouped = documents.group_by {|d| d.updated_on.to_date }
|
||||
documents.sort!{|a,b| b.updated_on <=> a.updated_on}
|
||||
@grouped = documents.group_by {|d| d.updated_on.to_date}
|
||||
when 'title'
|
||||
@grouped = documents.group_by {|d| d.title.first.upcase}
|
||||
when 'author'
|
||||
|
@ -88,7 +91,7 @@ class DocumentsController < ApplicationController
|
|||
render_attachment_warning_if_needed(@document)
|
||||
|
||||
if attachments.present? && attachments[:files].present? && Setting.notified_events.include?('document_added')
|
||||
Mailer.attachments_added(attachments[:files]).deliver
|
||||
Mailer.deliver_attachments_added(attachments[:files])
|
||||
end
|
||||
redirect_to document_path(@document)
|
||||
end
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -57,7 +59,7 @@ class EnumerationsController < ApplicationController
|
|||
end
|
||||
|
||||
def update
|
||||
if @enumeration.update_attributes(enumeration_params)
|
||||
if @enumeration.update(enumeration_params)
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
|
@ -107,7 +109,7 @@ class EnumerationsController < ApplicationController
|
|||
|
||||
def enumeration_params
|
||||
# can't require enumeration on #new action
|
||||
cf_ids = @enumeration.available_custom_fields.map{|c| c.id.to_s}
|
||||
cf_ids = @enumeration.available_custom_fields.map {|c| c.multiple? ? {c.id.to_s => []} : c.id.to_s}
|
||||
params.permit(:enumeration => [:name, :active, :is_default, :position, :custom_field_values => cf_ids])[:enumeration]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -44,7 +46,7 @@ class FilesController < ApplicationController
|
|||
end
|
||||
|
||||
def new
|
||||
@versions = @project.versions.sort
|
||||
@versions = @project.versions.sorted
|
||||
end
|
||||
|
||||
def create
|
||||
|
@ -55,12 +57,13 @@ class FilesController < ApplicationController
|
|||
|
||||
if attachments[:files].present?
|
||||
if Setting.notified_events.include?('file_added')
|
||||
Mailer.attachments_added(attachments[:files]).deliver
|
||||
Mailer.deliver_attachments_added(attachments[:files])
|
||||
end
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
flash[:notice] = l(:label_file_added)
|
||||
redirect_to project_files_path(@project) }
|
||||
redirect_to project_files_path(@project)
|
||||
}
|
||||
format.api { render_api_ok }
|
||||
end
|
||||
else
|
||||
|
@ -68,7 +71,8 @@ class FilesController < ApplicationController
|
|||
format.html {
|
||||
flash.now[:error] = l(:label_attachment) + " " + l('activerecord.errors.messages.invalid')
|
||||
new
|
||||
render :action => 'new' }
|
||||
render :action => 'new'
|
||||
}
|
||||
format.api { render :status => :bad_request }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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,22 +20,23 @@
|
|||
require 'csv'
|
||||
|
||||
class ImportsController < ApplicationController
|
||||
menu_item :issues
|
||||
|
||||
before_action :find_import, :only => [:show, :settings, :mapping, :run]
|
||||
before_action :authorize_global
|
||||
before_action :authorize_import
|
||||
|
||||
layout :import_layout
|
||||
|
||||
helper :issues
|
||||
helper :queries
|
||||
|
||||
def new
|
||||
@import = import_type.new
|
||||
end
|
||||
|
||||
def create
|
||||
@import = IssueImport.new
|
||||
@import = import_type.new
|
||||
@import.user = User.current
|
||||
@import.file = params[:file]
|
||||
@import.set_default_settings
|
||||
@import.set_default_settings(:project_id => params[:project_id])
|
||||
|
||||
if @import.save
|
||||
redirect_to import_settings_path(@import)
|
||||
|
@ -50,10 +53,12 @@ class ImportsController < ApplicationController
|
|||
redirect_to import_mapping_path(@import)
|
||||
end
|
||||
|
||||
rescue CSV::MalformedCSVError => e
|
||||
flash.now[:error] = l(:error_invalid_csv_file_or_settings)
|
||||
rescue ArgumentError, EncodingError => e
|
||||
flash.now[:error] = l(:error_invalid_file_encoding, :encoding => ERB::Util.h(@import.settings['encoding']))
|
||||
rescue CSV::MalformedCSVError, EncodingError => e
|
||||
if e.is_a?(CSV::MalformedCSVError) && e.message !~ /Invalid byte sequence/
|
||||
flash.now[:error] = l(:error_invalid_csv_file_or_settings)
|
||||
else
|
||||
flash.now[:error] = l(:error_invalid_file_encoding, :encoding => ERB::Util.h(@import.settings['encoding']))
|
||||
end
|
||||
rescue SystemCallError => e
|
||||
flash.now[:error] = l(:error_can_not_read_import_file)
|
||||
end
|
||||
|
@ -94,6 +99,14 @@ class ImportsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def current_menu(project)
|
||||
if import_layout == 'admin'
|
||||
nil
|
||||
else
|
||||
:application_menu
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_import
|
||||
|
@ -109,9 +122,9 @@ class ImportsController < ApplicationController
|
|||
end
|
||||
|
||||
def update_from_params
|
||||
if params[:import_settings].is_a?(Hash)
|
||||
if params[:import_settings].present?
|
||||
@import.settings ||= {}
|
||||
@import.settings.merge!(params[:import_settings])
|
||||
@import.settings.merge!(params[:import_settings].to_unsafe_hash)
|
||||
@import.save!
|
||||
end
|
||||
end
|
||||
|
@ -119,4 +132,31 @@ class ImportsController < ApplicationController
|
|||
def max_items_per_request
|
||||
5
|
||||
end
|
||||
|
||||
def import_layout
|
||||
import_type && import_type.layout || 'base'
|
||||
end
|
||||
|
||||
def menu_items
|
||||
menu_item = import_type ? import_type.menu_item : nil
|
||||
|
||||
{ self.controller_name.to_sym => { :actions => {}, :default => menu_item } }
|
||||
end
|
||||
|
||||
def authorize_import
|
||||
return render_404 unless import_type
|
||||
return render_403 unless import_type.authorized?(User.current)
|
||||
end
|
||||
|
||||
def import_type
|
||||
return @import_type if defined? @import_type
|
||||
|
||||
@import_type =
|
||||
if @import
|
||||
@import.class
|
||||
else
|
||||
type = Object.const_get(params[:type]) rescue nil
|
||||
type && type < Import ? type : nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -72,8 +74,8 @@ class IssueStatusesController < ApplicationController
|
|||
def destroy
|
||||
IssueStatus.find(params[:id]).destroy
|
||||
redirect_to issue_statuses_path
|
||||
rescue
|
||||
flash[:error] = l(:error_unable_delete_issue_status)
|
||||
rescue => e
|
||||
flash[:error] = l(:error_unable_delete_issue_status, ERB::Util.h(e.message))
|
||||
redirect_to issue_statuses_path
|
||||
end
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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,7 +20,7 @@
|
|||
class IssuesController < ApplicationController
|
||||
default_search_scope :issues
|
||||
|
||||
before_action :find_issue, :only => [:show, :edit, :update]
|
||||
before_action :find_issue, :only => [:show, :edit, :update, :issue_tab]
|
||||
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]
|
||||
|
@ -84,13 +86,10 @@ class IssuesController < ApplicationController
|
|||
|
||||
def show
|
||||
@journals = @issue.visible_journals_with_index
|
||||
@changesets = @issue.changesets.visible.preload(:repository, :user).to_a
|
||||
@has_changesets = @issue.changesets.visible.preload(:repository, :user).exists?
|
||||
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
|
||||
|
||||
if User.current.wants_comments_in_reverse_order?
|
||||
@journals.reverse!
|
||||
@changesets.reverse!
|
||||
end
|
||||
@journals.reverse! if User.current.wants_comments_in_reverse_order?
|
||||
|
||||
if User.current.allowed_to?(:view_time_entries, @project)
|
||||
Issue.load_visible_spent_hours([@issue])
|
||||
|
@ -102,11 +101,15 @@ class IssuesController < ApplicationController
|
|||
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
|
||||
@priorities = IssuePriority.active
|
||||
@time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
|
||||
@time_entries = @issue.time_entries.visible.preload(:activity, :user)
|
||||
@relation = IssueRelation.new
|
||||
retrieve_previous_and_next_issue_ids
|
||||
render :template => 'issues/show'
|
||||
}
|
||||
format.api
|
||||
format.api {
|
||||
@changesets = @issue.changesets.visible.preload(:repository, :user).to_a
|
||||
@changesets.reverse! if User.current.wants_comments_in_reverse_order?
|
||||
}
|
||||
format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
|
||||
format.pdf {
|
||||
send_file_headers! :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf"
|
||||
|
@ -177,7 +180,7 @@ class IssuesController < ApplicationController
|
|||
|
||||
if saved
|
||||
render_attachment_warning_if_needed(@issue)
|
||||
flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
|
||||
flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record? || params[:no_flash]
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_back_or_default issue_path(@issue, previous_and_next_issue_ids_params) }
|
||||
|
@ -191,6 +194,21 @@ class IssuesController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def issue_tab
|
||||
return render_error :status => 422 unless request.xhr?
|
||||
tab = params[:name]
|
||||
|
||||
case tab
|
||||
when 'time_entries'
|
||||
@time_entries = @issue.time_entries.visible.preload(:activity, :user).to_a
|
||||
render :partial => 'issues/tabs/time_entries', :locals => {:time_entries => @time_entries}
|
||||
when 'changesets'
|
||||
@changesets = @issue.changesets.visible.preload(:repository, :user).to_a
|
||||
@changesets.reverse! if User.current.wants_comments_in_reverse_order?
|
||||
render :partial => 'issues/tabs/changesets', :locals => {:changesets => @changesets}
|
||||
end
|
||||
end
|
||||
|
||||
# Bulk edit/copy a set of issues
|
||||
def bulk_edit
|
||||
@issues.sort!
|
||||
|
@ -259,14 +277,16 @@ class IssuesController < ApplicationController
|
|||
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}
|
||||
@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(:&)
|
||||
@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?
|
||||
@watchers_present = User.current.allowed_to?(:add_issue_watchers, @projects) &&
|
||||
Watcher.where(:watchable_type => 'Issue',
|
||||
:watchable_id => @issues.map(&:id)).exists?
|
||||
end
|
||||
|
||||
@safe_attributes = edited_issues.map(&:safe_attribute_names).reduce(:&)
|
||||
|
@ -316,7 +336,8 @@ class IssuesController < ApplicationController
|
|||
@issues.each do |orig_issue|
|
||||
orig_issue.reload
|
||||
if @copy
|
||||
issue = orig_issue.copy({},
|
||||
issue = orig_issue.copy(
|
||||
{},
|
||||
:attachments => copy_attachments,
|
||||
:subtasks => copy_subtasks,
|
||||
:watchers => copy_watchers,
|
||||
|
@ -464,6 +485,7 @@ class IssuesController < ApplicationController
|
|||
@issue.init_journal(User.current)
|
||||
|
||||
issue_attributes = params[:issue]
|
||||
issue_attributes[:assigned_to_id] = User.current.id if issue_attributes && issue_attributes[:assigned_to_id] == 'me'
|
||||
if issue_attributes && params[:conflict_resolution]
|
||||
case params[:conflict_resolution]
|
||||
when 'overwrite'
|
||||
|
@ -520,6 +542,7 @@ class IssuesController < ApplicationController
|
|||
# so we can use the default version for the new project
|
||||
attrs.delete(:fixed_version_id)
|
||||
end
|
||||
attrs[:assigned_to_id] = User.current.id if attrs[:assigned_to_id] == 'me'
|
||||
@issue.safe_attributes = attrs
|
||||
|
||||
if @issue.project
|
||||
|
@ -554,6 +577,7 @@ class IssuesController < ApplicationController
|
|||
time_entry = @time_entry || TimeEntry.new
|
||||
time_entry.project = @issue.project
|
||||
time_entry.issue = @issue
|
||||
time_entry.author = User.current
|
||||
time_entry.user = User.current
|
||||
time_entry.spent_on = User.current.today
|
||||
time_entry.safe_attributes = params[:time_entry]
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -64,13 +66,14 @@ class JournalsController < ApplicationController
|
|||
if @journal
|
||||
user = @journal.user
|
||||
text = @journal.notes
|
||||
@content = +"#{ll(Setting.default_language, :text_user_wrote_in, {:value => user, :link => "#note-#{params[:journal_indice]}"})}\n> "
|
||||
else
|
||||
user = @issue.author
|
||||
text = @issue.description
|
||||
@content = +"#{ll(Setting.default_language, :text_user_wrote, user)}\n> "
|
||||
end
|
||||
# Replaces pre blocks with [...]
|
||||
text = text.to_s.strip.gsub(%r{<pre>(.*?)</pre>}m, '[...]')
|
||||
@content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> "
|
||||
@content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
render_404
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -26,7 +28,7 @@ class MailHandlerController < ActionController::Base
|
|||
def index
|
||||
options = params.dup
|
||||
email = options.delete(:email)
|
||||
if MailHandler.receive(email, options)
|
||||
if MailHandler.safe_receive(email, options)
|
||||
head :created
|
||||
else
|
||||
head :unprocessable_entity
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -63,6 +65,7 @@ class MessagesController < ApplicationController
|
|||
if @message.save
|
||||
call_hook(:controller_messages_new_after_save, { :params => params, :message => @message})
|
||||
render_attachment_warning_if_needed(@message)
|
||||
flash[:notice] = l(:notice_successful_create)
|
||||
redirect_to board_message_path(@board, @message)
|
||||
end
|
||||
end
|
||||
|
@ -80,6 +83,7 @@ class MessagesController < ApplicationController
|
|||
attachments = Attachment.attach_files(@reply, params[:attachments])
|
||||
render_attachment_warning_if_needed(@reply)
|
||||
end
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
redirect_to board_message_path(@board, @topic, :r => @reply)
|
||||
end
|
||||
|
||||
|
@ -101,6 +105,7 @@ class MessagesController < ApplicationController
|
|||
(render_403; return false) unless @message.destroyable_by?(User.current)
|
||||
r = @message.to_param
|
||||
@message.destroy
|
||||
flash[:notice] = l(:notice_successful_delete)
|
||||
if @message.parent
|
||||
redirect_to board_message_path(@board, @message.parent, :r => r)
|
||||
else
|
||||
|
@ -112,18 +117,23 @@ class MessagesController < ApplicationController
|
|||
@subject = @message.subject
|
||||
@subject = "RE: #{@subject}" unless @subject.starts_with?('RE:')
|
||||
|
||||
@content = "#{ll(Setting.default_language, :text_user_wrote, @message.author)}\n> "
|
||||
if @message.root == @message
|
||||
@content = +"#{ll(Setting.default_language, :text_user_wrote, @message.author)}\n> "
|
||||
else
|
||||
@content = +"#{ll(Setting.default_language, :text_user_wrote_in, {:value => @message.author, :link => "message##{@message.id}"})}\n> "
|
||||
end
|
||||
@content << @message.content.to_s.strip.gsub(%r{<pre>(.*?)</pre>}m, '[...]').gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
|
||||
end
|
||||
|
||||
def preview
|
||||
message = @board.messages.find_by_id(params[:id])
|
||||
@text = (params[:message] || params[:reply])[:content]
|
||||
@text = params[:text] ? params[:text] : nil
|
||||
@previewed = message
|
||||
render :partial => 'common/preview'
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def find_message
|
||||
return unless find_board
|
||||
@message = @board.messages.includes(:parent).find(params[:id])
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -21,13 +23,17 @@ class MyController < ApplicationController
|
|||
# let user change user's password when user has to
|
||||
skip_before_action :check_password_change, :only => :password
|
||||
|
||||
require_sudo_mode :account, only: :post
|
||||
accept_api_auth :account
|
||||
|
||||
require_sudo_mode :account, only: :put
|
||||
require_sudo_mode :reset_rss_key, :reset_api_key, :show_api_key, :destroy
|
||||
|
||||
helper :issues
|
||||
helper :users
|
||||
helper :custom_fields
|
||||
helper :queries
|
||||
helper :activities
|
||||
helper :calendars
|
||||
|
||||
def index
|
||||
page
|
||||
|
@ -45,15 +51,25 @@ class MyController < ApplicationController
|
|||
def account
|
||||
@user = User.current
|
||||
@pref = @user.pref
|
||||
if request.post?
|
||||
if request.put?
|
||||
@user.safe_attributes = params[:user]
|
||||
@user.pref.safe_attributes = params[:pref]
|
||||
if @user.save
|
||||
@user.pref.save
|
||||
set_language_if_valid @user.language
|
||||
flash[:notice] = l(:notice_account_updated)
|
||||
redirect_to my_account_path
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
flash[:notice] = l(:notice_account_updated)
|
||||
redirect_to my_account_path
|
||||
}
|
||||
format.api { render_api_ok }
|
||||
end
|
||||
return
|
||||
else
|
||||
respond_to do |format|
|
||||
format.html { render :action => :account }
|
||||
format.api { render_validation_errors(@user) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -95,7 +111,7 @@ class MyController < ApplicationController
|
|||
if @user.save
|
||||
# The session token was destroyed by the password change, generate a new one
|
||||
session[:tk] = @user.generate_session_token
|
||||
Mailer.password_updated(@user)
|
||||
Mailer.deliver_password_updated(@user, User.current)
|
||||
flash[:notice] = l(:notice_account_password_updated)
|
||||
redirect_to my_account_path
|
||||
end
|
||||
|
@ -138,7 +154,7 @@ class MyController < ApplicationController
|
|||
block_settings = params[:settings] || {}
|
||||
|
||||
block_settings.each do |block, settings|
|
||||
@user.pref.update_block_settings(block, settings)
|
||||
@user.pref.update_block_settings(block, settings.to_unsafe_hash)
|
||||
end
|
||||
@user.pref.save
|
||||
@updated_blocks = block_settings.keys
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -24,7 +26,7 @@ class NewsController < ApplicationController
|
|||
before_action :authorize, :except => [:index]
|
||||
before_action :find_optional_project, :only => :index
|
||||
accept_rss_auth :index
|
||||
accept_api_auth :index
|
||||
accept_api_auth :index, :show, :create, :update, :destroy
|
||||
|
||||
helper :watchers
|
||||
helper :attachments
|
||||
|
@ -69,13 +71,21 @@ class NewsController < ApplicationController
|
|||
def create
|
||||
@news = News.new(:project => @project, :author => User.current)
|
||||
@news.safe_attributes = params[:news]
|
||||
@news.save_attachments(params[:attachments])
|
||||
@news.save_attachments(params[:attachments] || (params[:news] && params[:news][:uploads]))
|
||||
if @news.save
|
||||
render_attachment_warning_if_needed(@news)
|
||||
flash[:notice] = l(:notice_successful_create)
|
||||
redirect_to project_news_index_path(@project)
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
render_attachment_warning_if_needed(@news)
|
||||
flash[:notice] = l(:notice_successful_create)
|
||||
redirect_to project_news_index_path(@project)
|
||||
}
|
||||
format.api { render_api_ok }
|
||||
end
|
||||
else
|
||||
render :action => 'new'
|
||||
respond_to do |format|
|
||||
format.html { render :action => 'new' }
|
||||
format.api { render_validation_errors(@news) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -84,18 +94,29 @@ class NewsController < ApplicationController
|
|||
|
||||
def update
|
||||
@news.safe_attributes = params[:news]
|
||||
@news.save_attachments(params[:attachments])
|
||||
@news.save_attachments(params[:attachments] || (params[:news] && params[:news][:uploads]))
|
||||
if @news.save
|
||||
render_attachment_warning_if_needed(@news)
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
redirect_to news_path(@news)
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
render_attachment_warning_if_needed(@news)
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
redirect_to news_path(@news)
|
||||
}
|
||||
format.api { render_api_ok }
|
||||
end
|
||||
else
|
||||
render :action => 'edit'
|
||||
respond_to do |format|
|
||||
format.html { render :action => 'edit' }
|
||||
format.api { render_validation_errors(@news) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@news.destroy
|
||||
redirect_to project_news_index_path(@project)
|
||||
respond_to do |format|
|
||||
format.html { redirect_to project_news_index_path(@project) }
|
||||
format.api { render_api_ok }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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,28 +18,28 @@
|
|||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
class PreviewsController < ApplicationController
|
||||
before_action :find_project, :find_attachments
|
||||
before_action :find_project, :except => :text
|
||||
before_action :find_attachments
|
||||
|
||||
def issue
|
||||
@issue = Issue.visible.find_by_id(params[:id]) unless params[:id].blank?
|
||||
@issue = Issue.visible.find_by_id(params[:issue_id]) unless params[:issue_id].blank?
|
||||
if @issue
|
||||
@description = params[:issue] && params[:issue][:description]
|
||||
if @description && @description.gsub(/(\r?\n|\n\r?)/, "\n") == @issue.description.to_s.gsub(/(\r?\n|\n\r?)/, "\n")
|
||||
@description = nil
|
||||
end
|
||||
@notes = params[:journal] ? params[:journal][:notes] : nil
|
||||
@notes ||= params[:issue] ? params[:issue][:notes] : nil
|
||||
else
|
||||
@description = (params[:issue] ? params[:issue][:description] : nil)
|
||||
@previewed = @issue
|
||||
end
|
||||
render :layout => false
|
||||
@text = params[:text] ? params[:text] : nil
|
||||
render :partial => 'common/preview'
|
||||
end
|
||||
|
||||
def news
|
||||
if params[:id].present? && news = News.visible.find_by_id(params[:id])
|
||||
@previewed = news
|
||||
end
|
||||
@text = (params[:news] ? params[:news][:description] : nil)
|
||||
@text = params[:text] ? params[:text] : nil
|
||||
render :partial => 'common/preview'
|
||||
end
|
||||
|
||||
def text
|
||||
@text = params[:text] ? params[:text] : nil
|
||||
render :partial => 'common/preview'
|
||||
end
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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,6 +21,8 @@ class PrincipalMembershipsController < ApplicationController
|
|||
layout 'admin'
|
||||
self.main_menu = false
|
||||
|
||||
helper :members
|
||||
|
||||
before_action :require_admin
|
||||
before_action :find_principal, :only => [:new, :create]
|
||||
before_action :find_membership, :only => [:edit, :update, :destroy]
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -20,15 +22,8 @@ class ProjectEnumerationsController < ApplicationController
|
|||
before_action :authorize
|
||||
|
||||
def update
|
||||
if params[:enumerations]
|
||||
saved = Project.transaction do
|
||||
params[:enumerations].each do |id, activity|
|
||||
@project.update_or_create_time_entry_activity(id, activity)
|
||||
end
|
||||
end
|
||||
if saved
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
end
|
||||
if @project.update_or_create_time_entry_activities(update_params)
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
end
|
||||
|
||||
redirect_to settings_project_path(@project, :tab => 'activities')
|
||||
|
@ -41,4 +36,12 @@ class ProjectEnumerationsController < ApplicationController
|
|||
flash[:notice] = l(:notice_successful_update)
|
||||
redirect_to settings_project_path(@project, :tab => 'activities')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_params
|
||||
params.
|
||||
permit(:enumerations => [:parent_id, :active, {:custom_field_values => {}}]).
|
||||
require(:enumerations)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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,8 +33,12 @@ class ProjectsController < ApplicationController
|
|||
helper :custom_fields
|
||||
helper :issues
|
||||
helper :queries
|
||||
include QueriesHelper
|
||||
helper :projects_queries
|
||||
include ProjectsQueriesHelper
|
||||
helper :repositories
|
||||
helper :members
|
||||
helper :trackers
|
||||
|
||||
# Lists visible projects
|
||||
def index
|
||||
|
@ -41,14 +47,19 @@ class ProjectsController < ApplicationController
|
|||
return
|
||||
end
|
||||
|
||||
scope = Project.visible.sorted
|
||||
retrieve_project_query
|
||||
scope = project_scope
|
||||
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
unless params[:closed]
|
||||
scope = scope.active
|
||||
# TODO: see what to do with the board view and pagination
|
||||
if @query.display_type == 'board'
|
||||
@entries = scope.to_a
|
||||
else
|
||||
@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
|
||||
end
|
||||
@projects = scope.to_a
|
||||
}
|
||||
format.api {
|
||||
@offset, @limit = api_offset_and_limit
|
||||
|
@ -59,6 +70,11 @@ class ProjectsController < ApplicationController
|
|||
projects = scope.reorder(:created_on => :desc).limit(Setting.feeds_limit.to_i).to_a
|
||||
render_feed(projects, :title => "#{Setting.app_title}: #{l(:label_project_latest)}")
|
||||
}
|
||||
format.csv {
|
||||
# Export all entries
|
||||
@entries = scope.to_a
|
||||
send_data(query_to_csv(@entries, @query, params), :type => 'text/csv; header=present', :filename => 'projects.csv')
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -158,6 +174,7 @@ class ProjectsController < ApplicationController
|
|||
|
||||
if User.current.allowed_to_view_all_time_entries?(@project)
|
||||
@total_hours = TimeEntry.visible.where(cond).sum(:hours).to_f
|
||||
@total_estimated_hours = Issue.visible.where(cond).sum(:estimated_hours).to_f
|
||||
end
|
||||
|
||||
@key = User.current.rss_key
|
||||
|
@ -176,8 +193,7 @@ class ProjectsController < ApplicationController
|
|||
|
||||
@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)
|
||||
@versions = @project.shared_versions.status(@version_status).like(@version_name).sorted
|
||||
end
|
||||
|
||||
def edit
|
||||
|
@ -189,7 +205,7 @@ class ProjectsController < ApplicationController
|
|||
respond_to do |format|
|
||||
format.html {
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
redirect_to settings_project_path(@project)
|
||||
redirect_to settings_project_path(@project, params[:tab])
|
||||
}
|
||||
format.api { render_api_ok }
|
||||
end
|
||||
|
@ -204,12 +220,6 @@ class ProjectsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def modules
|
||||
@project.enabled_module_names = params[:enabled_module_names]
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
redirect_to settings_project_path(@project, :tab => 'modules')
|
||||
end
|
||||
|
||||
def archive
|
||||
unless @project.archive
|
||||
flash[:error] = l(:error_can_not_archive_project)
|
||||
|
@ -224,6 +234,19 @@ class ProjectsController < ApplicationController
|
|||
redirect_to_referer_or admin_projects_path(:status => params[:status])
|
||||
end
|
||||
|
||||
def bookmark
|
||||
jump_box = Redmine::ProjectJumpBox.new User.current
|
||||
if request.delete?
|
||||
jump_box.delete_project_bookmark @project
|
||||
elsif request.post?
|
||||
jump_box.bookmark_project @project
|
||||
end
|
||||
respond_to do |format|
|
||||
format.js
|
||||
format.html { redirect_to project_path(@project) }
|
||||
end
|
||||
end
|
||||
|
||||
def close
|
||||
@project.close
|
||||
redirect_to project_path(@project)
|
||||
|
@ -247,4 +270,15 @@ class ProjectsController < ApplicationController
|
|||
# hide project in layout
|
||||
@project = nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Returns the ProjectEntry scope for index
|
||||
def project_scope(options={})
|
||||
@query.results_scope(options)
|
||||
end
|
||||
|
||||
def retrieve_project_query
|
||||
retrieve_query(ProjectQuery, false, :defaults => @default_columns_names)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -104,6 +106,10 @@ class QueriesController < ApplicationController
|
|||
render_404
|
||||
end
|
||||
|
||||
def current_menu_item
|
||||
@query ? @query.queried_class.to_s.underscore.pluralize.to_sym : nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_query
|
||||
|
@ -114,18 +120,11 @@ class QueriesController < ApplicationController
|
|||
render_404
|
||||
end
|
||||
|
||||
def find_optional_project
|
||||
@project = Project.find(params[:project_id]) if params[:project_id]
|
||||
render_403 unless User.current.allowed_to?(:save_queries, @project, :global => true)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
render_404
|
||||
end
|
||||
|
||||
def update_query_from_params
|
||||
@query.project = params[:query_is_for_all] ? nil : @project
|
||||
@query.build_from_params(params)
|
||||
@query.column_names = nil if params[:default_columns]
|
||||
@query.sort_criteria = params[:query] && params[:query][:sort_criteria]
|
||||
@query.sort_criteria = (params[:query] && params[:query][:sort_criteria]) || @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]) || Query::VISIBILITY_PRIVATE
|
||||
|
@ -157,6 +156,10 @@ class QueriesController < ApplicationController
|
|||
redirect_to _time_entries_path(@project, nil, options)
|
||||
end
|
||||
|
||||
def redirect_to_project_query(options)
|
||||
redirect_to projects_path(options)
|
||||
end
|
||||
|
||||
# Returns the Query subclass, IssueQuery by default
|
||||
# for compatibility with previous behaviour
|
||||
def query_class
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -21,55 +23,56 @@ class ReportsController < ApplicationController
|
|||
|
||||
def issue_report
|
||||
@trackers = @project.rolled_up_trackers(false).visible
|
||||
@versions = @project.shared_versions.sort
|
||||
@versions = @project.shared_versions.sorted
|
||||
@priorities = IssuePriority.all.reverse
|
||||
@categories = @project.issue_categories
|
||||
@assignees = (Setting.issue_group_assignment? ? @project.principals : @project.users).sort
|
||||
@authors = @project.users.sort
|
||||
@assignees = (Setting.issue_group_assignment? ? @project.principals : @project.users).sorted
|
||||
@authors = @project.users.sorted
|
||||
@subprojects = @project.descendants.visible
|
||||
|
||||
@issues_by_tracker = Issue.by_tracker(@project)
|
||||
@issues_by_version = Issue.by_version(@project)
|
||||
@issues_by_priority = Issue.by_priority(@project)
|
||||
@issues_by_category = Issue.by_category(@project)
|
||||
@issues_by_assigned_to = Issue.by_assigned_to(@project)
|
||||
@issues_by_author = Issue.by_author(@project)
|
||||
with_subprojects = Setting.display_subprojects_issues?
|
||||
@issues_by_tracker = Issue.by_tracker(@project, with_subprojects)
|
||||
@issues_by_version = Issue.by_version(@project, with_subprojects)
|
||||
@issues_by_priority = Issue.by_priority(@project, with_subprojects)
|
||||
@issues_by_category = Issue.by_category(@project, with_subprojects)
|
||||
@issues_by_assigned_to = Issue.by_assigned_to(@project, with_subprojects)
|
||||
@issues_by_author = Issue.by_author(@project, with_subprojects)
|
||||
@issues_by_subproject = Issue.by_subproject(@project) || []
|
||||
|
||||
render :template => "reports/issue_report"
|
||||
end
|
||||
|
||||
def issue_report_details
|
||||
with_subprojects = Setting.display_subprojects_issues?
|
||||
case params[:detail]
|
||||
when "tracker"
|
||||
@field = "tracker_id"
|
||||
@rows = @project.rolled_up_trackers(false).visible
|
||||
@data = Issue.by_tracker(@project)
|
||||
@data = Issue.by_tracker(@project, with_subprojects)
|
||||
@report_title = l(:field_tracker)
|
||||
when "version"
|
||||
@field = "fixed_version_id"
|
||||
@rows = @project.shared_versions.sort
|
||||
@data = Issue.by_version(@project)
|
||||
@rows = @project.shared_versions.sorted
|
||||
@data = Issue.by_version(@project, with_subprojects)
|
||||
@report_title = l(:field_version)
|
||||
when "priority"
|
||||
@field = "priority_id"
|
||||
@rows = IssuePriority.all.reverse
|
||||
@data = Issue.by_priority(@project)
|
||||
@data = Issue.by_priority(@project, with_subprojects)
|
||||
@report_title = l(:field_priority)
|
||||
when "category"
|
||||
@field = "category_id"
|
||||
@rows = @project.issue_categories
|
||||
@data = Issue.by_category(@project)
|
||||
@data = Issue.by_category(@project, with_subprojects)
|
||||
@report_title = l(:field_category)
|
||||
when "assigned_to"
|
||||
@field = "assigned_to_id"
|
||||
@rows = (Setting.issue_group_assignment? ? @project.principals : @project.users).sort
|
||||
@data = Issue.by_assigned_to(@project)
|
||||
@rows = (Setting.issue_group_assignment? ? @project.principals : @project.users).sorted
|
||||
@data = Issue.by_assigned_to(@project, with_subprojects)
|
||||
@report_title = l(:field_assigned_to)
|
||||
when "author"
|
||||
@field = "author_id"
|
||||
@rows = @project.users.sort
|
||||
@data = Issue.by_author(@project)
|
||||
@rows = @project.users.sorted
|
||||
@data = Issue.by_author(@project, with_subprojects)
|
||||
@report_title = l(:field_author)
|
||||
when "subproject"
|
||||
@field = "project_id"
|
||||
|
@ -84,6 +87,6 @@ class ReportsController < ApplicationController
|
|||
private
|
||||
|
||||
def find_issue_statuses
|
||||
@statuses = IssueStatus.sorted.to_a
|
||||
@statuses = @project.rolled_up_statuses.sorted.to_a
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -15,13 +17,11 @@
|
|||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
require 'SVG/Graph/Bar'
|
||||
require 'SVG/Graph/BarHorizontal'
|
||||
require 'digest/sha1'
|
||||
require 'redmine/scm/adapters'
|
||||
|
||||
class ChangesetNotFound < Exception; end
|
||||
class InvalidRevisionParam < Exception; end
|
||||
class ChangesetNotFound < StandardError; end
|
||||
class InvalidRevisionParam < StandardError; end
|
||||
|
||||
class RepositoriesController < ApplicationController
|
||||
menu_item :repository
|
||||
|
@ -148,6 +148,13 @@ class RepositoriesController < ApplicationController
|
|||
send_opt[:disposition] = disposition(@path)
|
||||
send_data @repository.cat(@path, @rev), send_opt
|
||||
else
|
||||
# set up pagination from entry to entry
|
||||
parent_path = @path.split('/')[0...-1].join('/')
|
||||
@entries = @repository.entries(parent_path, @rev).reject(&:is_dir?)
|
||||
if index = @entries.index{|e| e.name == @entry.name}
|
||||
@paginator = Redmine::Pagination::Paginator.new(@entries.size, 1, index+1)
|
||||
end
|
||||
|
||||
if !@entry.size || @entry.size <= Setting.file_max_size_displayed.to_i.kilobyte
|
||||
content = @repository.cat(@path, @rev)
|
||||
(show_error_not_found; return) unless content
|
||||
|
@ -232,7 +239,7 @@ class RepositoriesController < ApplicationController
|
|||
if params[:format] == 'diff'
|
||||
@diff = @repository.diff(@path, @rev, @rev_to)
|
||||
(show_error_not_found; return) unless @diff
|
||||
filename = "changeset_r#{@rev}"
|
||||
filename = +"changeset_r#{@rev}"
|
||||
filename << "_r#{@rev_to}" if @rev_to
|
||||
send_data @diff.join, :filename => "#{filename}.diff",
|
||||
:type => 'text/x-patch',
|
||||
|
@ -250,18 +257,20 @@ class RepositoriesController < ApplicationController
|
|||
Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}-#{current_language}")
|
||||
unless read_fragment(@cache_key)
|
||||
@diff = @repository.diff(@path, @rev, @rev_to)
|
||||
show_error_not_found unless @diff
|
||||
(show_error_not_found; return) unless @diff
|
||||
end
|
||||
|
||||
@changeset = @repository.find_changeset_by_name(@rev)
|
||||
@changeset_to = @rev_to ? @repository.find_changeset_by_name(@rev_to) : nil
|
||||
@diff_format_revisions = @repository.diff_format_revisions(@changeset, @changeset_to)
|
||||
render :diff, :formats => :html, :layout => 'base.html.erb'
|
||||
end
|
||||
end
|
||||
|
||||
def stats
|
||||
end
|
||||
|
||||
# Returns JSON data for repository graphs
|
||||
def graph
|
||||
data = nil
|
||||
case params[:graph]
|
||||
|
@ -271,8 +280,7 @@ class RepositoriesController < ApplicationController
|
|||
data = graph_commits_per_author(@repository)
|
||||
end
|
||||
if data
|
||||
headers["Content-Type"] = "image/svg+xml"
|
||||
send_data(data, :type => "image/svg+xml", :disposition => "inline")
|
||||
render :json => data
|
||||
else
|
||||
render_404
|
||||
end
|
||||
|
@ -313,7 +321,7 @@ class RepositoriesController < ApplicationController
|
|||
@rev = params[:rev].blank? ? @repository.default_branch : params[:rev].to_s.strip
|
||||
@rev_to = params[:rev_to]
|
||||
|
||||
unless @rev.to_s.match(REV_PARAM_RE) && @rev_to.to_s.match(REV_PARAM_RE)
|
||||
unless REV_PARAM_RE.match?(@rev.to_s) && REV_PARAM_RE.match?(@rev_to.to_s)
|
||||
if @repository.branches.blank?
|
||||
raise InvalidRevisionParam
|
||||
end
|
||||
|
@ -341,55 +349,37 @@ class RepositoriesController < ApplicationController
|
|||
end
|
||||
|
||||
def graph_commits_per_month(repository)
|
||||
@date_to = User.current.today
|
||||
@date_from = @date_to << 11
|
||||
@date_from = Date.civil(@date_from.year, @date_from.month, 1)
|
||||
date_to = User.current.today
|
||||
date_from = date_to << 11
|
||||
date_from = Date.civil(date_from.year, date_from.month, 1)
|
||||
commits_by_day = Changeset.
|
||||
where("repository_id = ? AND commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to).
|
||||
where("repository_id = ? AND commit_date BETWEEN ? AND ?", repository.id, date_from, date_to).
|
||||
group(:commit_date).
|
||||
count
|
||||
commits_by_month = [0] * 12
|
||||
commits_by_day.each {|c| commits_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last }
|
||||
commits_by_day.each {|c| commits_by_month[(date_to.month - c.first.to_date.month) % 12] += c.last }
|
||||
|
||||
changes_by_day = Change.
|
||||
joins(:changeset).
|
||||
where("#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to).
|
||||
where("#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, date_from, date_to).
|
||||
group(:commit_date).
|
||||
count
|
||||
changes_by_month = [0] * 12
|
||||
changes_by_day.each {|c| changes_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last }
|
||||
changes_by_day.each {|c| changes_by_month[(date_to.month - c.first.to_date.month) % 12] += c.last }
|
||||
|
||||
fields = []
|
||||
today = User.current.today
|
||||
12.times {|m| fields << month_name(((today.month - 1 - m) % 12) + 1)}
|
||||
|
||||
graph = SVG::Graph::Bar.new(
|
||||
:height => 300,
|
||||
:width => 800,
|
||||
:fields => fields.reverse,
|
||||
:stack => :side,
|
||||
:scale_integers => true,
|
||||
:step_x_labels => 2,
|
||||
:show_data_values => false,
|
||||
:graph_title => l(:label_commits_per_month),
|
||||
:show_graph_title => true
|
||||
)
|
||||
|
||||
graph.add_data(
|
||||
:data => commits_by_month[0..11].reverse,
|
||||
:title => l(:label_revision_plural)
|
||||
)
|
||||
|
||||
graph.add_data(
|
||||
:data => changes_by_month[0..11].reverse,
|
||||
:title => l(:label_change_plural)
|
||||
)
|
||||
|
||||
graph.burn
|
||||
data = {
|
||||
:labels => fields.reverse,
|
||||
:commits => commits_by_month[0..11].reverse,
|
||||
:changes => changes_by_month[0..11].reverse
|
||||
}
|
||||
end
|
||||
|
||||
def graph_commits_per_author(repository)
|
||||
#data
|
||||
# data
|
||||
stats = repository.stats_by_author
|
||||
fields, commits_data, changes_data = [], [], []
|
||||
stats.each do |name, hsh|
|
||||
|
@ -398,7 +388,7 @@ class RepositoriesController < ApplicationController
|
|||
changes_data << hsh[:changes_count]
|
||||
end
|
||||
|
||||
#expand to 10 values if needed
|
||||
# expand to 10 values if needed
|
||||
fields = fields + [""]*(10 - fields.length) if fields.length<10
|
||||
commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
|
||||
changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
|
||||
|
@ -406,27 +396,11 @@ class RepositoriesController < ApplicationController
|
|||
# Remove email address in usernames
|
||||
fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
|
||||
|
||||
#prepare graph
|
||||
graph = SVG::Graph::BarHorizontal.new(
|
||||
:height => 30 * commits_data.length,
|
||||
:width => 800,
|
||||
:fields => fields,
|
||||
:stack => :side,
|
||||
:scale_integers => true,
|
||||
:show_data_values => false,
|
||||
:rotate_y_labels => false,
|
||||
:graph_title => l(:label_commits_per_author),
|
||||
:show_graph_title => true
|
||||
)
|
||||
graph.add_data(
|
||||
:data => commits_data,
|
||||
:title => l(:label_revision_plural)
|
||||
)
|
||||
graph.add_data(
|
||||
:data => changes_data,
|
||||
:title => l(:label_change_plural)
|
||||
)
|
||||
graph.burn
|
||||
data = {
|
||||
:labels => fields.reverse,
|
||||
:commits => commits_data.reverse,
|
||||
:changes => changes_data.reverse
|
||||
}
|
||||
end
|
||||
|
||||
def disposition(path)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -101,16 +103,22 @@ class RolesController < ApplicationController
|
|||
end
|
||||
|
||||
def permissions
|
||||
@roles = Role.sorted.to_a
|
||||
@permissions = Redmine::AccessControl.permissions.select { |p| !p.public? }
|
||||
if request.post?
|
||||
@roles.each do |role|
|
||||
role.permissions = params[:permissions][role.id.to_s]
|
||||
role.save
|
||||
end
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
redirect_to roles_path
|
||||
scope = Role.sorted
|
||||
if params[:ids].present?
|
||||
scope = scope.where(:id => params[:ids])
|
||||
end
|
||||
@roles = scope.to_a
|
||||
@permissions = Redmine::AccessControl.permissions.select { |p| !p.public? }
|
||||
end
|
||||
|
||||
def update_permissions
|
||||
@roles = Role.where(:id => params[:permissions].keys)
|
||||
@roles.each do |role|
|
||||
role.permissions = params[:permissions][role.id.to_s]
|
||||
role.save
|
||||
end
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
redirect_to roles_path
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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,12 +18,11 @@
|
|||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
class SearchController < ApplicationController
|
||||
before_action :find_optional_project
|
||||
before_action :find_optional_project_by_id, :authorize_global
|
||||
accept_api_auth :index
|
||||
|
||||
def index
|
||||
@question = params[:q] || ""
|
||||
@question.strip!
|
||||
@question = params[:q]&.strip || ""
|
||||
@all_words = params[:all_words] ? params[:all_words].present? : true
|
||||
@titles_only = params[:titles_only] ? params[:titles_only].present? : false
|
||||
@search_attachments = params[:attachments].presence || '0'
|
||||
|
@ -68,7 +69,7 @@ class SearchController < ApplicationController
|
|||
fetcher = Redmine::Search::Fetcher.new(
|
||||
@question, User.current, @scope, projects_to_search,
|
||||
:all_words => @all_words, :titles_only => @titles_only, :attachments => @search_attachments, :open_issues => @open_issues,
|
||||
:cache => params[:page].present?, :params => params
|
||||
:cache => params[:page].present?, :params => params.to_unsafe_hash
|
||||
)
|
||||
|
||||
if fetcher.tokens.present?
|
||||
|
@ -87,13 +88,4 @@ class SearchController < ApplicationController
|
|||
format.api { @results ||= []; render :layout => false }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def find_optional_project
|
||||
return true unless params[:id]
|
||||
@project = Project.find(params[:id])
|
||||
check_project_privacy
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
render_404
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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 +36,7 @@ class SettingsController < ApplicationController
|
|||
def edit
|
||||
@notifiables = Redmine::Notifiable.all
|
||||
if request.post?
|
||||
errors = Setting.set_all_from_params(params[:settings])
|
||||
errors = Setting.set_all_from_params(params[:settings].to_unsafe_hash)
|
||||
if errors.blank?
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
redirect_to settings_path(:tab => params[:tab])
|
||||
|
@ -46,7 +48,7 @@ class SettingsController < ApplicationController
|
|||
end
|
||||
|
||||
@options = {}
|
||||
user_format = User::USER_FORMATS.collect{|key, value| [key, value[:setting_order]]}.sort{|a, b| a[1] <=> b[1]}
|
||||
user_format = User::USER_FORMATS.collect{|key, value| [key, value[:setting_order]]}.sort_by{|f| f[1]}
|
||||
@options[:user_format] = user_format.collect{|f| [User.current.name(f[0]), f[0].to_s]}
|
||||
@deliveries = ActionMailer::Base.perform_deliveries
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -38,7 +40,7 @@ class SysController < ActionController::Base
|
|||
repository.safe_attributes = params[:repository]
|
||||
repository.project = project
|
||||
if repository.save
|
||||
render :json => {repository.class.name.underscore.gsub('/', '-') => {:id => repository.id, :url => repository.url}}, :status => 201
|
||||
render :json => {repository.class.name.underscore.tr('/', '-') => {:id => repository.id, :url => repository.url}}, :status => 201
|
||||
else
|
||||
head 422
|
||||
end
|
||||
|
@ -50,7 +52,7 @@ class SysController < ActionController::Base
|
|||
scope = Project.active.has_module(:repository)
|
||||
if params[:id]
|
||||
project = nil
|
||||
if params[:id].to_s =~ /^\d*$/
|
||||
if /^\d*$/.match?(params[:id].to_s)
|
||||
project = scope.find(params[:id])
|
||||
else
|
||||
project = scope.find_by_identifier(params[:id])
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -25,7 +27,6 @@ class TimelogController < ApplicationController
|
|||
|
||||
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
|
||||
|
@ -91,12 +92,12 @@ class TimelogController < ApplicationController
|
|||
end
|
||||
|
||||
def new
|
||||
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
|
||||
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :author => User.current, :spent_on => User.current.today)
|
||||
@time_entry.safe_attributes = params[:time_entry]
|
||||
end
|
||||
|
||||
def create
|
||||
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
|
||||
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :author => User.current, :user => User.current, :spent_on => User.current.today)
|
||||
@time_entry.safe_attributes = params[:time_entry]
|
||||
if @time_entry.project && !User.current.allowed_to?(:log_time, @time_entry.project)
|
||||
render_403
|
||||
|
@ -146,7 +147,6 @@ class TimelogController < ApplicationController
|
|||
|
||||
def update
|
||||
@time_entry.safe_attributes = params[:time_entry]
|
||||
|
||||
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
|
||||
|
||||
if @time_entry.save
|
||||
|
@ -166,8 +166,18 @@ class TimelogController < ApplicationController
|
|||
end
|
||||
|
||||
def bulk_edit
|
||||
@available_activities = @projects.map(&:activities).reduce(:&)
|
||||
@target_projects = Project.allowed_to(:log_time).to_a
|
||||
@custom_fields = TimeEntry.first.available_custom_fields.select {|field| field.format.bulk_edit_supported}
|
||||
if params[:time_entry]
|
||||
@target_project = @target_projects.detect {|p| p.id.to_s == params[:time_entry][:project_id].to_s}
|
||||
end
|
||||
if @target_project
|
||||
@available_activities = @target_project.activities
|
||||
else
|
||||
@available_activities = @projects.map(&:activities).reduce(:&)
|
||||
end
|
||||
@time_entry_params = params[:time_entry] || {}
|
||||
@time_entry_params[:custom_field_values] ||= {}
|
||||
end
|
||||
|
||||
def bulk_update
|
||||
|
@ -230,7 +240,8 @@ class TimelogController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def find_time_entry
|
||||
@time_entry = TimeEntry.find(params[:id])
|
||||
@project = @time_entry.project
|
||||
|
@ -262,25 +273,18 @@ private
|
|||
if params[:issue_id].present?
|
||||
@issue = Issue.find(params[:issue_id])
|
||||
@project = @issue.project
|
||||
authorize
|
||||
else
|
||||
find_optional_project
|
||||
end
|
||||
end
|
||||
|
||||
def find_optional_project
|
||||
if params[:project_id].present?
|
||||
@project = Project.find(params[:project_id])
|
||||
end
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
render_404
|
||||
end
|
||||
|
||||
# Returns the TimeEntry scope for index and report actions
|
||||
def time_entry_scope(options={})
|
||||
@query.results_scope(options)
|
||||
end
|
||||
|
||||
def retrieve_time_entry_query
|
||||
retrieve_query(TimeEntryQuery, false)
|
||||
retrieve_query(TimeEntryQuery, false, :defaults => @default_columns_names)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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,7 +34,7 @@ class TrackersController < ApplicationController
|
|||
end
|
||||
|
||||
def new
|
||||
@tracker ||= Tracker.new
|
||||
@tracker ||= Tracker.new(:default_status => IssueStatus.sorted.first)
|
||||
@tracker.safe_attributes = params[:tracker]
|
||||
@trackers = Tracker.sorted.to_a
|
||||
@projects = Project.all
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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,7 +30,10 @@ class UsersController < ApplicationController
|
|||
include SortHelper
|
||||
helper :custom_fields
|
||||
include CustomFieldsHelper
|
||||
include UsersHelper
|
||||
helper :principal_memberships
|
||||
helper :activities
|
||||
include ActivitiesHelper
|
||||
|
||||
require_sudo_mode :create, :update, :destroy
|
||||
|
||||
|
@ -59,6 +64,9 @@ class UsersController < ApplicationController
|
|||
@groups = Group.givable.sort
|
||||
render :layout => !request.xhr?
|
||||
}
|
||||
format.csv {
|
||||
send_data(users_to_csv(scope.order(sort_clause)), :type => 'text/csv; header=present', :filename => 'users.csv')
|
||||
}
|
||||
format.api
|
||||
end
|
||||
end
|
||||
|
@ -72,6 +80,16 @@ class UsersController < ApplicationController
|
|||
# show projects based on current user visibility
|
||||
@memberships = @user.memberships.preload(:roles, :project).where(Project.visible_condition(User.current)).to_a
|
||||
|
||||
@issue_counts = {}
|
||||
@issue_counts[:assigned] = {
|
||||
:total => Issue.visible.assigned_to(@user).count,
|
||||
:open => Issue.visible.open.assigned_to(@user).count
|
||||
}
|
||||
@issue_counts[:reported] = {
|
||||
:total => Issue.visible.where(:author_id => @user.id).count,
|
||||
:open => Issue.visible.open.where(:author_id => @user.id).count
|
||||
}
|
||||
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
|
||||
|
@ -95,13 +113,13 @@ class UsersController < ApplicationController
|
|||
@user.pref.safe_attributes = params[:pref]
|
||||
|
||||
if @user.save
|
||||
Mailer.account_information(@user, @user.password).deliver if params[:send_information]
|
||||
Mailer.deliver_account_information(@user, @user.password) if params[:send_information]
|
||||
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
flash[:notice] = l(:notice_user_successful_create, :id => view_context.link_to(@user.login, user_path(@user)))
|
||||
if params[:continue]
|
||||
attrs = params[:user].slice(:generate_password)
|
||||
attrs = {:generate_password => @user.generate_password }
|
||||
redirect_to new_user_path(:user => attrs)
|
||||
else
|
||||
redirect_to edit_user_path(@user)
|
||||
|
@ -140,9 +158,9 @@ class UsersController < ApplicationController
|
|||
@user.pref.save
|
||||
|
||||
if was_activated
|
||||
Mailer.account_activated(@user).deliver
|
||||
Mailer.deliver_account_activated(@user)
|
||||
elsif @user.active? && params[:send_information] && @user != User.current
|
||||
Mailer.account_information(@user, @user.password).deliver
|
||||
Mailer.deliver_account_information(@user, @user.password)
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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 +36,7 @@ class VersionsController < ApplicationController
|
|||
@trackers = @project.trackers.sorted.to_a
|
||||
retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?})
|
||||
@with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
|
||||
project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
|
||||
project_ids = @with_subprojects ? @project.self_and_descendants.pluck(:id) : [@project.id]
|
||||
|
||||
@versions = @project.shared_versions.preload(:custom_values)
|
||||
@versions += @project.rolled_up_versions.visible.preload(:custom_values) if @with_subprojects
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -42,8 +44,6 @@ class WikiController < ApplicationController
|
|||
helper :watchers
|
||||
include Redmine::Export::PDF
|
||||
|
||||
include ActionView::Helpers::SanitizeHelper
|
||||
|
||||
# List of pages, sorted alphabetically and by parent (hierarchy)
|
||||
def index
|
||||
load_pages_for_index
|
||||
|
@ -72,7 +72,7 @@ class WikiController < ApplicationController
|
|||
@page.title = '' unless editable?
|
||||
@page.validate
|
||||
if @page.errors[:title].blank?
|
||||
path = project_wiki_page_path(@project, @page.title)
|
||||
path = project_wiki_page_path(@project, @page.title, :parent => params[:parent])
|
||||
respond_to do |format|
|
||||
format.html { redirect_to path }
|
||||
format.js { render :js => "window.location = #{path.to_json}" }
|
||||
|
@ -109,7 +109,7 @@ class WikiController < ApplicationController
|
|||
send_data(export, :type => 'text/html', :filename => filename_for_content_disposition("#{@page.title}.html"))
|
||||
return
|
||||
elsif params[:format] == 'txt'
|
||||
send_data(strip_tags(@content.text), :type => 'text/plain', :filename => filename_for_content_disposition("#{@page.title}.txt"))
|
||||
send_data(@content.text, :type => 'text/plain', :filename => filename_for_content_disposition("#{@page.title}.txt"))
|
||||
return
|
||||
end
|
||||
end
|
||||
|
@ -325,7 +325,7 @@ class WikiController < ApplicationController
|
|||
@attachments += page.attachments
|
||||
@previewed = page.content
|
||||
end
|
||||
@text = params[:content][:text]
|
||||
@text = params[:content].present? ? params[:content][:text] : params[:text]
|
||||
render :partial => 'common/preview'
|
||||
end
|
||||
|
||||
|
@ -336,7 +336,7 @@ class WikiController < ApplicationController
|
|||
redirect_to :action => 'show', :id => @page.title, :project_id => @project
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def find_wiki
|
||||
@project = Project.find(params[:project_id])
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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,13 +21,6 @@ class WikisController < ApplicationController
|
|||
menu_item :settings
|
||||
before_action :find_project, :authorize
|
||||
|
||||
# Create or update a project's wiki
|
||||
def edit
|
||||
@wiki = @project.wiki || Wiki.new(:project => @project)
|
||||
@wiki.safe_attributes = params[:wiki]
|
||||
@wiki.save if request.post?
|
||||
end
|
||||
|
||||
# Delete a project's wiki
|
||||
def destroy
|
||||
if request.post? && params[:confirm] && @project.wiki
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2019 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
|
||||
|
@ -118,7 +120,7 @@ class WorkflowsController < ApplicationController
|
|||
def find_roles
|
||||
ids = Array.wrap(params[:role_id])
|
||||
if ids == ['all']
|
||||
@roles = Role.sorted.to_a
|
||||
@roles = Role.sorted.select(&:consider_workflow?)
|
||||
elsif ids.present?
|
||||
@roles = Role.where(:id => ids).to_a
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue