Compare commits

..

31 Commits
2.1.1 ... 1.4.0

Author SHA1 Message Date
Jean-Philippe Lang
7578e485a5 tagged version 1.4.0
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/tags/1.4.0@9416 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-14 08:20:21 +00:00
Jean-Philippe Lang
a35b81b9fa Merged r9412 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9413 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-14 08:08:22 +00:00
Jean-Philippe Lang
fd450fd2da Merged r9404, r9405 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9411 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-14 06:54:02 +00:00
Jean-Philippe Lang
2c0ba78f70 Merged r9409 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9410 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-14 06:53:10 +00:00
Jean-Philippe Lang
f0f01d370e Merged r9406 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9408 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-14 06:32:18 +00:00
Jean-Philippe Lang
4c330a1241 Merged r9391 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9403 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-14 04:58:46 +00:00
Jean-Philippe Lang
baa4ebd05f Merged r9389 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9402 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-14 04:57:13 +00:00
Jean-Philippe Lang
59f14478ed Merged r9387 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9401 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-14 04:53:47 +00:00
Jean-Philippe Lang
8fefb7c05b Merged r9390 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9400 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-14 04:52:49 +00:00
Jean-Philippe Lang
1feb373c89 Merged r9378 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9399 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-14 04:51:49 +00:00
Jean-Philippe Lang
32fd503cbb Merged r9381 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9398 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-14 04:50:58 +00:00
Jean-Philippe Lang
cf31aeaf81 Merged r9380 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9397 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-14 04:45:44 +00:00
Jean-Philippe Lang
83ea66fd2c Merged r9379 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9395 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-14 04:43:41 +00:00
Jean-Philippe Lang
ef2c5cab2d Merged r9392 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9393 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-13 19:41:05 +00:00
Etienne Massip
dee6f6b138 Merged r9374 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9388 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-11 18:31:32 +00:00
Jean-Philippe Lang
a4c0c18e3d Merged r9384, r9385 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9386 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-11 17:27:44 +00:00
Jean-Philippe Lang
4c82fbb6f8 Merged r9382 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9383 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-11 17:21:35 +00:00
Jean-Philippe Lang
68ded50edc Merged r9372 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9377 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-09 06:28:04 +00:00
Jean-Philippe Lang
72ecb80dc7 Merged r9358 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9376 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-09 06:27:07 +00:00
Jean-Philippe Lang
86ee285eb4 Merged r9367 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9375 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-09 06:22:31 +00:00
Jean-Philippe Lang
c229ea6386 Merged r9371 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9373 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-08 21:01:20 +00:00
Jean-Philippe Lang
687fca170e Merged r9359 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9360 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-06 13:51:55 +00:00
Toshi MARUYAMA
26564b06f7 Merged r9355 from trunk
remove 1.3-stable merged issues from CHANGELOG 1.4.0 list

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9356 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-06 13:01:12 +00:00
Jean-Philippe Lang
34016c38bd Merged r9349 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9354 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-06 12:39:51 +00:00
Jean-Philippe Lang
15ff361894 Merged r9350 and r9351 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9352 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-06 12:23:07 +00:00
Jean-Philippe Lang
b45b5f4322 Merged r9346 and r9347 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9348 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-06 11:17:31 +00:00
Toshi MARUYAMA
8addbc537a Merged r9343 from trunk
Simplified Chinese translation updated by fangzheng (#10611)

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9344 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-06 10:45:24 +00:00
Jean-Philippe Lang
87eeacba80 Merged r9341 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9342 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-06 10:27:22 +00:00
Jean-Philippe Lang
a7250c41e2 Merged r9339 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9340 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-06 10:25:03 +00:00
Jean-Philippe Lang
7c45396d92 Set version to stable.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9338 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-06 09:45:43 +00:00
Jean-Philippe Lang
c1f98c835c Adds 1.4-stable branch.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.4-stable@9337 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-04-06 09:41:15 +00:00
1343 changed files with 29686 additions and 21998 deletions

1
.gitignore vendored
View File

@@ -5,7 +5,6 @@
/config/database.yml
/config/email.yml
/config/initializers/session_store.rb
/config/initializers/secret_token.rb
/coverage
/db/*.db
/db/*.sqlite3

View File

@@ -7,7 +7,6 @@ config/configuration.yml
config/database.yml
config/email.yml
config/initializers/session_store.rb
config/initializers/secret_token.rb
coverage
db/*.db
db/*.sqlite3

27
Gemfile
View File

@@ -1,11 +1,10 @@
source 'http://rubygems.org'
source :rubygems
gem 'rails', '3.2.8'
gem "jquery-rails", "~> 2.0.2"
gem "i18n", "~> 0.6.0"
gem "rails", "2.3.14"
gem "i18n", "~> 0.4.2"
gem "coderay", "~> 1.0.6"
gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby]
gem "builder", "3.0.0"
gem "tzinfo", "~> 0.3.31"
# Optional gem for LDAP authentication
group :ldap do
@@ -15,7 +14,6 @@ end
# Optional gem for OpenID authentication
group :openid do
gem "ruby-openid", "~> 2.1.4", :require => "openid"
gem "rack-openid"
end
# Optional gem for exporting the gantt to a PNG file, not supported with jruby
@@ -47,7 +45,7 @@ end
platforms :mri_19, :mingw_19 do
group :mysql do
gem "mysql2", "~> 0.3.11"
gem "mysql2", "~> 0.2.7"
end
end
@@ -69,24 +67,21 @@ end
group :development do
gem "rdoc", ">= 2.4.2"
gem "yard"
end
group :test do
gem "shoulda", "~> 2.11"
# Shoulda does not work nice on Ruby 1.9.3 and seems to need test-unit explicitely.
gem "test-unit", :platforms => [:mri_19]
gem "mocha", "0.12.3"
gem "shoulda", "~> 2.10.3"
gem "edavis10-object_daddy", :require => "object_daddy"
gem "mocha"
end
local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
if File.exists?(local_gemfile)
if File.exists?('Gemfile.local')
puts "Loading Gemfile.local ..." if $DEBUG # `ruby -d` or `bundle -v`
instance_eval File.read(local_gemfile)
instance_eval File.read('Gemfile.local')
end
# Load plugins' Gemfiles
Dir.glob File.expand_path("../plugins/*/Gemfile", __FILE__) do |file|
Dir.glob File.expand_path("../vendor/plugins/*/Gemfile", __FILE__) do |file|
puts "Loading #{file} ..." if $DEBUG # `ruby -d` or `bundle -v`
instance_eval File.read(file)
end

View File

@@ -1,7 +1,15 @@
#!/usr/bin/env rake
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
# for example lib/tasks/switchtower.rake, and they will automatically be available to Rake.
require File.expand_path('../config/application', __FILE__)
require(File.join(File.dirname(__FILE__), 'config', 'boot'))
RedmineApp::Application.load_tasks
require 'rake'
require 'rake/testtask'
begin
require 'rdoc/task'
rescue LoadError
# RDoc is not available
end
require 'tasks/rails'

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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
@@ -40,26 +40,19 @@ class AccountController < ApplicationController
redirect_to home_url
end
# Lets user choose a new password
# Enable user to choose a new password
def lost_password
redirect_to(home_url) && return unless Setting.lost_password?
if params[:token]
@token = Token.find_by_action_and_value("recovery", params[:token].to_s)
if @token.nil? || @token.expired?
redirect_to home_url
return
end
@token = Token.find_by_action_and_value("recovery", params[:token])
redirect_to(home_url) && return unless @token and !@token.expired?
@user = @token.user
unless @user && @user.active?
redirect_to home_url
return
end
if request.post?
@user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
if @user.save
@token.destroy
flash[:notice] = l(:notice_account_password_updated)
redirect_to signin_path
redirect_to :action => 'login'
return
end
end
@@ -67,23 +60,17 @@ class AccountController < ApplicationController
return
else
if request.post?
user = User.find_by_mail(params[:mail].to_s)
# user not found or not active
unless user && user.active?
flash.now[:error] = l(:notice_account_unknown_email)
return
end
# user cannot change its password
unless user.change_password_allowed?
flash.now[:error] = l(:notice_can_t_change_password)
return
end
user = User.find_by_mail(params[:mail])
# user not found in db
(flash.now[:error] = l(:notice_account_unknown_email); return) unless user
# user uses an external authentification
(flash.now[:error] = l(:notice_can_t_change_password); return) if user.auth_source_id
# create a new token for password recovery
token = Token.new(:user => user, :action => "recovery")
if token.save
Mailer.lost_password(token).deliver
Mailer.deliver_lost_password(token)
flash[:notice] = l(:notice_account_lost_email_sent)
redirect_to signin_path
redirect_to :action => 'login'
return
end
end
@@ -97,9 +84,8 @@ class AccountController < ApplicationController
session[:auth_source_registration] = nil
@user = User.new(:language => Setting.default_language)
else
user_params = params[:user] || {}
@user = User.new
@user.safe_attributes = user_params
@user.safe_attributes = params[:user]
@user.admin = false
@user.register
if session[:auth_source_registration]
@@ -114,9 +100,7 @@ class AccountController < ApplicationController
end
else
@user.login = params[:user][:login]
unless user_params[:identity_url].present? && user_params[:password].blank? && user_params[:password_confirmation].blank?
@user.password, @user.password_confirmation = user_params[:password], user_params[:password_confirmation]
end
@user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation]
case Setting.self_registration
when '1'
@@ -142,11 +126,19 @@ class AccountController < ApplicationController
token.destroy
flash[:notice] = l(:notice_account_activated)
end
redirect_to signin_path
redirect_to :action => 'login'
end
private
def logout_user
if User.current.logged?
cookies.delete :autologin
Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin'])
self.logged_user = nil
end
end
def authenticate_user
if Setting.openid? && using_open_id?
open_id_authenticate(params[:openid_url])
@@ -169,7 +161,7 @@ class AccountController < ApplicationController
end
def open_id_authenticate(openid_url)
authenticate_with_open_id(openid_url, :required => [:nickname, :fullname, :email], :return_to => signin_url, :method => :post) do |result, identity_url, registration|
authenticate_with_open_id(openid_url, :required => [:nickname, :fullname, :email], :return_to => signin_url) do |result, identity_url, registration|
if result.successful?
user = User.find_or_initialize_by_identity_url(identity_url)
if user.new_record?
@@ -210,7 +202,6 @@ class AccountController < ApplicationController
end
def successful_authentication(user)
logger.info "Successful authentication for '#{user.login}' from #{request.remote_ip} at #{Time.now.utc}"
# Valid user
self.logged_user = user
# generate a key and set cookie if autologin
@@ -252,9 +243,9 @@ 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(token)
flash[:notice] = l(:notice_account_register_done)
redirect_to signin_path
redirect_to :action => 'login'
else
yield if block_given?
end
@@ -282,7 +273,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
else
yield if block_given?
@@ -291,6 +282,6 @@ class AccountController < ApplicationController
def account_pending
flash[:notice] = l(:notice_account_pending)
redirect_to signin_path
redirect_to :action => 'login'
end
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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
@@ -40,10 +40,10 @@ class ActivitiesController < ApplicationController
events = @activity.events(@date_from, @date_to)
if events.empty? || stale?(:etag => [@activity.scope, @date_to, @date_from, @with_subprojects, @author, events.first, events.size, User.current, current_language])
if events.empty? || stale?(:etag => [@activity.scope, @date_to, @date_from, @with_subprojects, @author, events.first, User.current, current_language])
respond_to do |format|
format.html {
@events_by_day = events.group_by {|event| User.current.time_to_date(event.event_datetime)}
@events_by_day = events.group_by(&:event_date)
render :layout => false if request.xhr?
}
format.atom {

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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,7 +63,7 @@ class AdminController < ApplicationController
# 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
@test = Mailer.deliver_test_email(User.current)
flash[:notice] = l(:notice_email_sent, User.current.mail)
rescue Exception => e
flash[:error] = l(:notice_email_error, e.message)

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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,59 +22,63 @@ class Unauthorized < Exception; end
class ApplicationController < ActionController::Base
include Redmine::I18n
class_attribute :accept_api_auth_actions
class_attribute :accept_rss_auth_actions
class_attribute :model_object
layout 'base'
exempt_from_layout 'builder', 'rsb'
protect_from_forgery
def handle_unverified_request
super
cookies.delete(:autologin)
end
# Remove broken cookie after upgrade from 0.8.x (#4292)
# See https://rails.lighthouseapp.com/projects/8994/tickets/3360
# TODO: remove it when Rails is fixed
before_filter :delete_broken_cookies
def delete_broken_cookies
if cookies['_redmine_session'] && cookies['_redmine_session'] !~ /--/
cookies.delete '_redmine_session'
redirect_to home_path
return false
end
end
before_filter :session_expiration, :user_setup, :check_if_login_required, :set_localization
# FIXME: Remove this when all of Rack and Rails have learned how to
# properly use encodings
before_filter :params_filter
def params_filter
if RUBY_VERSION >= '1.9' && defined?(Rails) && Rails::VERSION::MAJOR < 3
self.utf8nize!(params)
end
end
def utf8nize!(obj)
if obj.frozen?
obj
elsif obj.is_a? String
obj.respond_to?(:force_encoding) ? obj.force_encoding("UTF-8") : obj
elsif obj.is_a? Hash
obj.each {|k, v| obj[k] = self.utf8nize!(v)}
elsif obj.is_a? Array
obj.each {|v| self.utf8nize!(v)}
else
obj
end
end
before_filter :user_setup, :check_if_login_required, :set_localization
filter_parameter_logging :password
rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
rescue_from ::Unauthorized, :with => :deny_access
rescue_from ::ActionView::MissingTemplate, :with => :missing_template
include Redmine::Search::Controller
include Redmine::MenuManager::MenuController
helper Redmine::MenuManager::MenuHelper
def session_expiration
if session[:user_id]
if session_expired? && !try_to_autologin
reset_session
flash[:error] = l(:error_session_expired)
redirect_to signin_url
else
session[:atime] = Time.now.utc.to_i
end
end
end
def session_expired?
if Setting.session_lifetime?
unless session[:ctime] && (Time.now.utc.to_i - session[:ctime].to_i <= Setting.session_lifetime.to_i * 60)
return true
end
end
if Setting.session_timeout?
unless session[:atime] && (Time.now.utc.to_i - session[:atime].to_i <= Setting.session_timeout.to_i * 60)
return true
end
end
false
end
def start_user_session(user)
session[:user_id] = user.id
session[:ctime] = Time.now.utc.to_i
session[:atime] = Time.now.utc.to_i
Redmine::Scm::Base.all.each do |scm|
require_dependency "repository/#{scm.underscore}"
end
def user_setup
@@ -82,48 +86,33 @@ class ApplicationController < ActionController::Base
Setting.check_cache
# Find the current user
User.current = find_current_user
logger.info(" Current user: " + (User.current.logged? ? "#{User.current.login} (id=#{User.current.id})" : "anonymous")) if logger
end
# Returns the current user or nil if no user is logged in
# and starts a session if needed
def find_current_user
user = nil
unless api_request?
if session[:user_id]
# existing session
user = (User.active.find(session[:user_id]) rescue nil)
elsif autologin_user = try_to_autologin
user = autologin_user
elsif params[:format] == 'atom' && params[:key] && request.get? && accept_rss_auth?
# RSS key authentication does not start a session
user = User.find_by_rss_key(params[:key])
end
end
if user.nil? && Setting.rest_api_enabled? && accept_api_auth?
if session[:user_id]
# existing session
(User.active.find(session[:user_id]) rescue nil)
elsif cookies[:autologin] && Setting.autologin?
# auto-login feature starts a new session
user = User.try_to_autologin(cookies[:autologin])
session[:user_id] = user.id if user
user
elsif params[:format] == 'atom' && params[:key] && request.get? && accept_rss_auth?
# RSS key authentication does not start a session
User.find_by_rss_key(params[:key])
elsif Setting.rest_api_enabled? && accept_api_auth?
if (key = api_key_from_request)
# Use API key
user = User.find_by_api_key(key)
User.find_by_api_key(key)
else
# 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)
User.try_to_login(username, password) || User.find_by_api_key(username)
end
end
end
user
end
def try_to_autologin
if cookies[:autologin] && Setting.autologin?
# auto-login feature starts a new session
user = User.try_to_autologin(cookies[:autologin])
if user
reset_session
start_user_session(user)
end
user
end
end
# Sets the logged in user
@@ -131,21 +120,12 @@ class ApplicationController < ActionController::Base
reset_session
if user && user.is_a?(User)
User.current = user
start_user_session(user)
session[:user_id] = user.id
else
User.current = User.anonymous
end
end
# Logs out current user
def logout_user
if User.current.logged?
cookies.delete :autologin
Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin'])
self.logged_user = nil
end
end
# check if login is globally required to access the application
def check_if_login_required
# no check needed if user is already logged in
@@ -253,7 +233,7 @@ class ApplicationController < ActionController::Base
end
def find_model_object
model = self.class.model_object
model = self.class.read_inheritable_attribute('model_object')
if model
@object = model.find(params[:id])
self.instance_variable_set('@' + controller_name.singularize, @object) if @object
@@ -263,7 +243,7 @@ class ApplicationController < ActionController::Base
end
def self.model_object(model)
self.model_object = model
write_inheritable_attribute('model_object', model)
end
# Filter for bulk issue operations
@@ -283,7 +263,7 @@ class ApplicationController < ActionController::Base
# make sure that the user is a member of the project (or admin) if project is private
# used as a before_filter for actions that do not require any particular permission on the project
def check_project_privacy
if @project && !@project.archived?
if @project && @project.active?
if @project.visible?
true
else
@@ -297,16 +277,12 @@ class ApplicationController < ActionController::Base
end
def back_url
url = params[:back_url]
if url.nil? && referer = request.env['HTTP_REFERER']
url = CGI.unescape(referer.to_s)
end
url
params[:back_url] || request.env['HTTP_REFERER']
end
def redirect_back_or_default(default)
back_url = params[:back_url].to_s
if back_url.present?
back_url = CGI.unescape(params[:back_url].to_s)
if !back_url.blank?
begin
uri = URI.parse(back_url)
# do not redirect user to another host or to the login or register page
@@ -315,7 +291,6 @@ class ApplicationController < ActionController::Base
return
end
rescue URI::InvalidURIError
logger.warn("Could not redirect to invalid URL #{back_url}")
# redirect to default
end
end
@@ -323,19 +298,6 @@ class ApplicationController < ActionController::Base
false
end
# Redirects to the request referer if present, redirects to args or call block otherwise.
def redirect_to_referer_or(*args, &block)
redirect_to :back
rescue ::ActionController::RedirectBackError
if args.any?
redirect_to *args
elsif block_given?
block.call
else
raise "#redirect_to_referer_or takes arguments or a block"
end
end
def render_403(options={})
@project = nil
render_error({:message => :notice_not_authorized, :status => 403}.merge(options))
@@ -359,17 +321,13 @@ class ApplicationController < ActionController::Base
format.html {
render :template => 'common/error', :layout => use_layout, :status => @status
}
format.any { head @status }
format.atom { head @status }
format.xml { head @status }
format.js { head @status }
format.json { head @status }
end
end
# Handler for ActionView::MissingTemplate exception
def missing_template
logger.warn "Missing template, responding with 404"
@project = nil
render_404
end
# Filter for actions that provide an API response
# but have no HTML representation for non admin users
def require_admin_or_api_request
@@ -408,9 +366,9 @@ class ApplicationController < ActionController::Base
def self.accept_rss_auth(*actions)
if actions.any?
self.accept_rss_auth_actions = actions
write_inheritable_attribute('accept_rss_auth_actions', actions)
else
self.accept_rss_auth_actions || []
read_inheritable_attribute('accept_rss_auth_actions') || []
end
end
@@ -420,9 +378,9 @@ class ApplicationController < ActionController::Base
def self.accept_api_auth(*actions)
if actions.any?
self.accept_api_auth_actions = actions
write_inheritable_attribute('accept_api_auth_actions', actions)
else
self.accept_api_auth_actions || []
read_inheritable_attribute('accept_api_auth_actions') || []
end
end
@@ -502,9 +460,9 @@ class ApplicationController < ActionController::Base
# Returns the API key present in the request
def api_key_from_request
if params[:key].present?
params[:key].to_s
params[:key]
elsif request.headers["X-Redmine-API-Key"].present?
request.headers["X-Redmine-API-Key"].to_s
request.headers["X-Redmine-API-Key"]
end
end
@@ -536,12 +494,6 @@ 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 successfull updates or deletions via the API
def render_api_ok
# head :ok would return a response body with one space
render :text => '', :status => :ok, :layout => nil
end
# Renders API response on validation failure
def render_validation_errors(objects)
if objects.is_a?(Array)
@@ -549,12 +501,26 @@ class ApplicationController < ActionController::Base
else
@error_messages = objects.errors.full_messages
end
render :template => 'common/error_messages.api', :status => :unprocessable_entity, :layout => nil
render :template => 'common/error_messages.api', :status => :unprocessable_entity, :layout => false
end
# Overrides #_include_layout? so that #render with no arguments
# Overrides #default_template so that the api template
# is used automatically if it exists
def default_template(action_name = self.action_name)
if api_request?
begin
return self.view_paths.find_template(default_template_name(action_name), 'api')
rescue ::ActionView::MissingTemplate
# the api template was not found
# fallback to the default behaviour
end
end
super
end
# Overrides #pick_layout so that #render with no arguments
# doesn't use the layout for api requests
def _include_layout?(*args)
api_request? ? false : super
def pick_layout(*args)
api_request? ? nil : super
end
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,7 +17,7 @@
class AttachmentsController < ApplicationController
before_filter :find_project, :except => :upload
before_filter :file_readable, :read_authorize, :only => [:show, :download, :thumbnail]
before_filter :file_readable, :read_authorize, :only => [:show, :download]
before_filter :delete_authorize, :only => :destroy
before_filter :authorize_global, :only => :upload
@@ -52,26 +52,11 @@ class AttachmentsController < ApplicationController
@attachment.increment_download
end
if stale?(:etag => @attachment.digest)
# images are sent inline
send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
:type => detect_content_type(@attachment),
:disposition => (@attachment.image? ? 'inline' : 'attachment')
end
end
# images are sent inline
send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
:type => detect_content_type(@attachment),
:disposition => (@attachment.image? ? 'inline' : 'attachment')
def thumbnail
if @attachment.thumbnailable? && thumbnail = @attachment.thumbnail(:size => params[:size])
if stale?(:etag => thumbnail)
send_file thumbnail,
:filename => filename_for_content_disposition(@attachment.filename),
:type => detect_content_type(@attachment),
:disposition => 'inline'
end
else
# No thumbnail for the attachment or thumbnail could not be created
render :nothing => true, :status => 404
end
end
def upload
@@ -82,7 +67,7 @@ class AttachmentsController < ApplicationController
return
end
@attachment = Attachment.new(:file => request.raw_post)
@attachment = Attachment.new(:file => request.body)
@attachment.author = User.current
@attachment.filename = Redmine::Utils.random_hex(16)
@@ -103,7 +88,9 @@ class AttachmentsController < ApplicationController
end
# Make sure association callbacks are called
@attachment.container.attachments.delete(@attachment)
redirect_to_referer_or project_path(@project)
redirect_to :back
rescue ::ActionController::RedirectBackError
redirect_to :controller => 'projects', :action => 'show', :id => @project
end
private

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,44 +1,27 @@
# Redmine - project management software
# Copyright (C) 2006-2012 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
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class AutoCompletesController < ApplicationController
before_filter :find_project
def issues
@issues = []
q = (params[:q] || params[:term]).to_s.strip
if q.present?
scope = (params[:scope] == "all" || @project.nil? ? Issue : @project.issues).visible
if q.match(/^\d+$/)
@issues << scope.find_by_id(q.to_i)
end
@issues += scope.where("LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%").order("#{Issue.table_name}.id DESC").limit(10).all
@issues.compact!
q = params[:q].to_s
query = (params[:scope] == "all" && Setting.cross_project_issue_relations?) ? Issue : @project.issues
if q.match(/^\d+$/)
@issues << query.visible.find_by_id(q.to_i)
end
unless q.blank?
@issues += query.visible.find(:all, :conditions => ["LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%"], :limit => 10)
end
@issues.compact!
render :layout => false
end
private
def find_project
if params[:project_id].present?
@project = Project.find(params[:project_id])
end
project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id]
@project = Project.find(project_id)
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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 +25,7 @@ class BoardsController < ApplicationController
helper :watchers
def index
@boards = @project.boards.includes(:last_message => :author).all
@boards = @project.boards
# show the board if there is only one
if @boards.size == 1
@board = @boards.first
@@ -43,10 +43,10 @@ class BoardsController < ApplicationController
@topic_count = @board.topics.count
@topic_pages = Paginator.new self, @topic_count, per_page_option, params['page']
@topics = @board.topics.reorder("#{Message.table_name}.sticky DESC").order(sort_clause).all(
@topics = @board.topics.find :all, :order => ["#{Message.table_name}.sticky DESC", sort_clause].compact.join(', '),
:include => [:author, {:last_reply => :author}],
:limit => @topic_pages.items_per_page,
:offset => @topic_pages.current.offset)
:offset => @topic_pages.current.offset
@message = Message.new(:board => @board)
render :action => 'show', :layout => !request.xhr?
}

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,20 +1,3 @@
# Redmine - project management software
# Copyright (C) 2006-2012 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
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class ContextMenusController < ApplicationController
helper :watchers
helper :issues
@@ -24,7 +7,6 @@ class ContextMenusController < ApplicationController
if (@issues.size == 1)
@issue = @issues.first
end
@issue_ids = @issues.map(&:id).sort
@allowed_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
@projects = @issues.collect(&:project).compact.uniq
@@ -49,7 +31,6 @@ class ContextMenusController < ApplicationController
@assignables = @projects.map(&:assignable_users).reduce(:&)
@trackers = @projects.map(&:trackers).reduce(:&)
end
@versions = @projects.map {|p| p.shared_versions.open}.reduce(:&)
@priorities = IssuePriority.active.reverse
@back = back_url
@@ -67,7 +48,6 @@ class ContextMenusController < ApplicationController
end
end
@safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
render :layout => false
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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
@@ -86,9 +86,7 @@ class DocumentsController < ApplicationController
attachments = Attachment.attach_files(@document, params[:attachments])
render_attachment_warning_if_needed(@document)
if attachments.present? && attachments[:files].present? && Setting.notified_events.include?('document_added')
Mailer.attachments_added(attachments[:files]).deliver
end
Mailer.deliver_attachments_added(attachments[:files]) if attachments.present? && attachments[:files].present? && Setting.notified_events.include?('document_added')
redirect_to :action => 'show', :id => @document
end
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,20 +1,3 @@
# Redmine - project management software
# Copyright (C) 2006-2012 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
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class FilesController < ApplicationController
menu_item :files
@@ -46,7 +29,7 @@ class FilesController < ApplicationController
render_attachment_warning_if_needed(container)
if !attachments.empty? && !attachments[:files].blank? && Setting.notified_events.include?('file_added')
Mailer.attachments_added(attachments[:files]).deliver
Mailer.deliver_attachments_added(attachments[:files])
end
redirect_to project_files_path(@project)
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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,34 +19,51 @@ class GroupsController < ApplicationController
layout 'admin'
before_filter :require_admin
before_filter :find_group, :except => [:index, :new, :create]
accept_api_auth :index, :show, :create, :update, :destroy, :add_users, :remove_user
helper :custom_fields
# GET /groups
# GET /groups.xml
def index
@groups = Group.sorted.all
@groups = Group.find(:all, :order => 'lastname')
respond_to do |format|
format.html
format.api
format.html # index.html.erb
format.xml { render :xml => @groups }
end
end
# GET /groups/1
# GET /groups/1.xml
def show
@group = Group.find(params[:id])
respond_to do |format|
format.html
format.api
format.html # show.html.erb
format.xml { render :xml => @group }
end
end
# GET /groups/new
# GET /groups/new.xml
def new
@group = Group.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @group }
end
end
# GET /groups/1/edit
def edit
@group = Group.find(params[:id], :include => :projects)
end
# POST /groups
# POST /groups.xml
def create
@group = Group.new
@group.safe_attributes = params[:group]
@group = Group.new(params[:group])
respond_to do |format|
if @group.save
@@ -54,87 +71,102 @@ class GroupsController < ApplicationController
flash[:notice] = l(:notice_successful_create)
redirect_to(params[:continue] ? new_group_path : groups_path)
}
format.api { render :action => 'show', :status => :created, :location => group_url(@group) }
format.xml { render :xml => @group, :status => :created, :location => @group }
else
format.html { render :action => "new" }
format.api { render_validation_errors(@group) }
format.xml { render :xml => @group.errors, :status => :unprocessable_entity }
end
end
end
def edit
end
# PUT /groups/1
# PUT /groups/1.xml
def update
@group.safe_attributes = params[:group]
@group = Group.find(params[:id])
respond_to do |format|
if @group.save
if @group.update_attributes(params[:group])
flash[:notice] = l(:notice_successful_update)
format.html { redirect_to(groups_path) }
format.api { render_api_ok }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.api { render_validation_errors(@group) }
format.xml { render :xml => @group.errors, :status => :unprocessable_entity }
end
end
end
# DELETE /groups/1
# DELETE /groups/1.xml
def destroy
@group = Group.find(params[:id])
@group.destroy
respond_to do |format|
format.html { redirect_to(groups_url) }
format.api { render_api_ok }
format.xml { head :ok }
end
end
def add_users
@users = User.find_all_by_id(params[:user_id] || params[:user_ids])
@group.users << @users if request.post?
@group = Group.find(params[:id])
users = User.find_all_by_id(params[:user_ids])
@group.users << users if request.post?
respond_to do |format|
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'users' }
format.js
format.api { render_api_ok }
format.js {
render(:update) {|page|
page.replace_html "tab-content-users", :partial => 'groups/users'
users.each {|user| page.visual_effect(:highlight, "user-#{user.id}") }
}
}
end
end
def remove_user
@group = Group.find(params[:id])
@group.users.delete(User.find(params[:user_id])) if request.delete?
respond_to do |format|
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'users' }
format.js
format.api { render_api_ok }
format.js { render(:update) {|page| page.replace_html "tab-content-users", :partial => 'groups/users'} }
end
end
def autocomplete_for_user
@group = Group.find(params[:id])
@users = User.active.not_in_group(@group).like(params[:q]).all(:limit => 100)
render :layout => false
end
def edit_membership
@group = Group.find(params[:id])
@membership = Member.edit_membership(params[:membership_id], params[:membership], @group)
@membership.save if request.post?
respond_to do |format|
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
format.js
if @membership.valid?
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
format.js {
render(:update) {|page|
page.replace_html "tab-content-memberships", :partial => 'groups/memberships'
page.visual_effect(:highlight, "member-#{@membership.id}")
}
}
else
format.js {
render(:update) {|page|
page.alert(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', ')))
}
}
end
end
end
def destroy_membership
@group = Group.find(params[:id])
Member.find(params[:membership_id]).destroy if request.post?
respond_to do |format|
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
format.js
format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'groups/memberships'} }
end
end
private
def find_group
@group = Group.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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
@@ -41,11 +41,6 @@ class IssueCategoriesController < ApplicationController
def new
@category = @project.issue_categories.build
@category.safe_attributes = params[:issue_category]
respond_to do |format|
format.html
format.js
end
end
def create
@@ -57,13 +52,20 @@ class IssueCategoriesController < ApplicationController
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project
end
format.js
format.js do
# IE doesn't support the replace_html rjs method for select box options
render(:update) {|page| page.replace "issue_category_id",
content_tag('select', content_tag('option') + options_from_collection_for_select(@project.issue_categories, 'id', 'name', @category.id), :id => 'issue_category_id', :name => 'issue[category_id]')
}
end
format.api { render :action => 'show', :status => :created, :location => issue_category_path(@category) }
end
else
respond_to do |format|
format.html { render :action => 'new'}
format.js { render :action => 'new'}
format.js do
render(:update) {|page| page.alert(@category.errors.full_messages.join('\n')) }
end
format.api { render_validation_errors(@category) }
end
end
@@ -80,7 +82,7 @@ class IssueCategoriesController < ApplicationController
flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project
}
format.api { render_api_ok }
format.api { head :ok }
end
else
respond_to do |format|
@@ -100,7 +102,7 @@ class IssueCategoriesController < ApplicationController
@category.destroy(reassign_to)
respond_to do |format|
format.html { redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'categories' }
format.api { render_api_ok }
format.api { head :ok }
end
return
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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
@@ -49,9 +49,16 @@ class IssueRelationsController < ApplicationController
respond_to do |format|
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @issue }
format.js {
format.js do
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
}
render :update do |page|
page.replace_html "relations", :partial => 'issues/relations'
if @relation.errors.empty?
page << "$('relation_delay').value = ''"
page << "$('relation_issue_to_id').value = ''"
end
end
end
format.api {
if saved
render :action => 'show', :status => :created, :location => relation_url(@relation)
@@ -68,8 +75,8 @@ class IssueRelationsController < ApplicationController
respond_to do |format|
format.html { redirect_to issue_path } # TODO : does this really work since @issue is always nil? What is it useful to?
format.js
format.api { render_api_ok }
format.js { render(:update) {|page| page.remove "relation-#{@relation.id}"} }
format.api { head :ok }
end
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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
@@ -50,6 +50,7 @@ class IssuesController < ApplicationController
include SortHelper
include IssuesHelper
helper :timelog
helper :gantt
include Redmine::Export::PDF
def index
@@ -127,7 +128,17 @@ class IssuesController < ApplicationController
def new
respond_to do |format|
format.html { render :action => 'new', :layout => !request.xhr? }
format.js { render :partial => 'update_form' }
format.js {
render(:update) { |page|
if params[:project_change]
page.replace_html 'all_attributes', :partial => 'form'
else
page.replace_html 'attributes', :partial => 'attributes'
end
m = User.current.allowed_to?(:log_time, @issue.project) ? 'show' : 'hide'
page << "if ($('log_time')) {Element.#{m}('log_time');}"
}
}
end
end
@@ -139,7 +150,7 @@ class IssuesController < ApplicationController
respond_to do |format|
format.html {
render_attachment_warning_if_needed(@issue)
flash[:notice] = l(:notice_issue_successful_create, :id => view_context.link_to("##{@issue.id}", issue_path(@issue), :title => @issue.subject))
flash[:notice] = l(:notice_issue_successful_create, :id => "<a href='#{issue_path(@issue)}'>##{@issue.id}</a>")
redirect_to(params[:continue] ? { :action => 'new', :project_id => @issue.project, :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
{ :action => 'show', :id => @issue })
}
@@ -172,7 +183,12 @@ class IssuesController < ApplicationController
rescue ActiveRecord::StaleObjectError
@conflict = true
if params[:last_journal_id]
@conflict_journals = @issue.journals_after(params[:last_journal_id]).all
if params[:last_journal_id].present?
last_journal_id = params[:last_journal_id].to_i
@conflict_journals = @issue.journals.all(:conditions => ["#{Journal.table_name}.id > ?", last_journal_id])
else
@conflict_journals = @issue.journals.all
end
end
end
@@ -182,7 +198,7 @@ class IssuesController < ApplicationController
respond_to do |format|
format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
format.api { render_api_ok }
format.api { head :ok }
end
else
respond_to do |format|
@@ -221,7 +237,6 @@ class IssuesController < ApplicationController
@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?
end
@safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
@@ -235,20 +250,10 @@ class IssuesController < ApplicationController
unsaved_issue_ids = []
moved_issues = []
if @copy && params[:copy_subtasks].present?
# Descendant issues will be copied with the parent task
# Don't copy them twice
@issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}}
end
@issues.each do |issue|
issue.reload
if @copy
issue = issue.copy({},
:attachments => params[:copy_attachments].present?,
:subtasks => params[:copy_subtasks].present?
)
issue = issue.copy({}, :attachments => params[:copy_attachments].present?)
end
journal = issue.init_journal(User.current, params[:notes])
issue.safe_attributes = attributes
@@ -303,7 +308,7 @@ class IssuesController < ApplicationController
end
respond_to do |format|
format.html { redirect_back_or_default(:action => 'index', :project_id => @project) }
format.api { render_api_ok }
format.api { head :ok }
end
end
@@ -385,8 +390,7 @@ private
begin
@copy_from = Issue.visible.find(params[:copy_from])
@copy_attachments = params[:copy_attachments].present? || request.get?
@copy_subtasks = params[:copy_subtasks].present? || request.get?
@issue.copy_from(@copy_from, :attachments => @copy_attachments, :subtasks => @copy_subtasks)
@issue.copy_from(@copy_from, :attachments => @copy_attachments)
rescue ActiveRecord::RecordNotFound
render_404
return
@@ -398,7 +402,7 @@ private
end
@issue.project = @project
@issue.author ||= User.current
@issue.author = User.current
# Tracker must be set before custom field values
@issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
if @issue.tracker.nil?

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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
@@ -67,8 +67,16 @@ class JournalsController < ApplicationController
end
# Replaces pre blocks with [...]
text = text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]')
@content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> "
@content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> "
content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
render(:update) { |page|
page.<< "$('notes').value = \"#{escape_javascript content}\";"
page.show 'update'
page << "Form.Element.focus('notes');"
page << "Element.scrollTo('update');"
page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;"
}
end
def edit

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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,16 +63,31 @@ class MembersController < ApplicationController
end
respond_to do |format|
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
format.js { @members = members }
format.api {
@member = members.first
if @member.valid?
if members.present? && members.all? {|m| m.valid? }
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
format.js {
render(:update) {|page|
page.replace_html "tab-content-members", :partial => 'projects/settings/members'
page << 'hideOnLoad()'
members.each {|member| page.visual_effect(:highlight, "member-#{member.id}") }
}
}
format.api {
@member = members.first
render :action => 'show', :status => :created, :location => membership_url(@member)
else
render_validation_errors(@member)
end
}
}
else
format.js {
render(:update) {|page|
errors = members.collect {|m|
m.errors.full_messages
}.flatten.uniq
page.alert(l(:notice_failed_to_save_members, :errors => errors.join(', ')))
}
}
format.api { render_validation_errors(members.first) }
end
end
end
@@ -83,10 +98,16 @@ class MembersController < ApplicationController
saved = @member.save
respond_to do |format|
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
format.js
format.js {
render(:update) {|page|
page.replace_html "tab-content-members", :partial => 'projects/settings/members'
page << 'hideOnLoad()'
page.visual_effect(:highlight, "member-#{@member.id}")
}
}
format.api {
if saved
render_api_ok
head :ok
else
render_validation_errors(@member)
end
@@ -100,10 +121,14 @@ class MembersController < ApplicationController
end
respond_to do |format|
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
format.js
format.js { render(:update) {|page|
page.replace_html "tab-content-members", :partial => 'projects/settings/members'
page << 'hideOnLoad()'
}
}
format.api {
if @member.destroyed?
render_api_ok
head :ok
else
head :unprocessable_entity
end
@@ -115,4 +140,5 @@ class MembersController < ApplicationController
@principals = Principal.active.not_member_of(@project).like(params[:q]).all(:limit => 100)
render :layout => false
end
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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 +22,6 @@ class MessagesController < ApplicationController
before_filter :find_message, :except => [:new, :preview]
before_filter :authorize, :except => [:preview, :edit, :destroy]
helper :boards
helper :watchers
helper :attachments
include AttachmentsHelper
@@ -60,7 +59,7 @@ class MessagesController < ApplicationController
if @message.save
call_hook(:controller_messages_new_after_save, { :params => params, :message => @message})
render_attachment_warning_if_needed(@message)
redirect_to board_message_path(@board, @message)
redirect_to :action => 'show', :id => @message
end
end
end
@@ -77,7 +76,7 @@ class MessagesController < ApplicationController
attachments = Attachment.attach_files(@reply, params[:attachments])
render_attachment_warning_if_needed(@reply)
end
redirect_to board_message_path(@board, @topic, :r => @reply)
redirect_to :action => 'show', :id => @topic, :r => @reply
end
# Edit a message
@@ -89,35 +88,40 @@ class MessagesController < ApplicationController
render_attachment_warning_if_needed(@message)
flash[:notice] = l(:notice_successful_update)
@message.reload
redirect_to board_message_path(@message.board, @message.root, :r => (@message.parent_id && @message.id))
redirect_to :action => 'show', :board_id => @message.board, :id => @message.root, :r => (@message.parent_id && @message.id)
end
end
# Delete a messages
def destroy
(render_403; return false) unless @message.destroyable_by?(User.current)
r = @message.to_param
@message.destroy
if @message.parent
redirect_to board_message_path(@board, @message.parent, :r => r)
else
redirect_to project_board_path(@project, @board)
end
redirect_to @message.parent.nil? ?
{ :controller => 'boards', :action => 'show', :project_id => @project, :id => @board } :
{ :action => 'show', :id => @message.parent, :r => @message }
end
def quote
@subject = @message.subject
@subject = "RE: #{@subject}" unless @subject.starts_with?('RE:')
@content = "#{ll(Setting.default_language, :text_user_wrote, @message.author)}\n> "
@content << @message.content.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]').gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
user = @message.author
text = @message.content
subject = @message.subject.gsub('"', '\"')
subject = "RE: #{subject}" unless subject.starts_with?('RE:')
content = "#{ll(Setting.default_language, :text_user_wrote, user)}\\n> "
content << text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]').gsub('"', '\"').gsub(/(\r?\n|\r\n?)/, "\\n> ") + "\\n\\n"
render(:update) { |page|
page << "$('message_subject').value = \"#{subject}\";"
page.<< "$('message_content').value = \"#{content}\";"
page.show 'reply'
page << "Form.Element.focus('message_content');"
page << "Element.scrollTo('reply');"
page << "$('message_content').scrollTop = $('message_content').scrollHeight - $('message_content').clientHeight;"
}
end
def preview
message = @board.messages.find_by_id(params[:id])
@attachements = message.attachments if message
@text = (params[:message] || params[:reply])[:content]
@previewed = message
render :partial => 'common/preview'
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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
@@ -65,24 +65,6 @@ class MyController < ApplicationController
end
end
# Destroys user's account
def destroy
@user = User.current
unless @user.own_account_deletable?
redirect_to :action => 'account'
return
end
if request.post? && params[:confirm]
@user.destroy
if @user.destroyed?
logout_user
flash[:notice] = l(:notice_account_deleted)
end
redirect_to home_path
end
end
# Manage user's password
def password
@user = User.current
@@ -135,11 +117,7 @@ class MyController < ApplicationController
@user = User.current
@blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT.dup
@block_options = []
BLOCKS.each do |k, v|
unless %w(top left right).detect {|f| (@blocks[f] ||= []).include?(k)}
@block_options << [l("my.blocks.#{v}", :default => [v, v.to_s.humanize]), k.dasherize]
end
end
BLOCKS.each {|k, v| @block_options << [l("my.blocks.#{v}", :default => [v, v.to_s.humanize]), k.dasherize]}
end
# Add a block to user's page
@@ -156,7 +134,7 @@ class MyController < ApplicationController
layout['top'].unshift block
@user.pref[:my_page_layout] = layout
@user.pref.save
redirect_to :action => 'page_layout'
render :partial => "block", :locals => {:user => @user, :block_name => block}
end
# Remove a block to user's page
@@ -169,7 +147,7 @@ class MyController < ApplicationController
%w(top left right).each {|f| (layout[f] ||= []).delete block }
@user.pref[:my_page_layout] = layout
@user.pref.save
redirect_to :action => 'page_layout'
render :nothing => true
end
# Change blocks order on user's page
@@ -179,8 +157,7 @@ class MyController < ApplicationController
group = params[:group]
@user = User.current
if group.is_a?(String)
group_items = (params["blocks"] || []).collect(&:underscore)
group_items.each {|s| s.sub!(/^block_/, '')}
group_items = (params["list-#{group}"] || []).collect(&:underscore)
if group_items and group_items.is_a? Array
layout = @user.pref[:my_page_layout] || {}
# remove group blocks if they are presents in other groups

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,20 +1,3 @@
# Redmine - project management software
# Copyright (C) 2006-2012 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
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class ProjectEnumerationsController < ApplicationController
before_filter :find_project_by_project_id
before_filter :authorize

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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
@@ -48,11 +48,7 @@ class ProjectsController < ApplicationController
def index
respond_to do |format|
format.html {
scope = Project
unless params[:closed]
scope = scope.active
end
@projects = scope.visible.order('lft').all
@projects = Project.visible.find(:all, :order => 'lft')
}
format.api {
@offset, @limit = api_offset_and_limit
@@ -69,14 +65,14 @@ class ProjectsController < ApplicationController
def new
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.sorted.all
@trackers = Tracker.all
@project = Project.new
@project.safe_attributes = params[:project]
end
def create
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.sorted.all
@trackers = Tracker.all
@project = Project.new
@project.safe_attributes = params[:project]
@@ -109,7 +105,7 @@ class ProjectsController < ApplicationController
def copy
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.sorted.all
@trackers = Tracker.all
@root_projects = Project.find(:all,
:conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}",
:order => 'name')
@@ -156,8 +152,12 @@ class ProjectsController < ApplicationController
cond = @project.project_condition(Setting.display_subprojects_issues?)
@open_issues_by_tracker = Issue.visible.open.where(cond).count(:group => :tracker)
@total_issues_by_tracker = Issue.visible.where(cond).count(:group => :tracker)
@open_issues_by_tracker = Issue.visible.count(:group => :tracker,
:include => [:project, :status, :tracker],
:conditions => ["(#{cond}) AND #{IssueStatus.table_name}.is_closed=?", false])
@total_issues_by_tracker = Issue.visible.count(:group => :tracker,
:include => [:project, :status, :tracker],
:conditions => cond)
if User.current.allowed_to?(:view_time_entries, @project)
@total_hours = TimeEntry.visible.sum(:hours, :include => :project, :conditions => cond).to_f
@@ -175,7 +175,7 @@ class ProjectsController < ApplicationController
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@issue_category ||= IssueCategory.new
@member ||= @project.members.new
@trackers = Tracker.sorted.all
@trackers = Tracker.all
@wiki ||= @project.wiki
end
@@ -191,7 +191,7 @@ class ProjectsController < ApplicationController
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'settings', :id => @project
}
format.api { render_api_ok }
format.api { head :ok }
end
else
respond_to do |format|
@@ -224,16 +224,6 @@ class ProjectsController < ApplicationController
redirect_to(url_for(:controller => 'admin', :action => 'projects', :status => params[:status]))
end
def close
@project.close
redirect_to project_path(@project)
end
def reopen
@project.reopen
redirect_to project_path(@project)
end
# Delete @project
def destroy
@project_to_destroy = @project
@@ -241,7 +231,7 @@ class ProjectsController < ApplicationController
@project_to_destroy.destroy
respond_to do |format|
format.html { redirect_to :controller => 'admin', :action => 'projects' }
format.api { render_api_ok }
format.api { head :ok }
end
end
# hide project in layout

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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 +22,7 @@ class ReportsController < ApplicationController
def issue_report
@trackers = @project.trackers
@versions = @project.shared_versions.sort
@priorities = IssuePriority.all.reverse
@priorities = IssuePriority.all
@categories = @project.issue_categories
@assignees = (Setting.issue_group_assignment? ? @project.principals : @project.users).sort
@authors = @project.users.sort
@@ -53,7 +53,7 @@ class ReportsController < ApplicationController
@report_title = l(:field_version)
when "priority"
@field = "priority_id"
@rows = IssuePriority.all.reverse
@rows = IssuePriority.all
@data = Issue.by_priority(@project)
@report_title = l(:field_priority)
when "category"

View File

@@ -18,7 +18,6 @@
require 'SVG/Graph/Bar'
require 'SVG/Graph/BarHorizontal'
require 'digest/sha1'
require 'redmine/scm/adapters/abstract_adapter'
class ChangesetNotFound < Exception; end
class InvalidRevisionParam < Exception; end
@@ -42,15 +41,11 @@ class RepositoriesController < ApplicationController
@repository = Repository.factory(scm)
@repository.is_default = @project.repository.nil?
@repository.project = @project
render :layout => !request.xhr?
end
def create
attrs = pickup_extra_info
@repository = Repository.factory(params[:repository_scm])
@repository.safe_attributes = params[:repository]
if attrs[:attrs_extra].keys.any?
@repository.merge_extra_info(attrs[:attrs_extra])
end
@repository = Repository.factory(params[:repository_scm], params[:repository])
@repository.project = @project
if request.post? && @repository.save
redirect_to settings_project_path(@project, :tab => 'repositories')
@@ -63,11 +58,7 @@ class RepositoriesController < ApplicationController
end
def update
attrs = pickup_extra_info
@repository.safe_attributes = attrs[:attrs]
if attrs[:attrs_extra].keys.any?
@repository.merge_extra_info(attrs[:attrs_extra])
end
@repository.attributes = params[:repository]
@repository.project = @project
if request.put? && @repository.save
redirect_to settings_project_path(@project, :tab => 'repositories')
@@ -76,20 +67,6 @@ class RepositoriesController < ApplicationController
end
end
def pickup_extra_info
p = {}
p_extra = {}
params[:repository].each do |k, v|
if k =~ /^extra_/
p_extra[k] = v
else
p[k] = v
end
end
{:attrs => p, :attrs_extra => p_extra}
end
private :pickup_extra_info
def committers
@committers = @repository.committers
@users = @project.users
@@ -152,15 +129,7 @@ class RepositoriesController < ApplicationController
end
end
def raw
entry_and_raw(true)
end
def entry
entry_and_raw(false)
end
def entry_and_raw(is_raw)
@entry = @repository.entry(@path, @rev)
(show_error_not_found; return) unless @entry
@@ -169,14 +138,13 @@ class RepositoriesController < ApplicationController
@content = @repository.cat(@path, @rev)
(show_error_not_found; return) unless @content
if is_raw ||
if 'raw' == params[:format] ||
(@content.size && @content.size > Setting.file_max_size_displayed.to_i.kilobyte) ||
! is_entry_text_data?(@content, @path)
# Force the download
send_opt = { :filename => filename_for_content_disposition(@path.split('/').last) }
send_type = Redmine::MimeType.of(@path)
send_opt[:type] = send_type.to_s if send_type
send_opt[:disposition] = (Redmine::MimeType.is_type?('image', @path) && !is_raw ? 'inline' : 'attachment')
send_data @content, send_opt
else
# Prevent empty lines when displaying a file with Windows style eol
@@ -186,7 +154,6 @@ class RepositoriesController < ApplicationController
@changeset = @repository.find_changeset_by_name(@rev)
end
end
private :entry_and_raw
def is_entry_text_data?(ent, path)
# UTF-16 contains "\x00".
@@ -235,6 +202,22 @@ class RepositoriesController < ApplicationController
if @issue
@changeset.issues << @issue
respond_to do |format|
format.js {
render :update do |page|
page.replace_html "related-issues", :partial => "related_issues"
page.visual_effect :highlight, "related-issue-#{@issue.id}"
end
}
end
else
respond_to do |format|
format.js {
render :update do |page|
page.alert(l(:label_issue) + ' ' + l('activerecord.errors.messages.invalid'))
end
}
end
end
end
@@ -245,6 +228,14 @@ class RepositoriesController < ApplicationController
if @issue
@changeset.issues.delete(@issue)
end
respond_to do |format|
format.js {
render :update do |page|
page.remove "related-issue-#{@issue.id}"
end if @issue
}
end
end
def diff
@@ -316,7 +307,8 @@ class RepositoriesController < ApplicationController
@repository = @project.repository
end
(render_404; return false) unless @repository
@path = params[:path].is_a?(Array) ? params[:path].join('/') : params[:path].to_s
@path = params[:path].join('/') unless params[:path].nil?
@path ||= ''
@rev = params[:rev].blank? ? @repository.default_branch : params[:rev].to_s.strip
@rev_to = params[:rev_to]
@@ -351,17 +343,17 @@ class RepositoriesController < ApplicationController
@date_to = Date.today
@date_from = @date_to << 11
@date_from = Date.civil(@date_from.year, @date_from.month, 1)
commits_by_day = Changeset.count(
commits_by_day = repository.changesets.count(
:all, :group => :commit_date,
:conditions => ["repository_id = ? AND commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
:conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
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[c.first.to_date.months_ago] += c.last }
changes_by_day = Change.count(
:all, :group => :commit_date, :include => :changeset,
:conditions => ["#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
changes_by_day = repository.changes.count(
:all, :group => :commit_date,
:conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
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[c.first.to_date.months_ago] += c.last }
fields = []
12.times {|m| fields << month_name(((Date.today.month - 1 - m) % 12) + 1)}
@@ -392,10 +384,10 @@ class RepositoriesController < ApplicationController
end
def graph_commits_per_author(repository)
commits_by_author = Changeset.count(:all, :group => :committer, :conditions => ["repository_id = ?", repository.id])
commits_by_author = repository.changesets.count(:all, :group => :committer)
commits_by_author.to_a.sort! {|x, y| x.last <=> y.last}
changes_by_author = Change.count(:all, :group => :committer, :include => :changeset, :conditions => ["#{Changeset.table_name}.repository_id = ?", repository.id])
changes_by_author = repository.changes.count(:all, :group => :committer)
h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
fields = commits_by_author.collect {|r| r.first}
@@ -431,3 +423,4 @@ class RepositoriesController < ApplicationController
graph.burn
end
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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
@@ -36,11 +36,8 @@ class RolesController < ApplicationController
end
def new
# Prefills the form with 'Non member' role permissions by default
# Prefills the form with 'Non member' role permissions
@role = Role.new(params[:role] || {:permissions => Role.non_member.permissions})
if params[:copy].present? && @copy_from = Role.find_by_id(params[:copy])
@role.copy_from(@copy_from)
end
@roles = Role.sorted.all
end
@@ -49,7 +46,7 @@ class RolesController < ApplicationController
if request.post? && @role.save
# workflow copy
if !params[:copy_workflow_from].blank? && (copy_from = Role.find_by_id(params[:copy_workflow_from]))
@role.workflow_rules.copy(copy_from)
@role.workflows.copy(copy_from)
end
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'index'

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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,13 +18,12 @@
class TimelogController < ApplicationController
menu_item :issues
before_filter :find_project_for_new_time_entry, :only => [:create]
before_filter :find_project, :only => [:create]
before_filter :find_time_entry, :only => [:show, :edit, :update]
before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
before_filter :authorize, :except => [:new, :index, :report]
before_filter :find_optional_project, :only => [:index, :report]
before_filter :find_optional_project_for_new_time_entry, :only => [:new]
before_filter :find_optional_project, :only => [:new, :index, :report]
before_filter :authorize_global, :only => [:new, :index, :report]
accept_rss_auth :index
@@ -39,7 +38,7 @@ class TimelogController < ApplicationController
def index
sort_init 'spent_on', 'desc'
sort_update 'spent_on' => ['spent_on', "#{TimeEntry.table_name}.created_on"],
sort_update 'spent_on' => 'spent_on',
'user' => 'user_id',
'activity' => 'activity_id',
'project' => "#{Project.table_name}.name",
@@ -134,13 +133,9 @@ class TimelogController < ApplicationController
flash[:notice] = l(:notice_successful_create)
if params[:continue]
if params[:project_id]
redirect_to :action => 'new', :project_id => @time_entry.project, :issue_id => @time_entry.issue,
:time_entry => {:issue_id => @time_entry.issue_id, :activity_id => @time_entry.activity_id},
:back_url => params[:back_url]
redirect_to :action => 'new', :project_id => @time_entry.project, :issue_id => @time_entry.issue, :back_url => params[:back_url]
else
redirect_to :action => 'new',
:time_entry => {:project_id => @time_entry.project_id, :issue_id => @time_entry.issue_id, :activity_id => @time_entry.activity_id},
:back_url => params[:back_url]
redirect_to :action => 'new', :back_url => params[:back_url]
end
else
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
@@ -171,7 +166,7 @@ class TimelogController < ApplicationController
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
}
format.api { render_api_ok }
format.api { head :ok }
end
else
respond_to do |format|
@@ -223,7 +218,7 @@ class TimelogController < ApplicationController
}
format.api {
if destroyed
render_api_ok
head :ok
else
render_validation_errors(@time_entries)
end
@@ -263,7 +258,7 @@ private
end
end
def find_optional_project_for_new_time_entry
def find_project
if (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present?
@project = Project.find(project_id)
end
@@ -271,15 +266,12 @@ private
@issue = Issue.find(issue_id)
@project ||= @issue.project
end
rescue ActiveRecord::RecordNotFound
render_404
end
def find_project_for_new_time_entry
find_optional_project_for_new_time_entry
if @project.nil?
render_404
return false
end
rescue ActiveRecord::RecordNotFound
render_404
end
def find_optional_project

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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
@@ -29,7 +29,7 @@ class TrackersController < ApplicationController
render :action => "index", :layout => false if request.xhr?
}
format.api {
@trackers = Tracker.sorted.all
@trackers = Tracker.all
}
end
end
@@ -45,7 +45,7 @@ class TrackersController < ApplicationController
if request.post? and @tracker.save
# workflow copy
if !params[:copy_workflow_from].blank? && (copy_from = Tracker.find_by_id(params[:copy_workflow_from]))
@tracker.workflow_rules.copy(copy_from)
@tracker.workflows.copy(copy_from)
end
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'index'
@@ -80,22 +80,4 @@ class TrackersController < ApplicationController
end
redirect_to :action => 'index'
end
def fields
if request.post? && params[:trackers]
params[:trackers].each do |tracker_id, tracker_params|
tracker = Tracker.find_by_id(tracker_id)
if tracker
tracker.core_fields = tracker_params[:core_fields]
tracker.custom_field_ids = tracker_params[:custom_field_ids]
tracker.save
end
end
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'fields'
return
end
@trackers = Tracker.sorted.all
@custom_fields = IssueCustomField.all.sort
end
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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
@@ -99,11 +99,11 @@ class UsersController < ApplicationController
@user.pref.save
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
Mailer.account_information(@user, params[:user][:password]).deliver if params[:send_information]
Mailer.deliver_account_information(@user, params[: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)))
flash[:notice] = l(:notice_successful_create)
redirect_to(params[:continue] ?
{:controller => 'users', :action => 'new'} :
{:controller => 'users', :action => 'edit', :id => @user}
@@ -146,17 +146,17 @@ class UsersController < ApplicationController
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
if was_activated
Mailer.account_activated(@user).deliver
Mailer.deliver_account_activated(@user)
elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.auth_source_id.nil?
Mailer.account_information(@user, params[:user][:password]).deliver
Mailer.deliver_account_information(@user, params[:user][:password])
end
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_to_referer_or edit_user_path(@user)
redirect_to :back
}
format.api { render_api_ok }
format.api { head :ok }
end
else
@auth_sources = AuthSource.find(:all)
@@ -169,13 +169,15 @@ class UsersController < ApplicationController
format.api { render_validation_errors(@user) }
end
end
rescue ::ActionController::RedirectBackError
redirect_to :controller => 'users', :action => 'edit', :id => @user
end
def destroy
@user.destroy
respond_to do |format|
format.html { redirect_back_or_default(users_url) }
format.api { render_api_ok }
format.html { redirect_to(users_url) }
format.api { head :ok }
end
end
@@ -183,8 +185,21 @@ class UsersController < ApplicationController
@membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
@membership.save
respond_to do |format|
format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
format.js
if @membership.valid?
format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
format.js {
render(:update) {|page|
page.replace_html "tab-content-memberships", :partial => 'users/memberships'
page.visual_effect(:highlight, "member-#{@membership.id}")
}
}
else
format.js {
render(:update) {|page|
page.alert(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', ')))
}
}
end
end
end
@@ -195,7 +210,7 @@ class UsersController < ApplicationController
end
respond_to do |format|
format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
format.js
format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'users/memberships'} }
end
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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
@@ -78,7 +78,13 @@ class VersionsController < ApplicationController
respond_to do |format|
format.html
format.js
format.js do
render :update do |page|
page.replace_html 'ajax-modal', :partial => 'versions/new_modal'
page << "showModal('ajax-modal', '600px');"
page << "Form.Element.focus('version_name');"
end
end
end
end
@@ -97,7 +103,14 @@ class VersionsController < ApplicationController
flash[:notice] = l(:notice_successful_create)
redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
end
format.js
format.js do
render(:update) {|page|
page << 'hideModal();'
# IE doesn't support the replace_html rjs method for select box options
page.replace "issue_fixed_version_id",
content_tag('select', content_tag('option') + version_options_for_select(@project.shared_versions.open, @version), :id => 'issue_fixed_version_id', :name => 'issue[fixed_version_id]')
}
end
format.api do
render :action => 'show', :status => :created, :location => version_url(@version)
end
@@ -105,7 +118,12 @@ class VersionsController < ApplicationController
else
respond_to do |format|
format.html { render :action => 'new' }
format.js { render :action => 'new' }
format.js do
render :update do |page|
page.replace_html 'ajax-modal', :partial => 'versions/new_modal'
page << "Form.Element.focus('version_name');"
end
end
format.api { render_validation_errors(@version) }
end
end
@@ -126,7 +144,7 @@ class VersionsController < ApplicationController
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
}
format.api { render_api_ok }
format.api { head :ok }
end
else
respond_to do |format|
@@ -149,7 +167,7 @@ class VersionsController < ApplicationController
@version.destroy
respond_to do |format|
format.html { redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project }
format.api { render_api_ok }
format.api { head :ok }
end
else
respond_to do |format|
@@ -165,7 +183,7 @@ class VersionsController < ApplicationController
def status_by
respond_to do |format|
format.html { render :action => 'show' }
format.js
format.js { render(:update) {|page| page.replace_html 'status_by', render_issue_status_by(@version, params[:status_by])} }
end
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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
@@ -33,6 +33,15 @@ class WatchersController < ApplicationController
end
def new
respond_to do |format|
format.js do
render :update do |page|
page.replace_html 'ajax-modal', :partial => 'watchers/new', :locals => {:watched => @watched}
page << "showModal('ajax-modal', '400px');"
page << "$('ajax-modal').addClassName('new-watcher');"
end
end
end
end
def create
@@ -43,15 +52,34 @@ class WatchersController < ApplicationController
end
end
respond_to do |format|
format.html { redirect_to_referer_or {render :text => 'Watcher added.', :layout => true}}
format.js
format.html { redirect_to :back }
format.js do
render :update do |page|
page.replace_html 'ajax-modal', :partial => 'watchers/new', :locals => {:watched => @watched}
page.replace_html 'watchers', :partial => 'watchers/watchers', :locals => {:watched => @watched}
end
end
end
rescue ::ActionController::RedirectBackError
render :text => 'Watcher added.', :layout => true
end
def append
if params[:watcher].is_a?(Hash)
user_ids = params[:watcher][:user_ids] || [params[:watcher][:user_id]]
@users = User.active.find_all_by_id(user_ids)
users = User.active.find_all_by_id(user_ids)
respond_to do |format|
format.js do
render :update do |page|
users.each do |user|
page.select("#issue_watcher_user_ids_#{user.id}").each do |item|
page.remove item
end
end
page.insert_html :bottom, 'watchers_inputs', :text => watchers_checkboxes(nil, users, true)
end
end
end
end
end
@@ -59,7 +87,11 @@ class WatchersController < ApplicationController
@watched.set_watcher(User.find(params[:user_id]), false) if request.post?
respond_to do |format|
format.html { redirect_to :back }
format.js
format.js do
render :update do |page|
page.replace_html 'watchers', :partial => 'watchers/watchers', :locals => {:watched => @watched}
end
end
end
end
@@ -79,7 +111,7 @@ private
@watched = klass.find(params[:object_id])
@project = @watched.project
elsif params[:project_id]
@project = Project.visible.find_by_param(params[:project_id])
@project = Project.visible.find(params[:project_id])
end
rescue
render_404
@@ -88,8 +120,17 @@ private
def set_watcher(user, watching)
@watched.set_watcher(user, watching)
respond_to do |format|
format.html { redirect_to_referer_or {render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true}}
format.js { render :partial => 'set_watcher', :locals => {:user => user, :watched => @watched} }
format.html { redirect_to :back }
format.js do
render(:update) do |page|
c = watcher_css(@watched)
page.select(".#{c}").each do |item|
page.replace_html item, watcher_link(@watched, user)
end
end
end
end
rescue ::ActionController::RedirectBackError
render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true
end
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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
@@ -109,7 +109,7 @@ class WikiController < ApplicationController
# To prevent StaleObjectError exception when reverting to a previous version
@content.version = @page.content.version
@text = @content.text
if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
@section = params[:section].to_i
@@ -163,8 +163,6 @@ class WikiController < ApplicationController
# Optimistic locking exception
flash.now[:error] = l(:notice_locking_conflict)
render :action => 'edit'
rescue ActiveRecord::RecordNotSaved
render :action => 'edit'
end
# rename a page

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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,6 +24,7 @@ class WikisController < ApplicationController
@wiki = @project.wiki || Wiki.new(:project => @project)
@wiki.safe_attributes = params[:wiki]
@wiki.save if request.post?
render(:update) {|page| page.replace_html "tab-content-wiki", :partial => 'projects/settings/wiki'}
end
# Delete a project's wiki

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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,27 +18,30 @@
class WorkflowsController < ApplicationController
layout 'admin'
before_filter :require_admin, :find_roles, :find_trackers
before_filter :require_admin
before_filter :find_roles
before_filter :find_trackers
def index
@workflow_counts = WorkflowTransition.count_by_tracker_and_role
@workflow_counts = Workflow.count_by_tracker_and_role
end
def edit
@role = Role.find_by_id(params[:role_id]) if params[:role_id]
@tracker = Tracker.find_by_id(params[:tracker_id]) if params[:tracker_id]
@role = Role.find_by_id(params[:role_id])
@tracker = Tracker.find_by_id(params[:tracker_id])
if request.post?
WorkflowTransition.destroy_all( ["role_id=? and tracker_id=?", @role.id, @tracker.id])
Workflow.destroy_all( ["role_id=? and tracker_id=?", @role.id, @tracker.id])
(params[:issue_status] || []).each { |status_id, transitions|
transitions.each { |new_status_id, options|
author = options.is_a?(Array) && options.include?('author') && !options.include?('always')
assignee = options.is_a?(Array) && options.include?('assignee') && !options.include?('always')
WorkflowTransition.create(:role_id => @role.id, :tracker_id => @tracker.id, :old_status_id => status_id, :new_status_id => new_status_id, :author => author, :assignee => assignee)
@role.workflows.build(:tracker_id => @tracker.id, :old_status_id => status_id, :new_status_id => new_status_id, :author => author, :assignee => assignee)
}
}
if @role.save
redirect_to :action => 'edit', :role_id => @role, :tracker_id => @tracker, :used_statuses_only => params[:used_statuses_only]
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'edit', :role_id => @role, :tracker_id => @tracker
return
end
end
@@ -47,10 +50,10 @@ class WorkflowsController < ApplicationController
if @tracker && @used_statuses_only && @tracker.issue_statuses.any?
@statuses = @tracker.issue_statuses
end
@statuses ||= IssueStatus.sorted.all
@statuses ||= IssueStatus.find(:all, :order => 'position')
if @tracker && @role && @statuses.any?
workflows = WorkflowTransition.where(:role_id => @role.id, :tracker_id => @tracker.id).all
workflows = Workflow.all(:conditions => {:role_id => @role.id, :tracker_id => @tracker.id})
@workflows = {}
@workflows['always'] = workflows.select {|w| !w.author && !w.assignee}
@workflows['author'] = workflows.select {|w| w.author}
@@ -58,35 +61,6 @@ class WorkflowsController < ApplicationController
end
end
def permissions
@role = Role.find_by_id(params[:role_id]) if params[:role_id]
@tracker = Tracker.find_by_id(params[:tracker_id]) if params[:tracker_id]
if request.post? && @role && @tracker
WorkflowPermission.replace_permissions(@tracker, @role, params[:permissions] || {})
redirect_to :action => 'permissions', :role_id => @role, :tracker_id => @tracker, :used_statuses_only => params[:used_statuses_only]
return
end
@used_statuses_only = (params[:used_statuses_only] == '0' ? false : true)
if @tracker && @used_statuses_only && @tracker.issue_statuses.any?
@statuses = @tracker.issue_statuses
end
@statuses ||= IssueStatus.sorted.all
if @role && @tracker
@fields = (Tracker::CORE_FIELDS_ALL - @tracker.disabled_core_fields).map {|field| [field, l("field_"+field.sub(/_id$/, ''))]}
@custom_fields = @tracker.custom_fields
@permissions = WorkflowPermission.where(:tracker_id => @tracker.id, :role_id => @role.id).all.inject({}) do |h, w|
h[w.old_status_id] ||= {}
h[w.old_status_id][w.field_name] = w.rule
h
end
@statuses.each {|status| @permissions[status.id] ||= {}}
end
end
def copy
if params[:source_tracker_id].blank? || params[:source_tracker_id] == 'any'
@@ -109,7 +83,7 @@ class WorkflowsController < ApplicationController
elsif @target_trackers.nil? || @target_roles.nil?
flash.now[:error] = l(:error_workflow_copy_target)
else
WorkflowRule.copy(@source_tracker, @source_role, @target_trackers, @target_roles)
Workflow.copy(@source_tracker, @source_role, @target_trackers, @target_roles)
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'copy', :source_tracker_id => @source_tracker, :source_role_id => @source_role
end
@@ -119,10 +93,10 @@ class WorkflowsController < ApplicationController
private
def find_roles
@roles = Role.sorted.all
@roles = Role.find(:all, :order => 'builtin, position')
end
def find_trackers
@trackers = Tracker.sorted.all
@trackers = Tracker.find(:all, :order => 'position')
end
end

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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,8 +20,6 @@
module AdminHelper
def project_status_options_for_select(selected)
options_for_select([[l(:label_all), ''],
[l(:project_status_active), '1'],
[l(:project_status_closed), '5'],
[l(:project_status_archived), '9']], selected.to_s)
[l(:status_active), '1']], selected.to_s)
end
end

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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
@@ -43,6 +43,12 @@ module ApplicationHelper
link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
end
# Display a link to remote if user is authorized
def link_to_remote_if_authorized(name, options = {}, html_options = nil)
url = options[:url] || {}
link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
end
# Displays a link to user's account page if active
def link_to_user(user, options={})
if user.is_a?(User)
@@ -122,7 +128,7 @@ module ApplicationHelper
h(truncate(message.subject, :length => 60)),
{ :controller => 'messages', :action => 'show',
:board_id => message.board_id,
:id => (message.parent_id || message.id),
:id => message.root,
:r => (message.parent_id && message.id),
:anchor => (message.parent_id ? "message-#{message.id}" : nil)
}.merge(options),
@@ -139,23 +145,17 @@ module ApplicationHelper
# link_to_project(project, {}, :class => "project") # => html options with default url (project overview)
#
def link_to_project(project, options={}, html_options = nil)
if project.archived?
h(project)
else
if project.active?
url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
link_to(h(project), url, html_options)
else
h(project)
end
end
def thumbnail_tag(attachment)
link_to image_tag(url_for(:controller => 'attachments', :action => 'thumbnail', :id => attachment)),
{:controller => 'attachments', :action => 'show', :id => attachment, :filename => attachment.filename},
:title => attachment.filename
end
def toggle_link(name, id, options={})
onclick = "$('##{id}').toggle(); "
onclick << (options[:focus] ? "$('##{options[:focus]}').focus(); " : "this.blur(); ")
onclick = "Element.toggle('#{id}'); "
onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
onclick << "return false;"
link_to(name, "#", :onclick => onclick)
end
@@ -168,12 +168,17 @@ module ApplicationHelper
}))
end
def prompt_to_remote(name, text, param, url, html_options = {})
html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
link_to name, {}, html_options
end
def format_activity_title(text)
h(truncate_single_line(text, :length => 100))
end
def format_activity_day(date)
date == User.current.today ? l(:label_today).titleize : format_date(date)
date == Date.today ? l(:label_today).titleize : format_date(date)
end
def format_activity_description(text)
@@ -195,39 +200,6 @@ module ApplicationHelper
end
end
# Renders a tree of projects as a nested set of unordered lists
# The given collection may be a subset of the whole project tree
# (eg. some intermediate nodes are private and can not be seen)
def render_project_nested_lists(projects)
s = ''
if projects.any?
ancestors = []
original_project = @project
projects.sort_by(&:lft).each do |project|
# set the project environment to please macros.
@project = project
if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>\n"
else
ancestors.pop
s << "</li>"
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
ancestors.pop
s << "</ul></li>\n"
end
end
classes = (ancestors.empty? ? 'root' : 'child')
s << "<li class='#{classes}'><div class='#{classes}'>"
s << h(block_given? ? yield(project) : project.name)
s << "</div>\n"
ancestors << project
end
s << ("</li></ul>\n" * ancestors.size)
@project = original_project
end
s.html_safe
end
def render_page_hierarchy(pages, node=nil, options={})
content = ''
if pages[node]
@@ -248,7 +220,7 @@ module ApplicationHelper
def render_flash_messages
s = ''
flash.each do |k,v|
s << content_tag('div', v.html_safe, :class => "flash #{k}", :id => "flash_#{k}")
s << (content_tag('div', v.html_safe, :class => "flash #{k}"))
end
s.html_safe
end
@@ -265,24 +237,23 @@ module ApplicationHelper
# Renders the project quick-jump box
def render_project_jump_box
return unless User.current.logged?
projects = User.current.memberships.collect(&:project).compact.select(&:active?).uniq
projects = User.current.memberships.collect(&:project).compact.uniq
if projects.any?
options =
("<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
'<option value="" disabled="disabled">---</option>').html_safe
options << project_tree_options_for_select(projects, :selected => @project) do |p|
{ :value => project_path(:id => p, :jump => current_menu_item) }
s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
"<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
'<option value="" disabled="disabled">---</option>'
s << project_tree_options_for_select(projects, :selected => @project) do |p|
{ :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
end
select_tag('project_quick_jump_box', options, :onchange => 'if (this.value != \'\') { window.location = this.value; }')
s << '</select>'
s.html_safe
end
end
def project_tree_options_for_select(projects, options = {})
s = ''
project_tree(projects) do |project, level|
name_prefix = (level > 0 ? '&nbsp;' * 2 * level + '&#187; ' : '').html_safe
name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ').html_safe : '')
tag_options = {:value => project.id}
if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project))
tag_options[:selected] = 'selected'
@@ -302,6 +273,30 @@ module ApplicationHelper
Project.project_tree(projects, &block)
end
def project_nested_ul(projects, &block)
s = ''
if projects.any?
ancestors = []
projects.sort_by(&:lft).each do |project|
if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
s << "<ul>\n"
else
ancestors.pop
s << "</li>"
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
ancestors.pop
s << "</ul></li>\n"
end
end
s << "<li>"
s << yield(project).to_s
ancestors << project
end
s << ("</li></ul>\n" * ancestors.size)
end
s.html_safe
end
def principals_check_box_tags(name, principals)
s = ''
principals.sort.each do |principal|
@@ -314,7 +309,7 @@ module ApplicationHelper
def principals_options_for_select(collection, selected=nil)
s = ''
if collection.include?(User.current)
s << content_tag('option', "<< #{l(:label_me)} >>", :value => User.current.id)
s << content_tag('option', "<< #{l(:label_me)} >>".html_safe, :value => User.current.id)
end
groups = ''
collection.sort.each do |element|
@@ -324,7 +319,7 @@ module ApplicationHelper
unless groups.empty?
s << %(<optgroup label="#{h(l(:label_group_plural))}">#{groups}</optgroup>)
end
s.html_safe
s
end
# Truncates and returns the string as a single line
@@ -357,7 +352,7 @@ module ApplicationHelper
def time_tag(time)
text = distance_of_time_in_words(Time.now, time)
if @project
link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => User.current.time_to_date(time)}, :title => format_time(time))
link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => time.to_date}, :title => format_time(time))
else
content_tag('acronym', text, :title => format_time(time))
end
@@ -374,8 +369,7 @@ module ApplicationHelper
end
def to_path_param(path)
str = path.to_s.split(%r{[/\\]}).select{|p| !p.blank?}.join("/")
str.blank? ? nil : str
path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
end
def pagination_links_full(paginator, count=nil, options={})
@@ -404,7 +398,7 @@ module ApplicationHelper
unless count.nil?
html << " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})"
if per_page_links != false && links = per_page_links(paginator.items_per_page, count)
if per_page_links != false && links = per_page_links(paginator.items_per_page)
html << " | #{links}"
end
end
@@ -412,23 +406,11 @@ module ApplicationHelper
html.html_safe
end
def per_page_links(selected=nil, item_count=nil)
values = Setting.per_page_options_array
if item_count && values.any?
if item_count > values.first
max = values.detect {|value| value >= item_count} || item_count
else
max = item_count
end
values = values.select {|value| value <= max || value == selected}
end
if values.empty? || (values.size == 1 && values.first == selected)
return nil
end
links = values.collect do |n|
def per_page_links(selected=nil)
links = Setting.per_page_options_array.collect do |n|
n == selected ? n : link_to_content_update(n, params.merge(:per_page => n))
end
l(:label_display_per_page, links.join(', '))
links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
end
def reorder_links(name, url, method = :post)
@@ -527,8 +509,6 @@ module ApplicationHelper
project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
only_path = options.delete(:only_path) == false ? false : true
text = text.dup
macros = catch_macros(text)
text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr)
@parsed_headings = []
@@ -536,8 +516,8 @@ module ApplicationHelper
@current_section = 0 if options[:edit_section_links]
parse_sections(text, project, obj, attr, only_path, options)
text = parse_non_pre_blocks(text, obj, macros) do |text|
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links].each do |method_name|
text = parse_non_pre_blocks(text) do |text|
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_macros].each do |method_name|
send method_name, text, project, obj, attr, only_path, options
end
end
@@ -550,7 +530,7 @@ module ApplicationHelper
text.html_safe
end
def parse_non_pre_blocks(text, obj, macros)
def parse_non_pre_blocks(text)
s = StringScanner.new(text)
tags = []
parsed = ''
@@ -559,9 +539,6 @@ module ApplicationHelper
text, full_tag, closing, tag = s[1], s[2], s[3], s[4]
if tags.empty?
yield text
inject_macros(text, obj, macros) if macros.any?
else
inject_macros(text, obj, macros, false) if macros.any?
end
parsed << text
if tag
@@ -717,7 +694,7 @@ module ApplicationHelper
oid = identifier.to_i
case prefix
when nil
if oid.to_s == identifier && issue = Issue.visible.find_by_id(oid, :include => :status)
if issue = Issue.visible.find_by_id(oid, :include => :status)
anchor = comment_id ? "note-#{comment_id}" : nil
link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid, :anchor => anchor},
:class => issue.css_classes,
@@ -822,7 +799,7 @@ module ApplicationHelper
end
end
HEADING_RE = /(<h(\d)( [^>]+)?>(.+?)<\/h(\d)>)/i unless const_defined?(:HEADING_RE)
HEADING_RE = /(<h(1|2|3|4)( [^>]+)?>(.+?)<\/h(1|2|3|4)>)/i unless const_defined?(:HEADING_RE)
def parse_sections(text, project, obj, attr, only_path, options)
return unless options[:edit_section_links]
@@ -861,57 +838,31 @@ module ApplicationHelper
end
end
MACROS_RE = /(
MACROS_RE = /
(!)? # escaping
(
\{\{ # opening tag
([\w]+) # macro name
(\(([^\n\r]*?)\))? # optional arguments
([\n\r].*?[\n\r])? # optional block of text
(\(([^\}]*)\))? # optional arguments
\}\} # closing tag
)
)/mx unless const_defined?(:MACROS_RE)
/x unless const_defined?(:MACROS_RE)
MACRO_SUB_RE = /(
\{\{
macro\((\d+)\)
\}\}
)/x unless const_defined?(:MACRO_SUB_RE)
# Extracts macros from text
def catch_macros(text)
macros = {}
# Macros substitution
def parse_macros(text, project, obj, attr, only_path, options)
text.gsub!(MACROS_RE) do
all, macro = $1, $4.downcase
if macro_exists?(macro) || all =~ MACRO_SUB_RE
index = macros.size
macros[index] = all
"{{macro(#{index})}}"
esc, all, macro = $1, $2, $3.downcase
args = ($5 || '').split(',').each(&:strip)
if esc.nil?
begin
exec_macro(macro, obj, args)
rescue => e
"<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
end || all
else
all
end
end
macros
end
# Executes and replaces macros in text
def inject_macros(text, obj, macros, execute=true)
text.gsub!(MACRO_SUB_RE) do
all, index = $1, $2.to_i
orig = macros.delete(index)
if execute && orig && orig =~ MACROS_RE
esc, all, macro, args, block = $2, $3, $4.downcase, $6.to_s, $7.try(:strip)
if esc.nil?
h(exec_macro(macro, obj, args, block) || all)
else
h(all)
end
elsif orig
h(orig)
else
h(all)
end
end
end
TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
@@ -919,8 +870,6 @@ module ApplicationHelper
# Renders the TOC with given headings
def replace_toc(text, headings)
text.gsub!(TOC_RE) do
# Keep only the 4 first levels
headings = headings.select{|level, anchor, item| level <= 4}
if headings.empty?
''
else
@@ -968,12 +917,19 @@ module ApplicationHelper
content_tag("label", label_text)
end
def labelled_tabular_form_for(*args, &proc)
ActiveSupport::Deprecation.warn "ApplicationHelper#labelled_tabular_form_for is deprecated and will be removed in Redmine 1.5. Use #labelled_form_for instead."
args << {} unless args.last.is_a?(Hash)
options = args.last
options[:html] ||= {}
options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
form_for(*args, &proc)
end
def labelled_form_for(*args, &proc)
args << {} unless args.last.is_a?(Hash)
options = args.last
if args.first.is_a?(Symbol)
options.merge!(:as => args.shift)
end
options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
form_for(*args, &proc)
end
@@ -986,11 +942,10 @@ module ApplicationHelper
end
def labelled_remote_form_for(*args, &proc)
ActiveSupport::Deprecation.warn "ApplicationHelper#labelled_remote_form_for is deprecated and will be removed in Redmine 2.2."
args << {} unless args.last.is_a?(Hash)
options = args.last
options.merge!({:builder => Redmine::Views::LabelledFormBuilder, :remote => true})
form_for(*args, &proc)
options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
remote_form_for(*args, &proc)
end
def error_messages_for(*objects)
@@ -1007,39 +962,10 @@ module ApplicationHelper
html.html_safe
end
def delete_link(url, options={})
options = {
:method => :delete,
:data => {:confirm => l(:text_are_you_sure)},
:class => 'icon icon-del'
}.merge(options)
link_to l(:button_delete), url, options
end
def preview_link(url, form, target='preview', options={})
content_tag 'a', l(:label_preview), {
:href => "#",
:onclick => %|submitPreview("#{escape_javascript url_for(url)}", "#{escape_javascript form}", "#{escape_javascript target}"); return false;|,
:accesskey => accesskey(:preview)
}.merge(options)
end
def link_to_function(name, function, html_options={})
content_tag(:a, name, {:href => '#', :onclick => "#{function}; return false;"}.merge(html_options))
end
def back_url
url = params[:back_url]
if url.nil? && referer = request.env['HTTP_REFERER']
url = CGI.unescape(referer.to_s)
end
url
end
def back_url_hidden_field_tag
url = back_url
hidden_field_tag('back_url', url, :id => nil) unless url.blank?
back_url = params[:back_url] || request.env['HTTP_REFERER']
back_url = CGI.unescape(back_url.to_s)
hidden_field_tag('back_url', CGI.escape(back_url), :id => nil) unless back_url.blank?
end
def check_all_links(form_name)
@@ -1083,89 +1009,37 @@ module ApplicationHelper
end
@context_menu_included = true
end
javascript_tag "contextMenuInit('#{ url_for(url) }')"
javascript_tag "new ContextMenu('#{ url_for(url) }')"
end
def calendar_for(field_id)
include_calendar_headers_tags
javascript_tag("$(function() { $('##{field_id}').datepicker(datepickerOptions); });")
image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
end
def include_calendar_headers_tags
unless @calendar_headers_tags_included
@calendar_headers_tags_included = true
content_for :header_tags do
start_of_week = Setting.start_of_week
start_of_week = l(:general_first_day_of_week, :default => '1') if start_of_week.blank?
# Redmine uses 1..7 (monday..sunday) in settings and locales
# JQuery uses 0..6 (sunday..saturday), 7 needs to be changed to 0
start_of_week = start_of_week.to_i % 7
tags = javascript_tag(
"var datepickerOptions={dateFormat: 'yy-mm-dd', firstDay: #{start_of_week}, " +
"showOn: 'button', buttonImageOnly: true, buttonImage: '" +
path_to_image('/images/calendar.png') +
"', showButtonPanel: true};")
jquery_locale = l('jquery.locale', :default => current_language.to_s)
unless jquery_locale == 'en'
tags << javascript_include_tag("i18n/jquery.ui.datepicker-#{jquery_locale}.js")
end
tags
end
end
end
# Overrides Rails' stylesheet_link_tag with themes and plugins support.
# Examples:
# stylesheet_link_tag('styles') # => picks styles.css from the current theme or defaults
# stylesheet_link_tag('styles', :plugin => 'foo) # => picks styles.css from plugin's assets
#
def stylesheet_link_tag(*sources)
options = sources.last.is_a?(Hash) ? sources.pop : {}
plugin = options.delete(:plugin)
sources = sources.map do |source|
if plugin
"/plugin_assets/#{plugin}/stylesheets/#{source}"
elsif current_theme && current_theme.stylesheets.include?(source)
current_theme.stylesheet_path(source)
else
source
end
end
super sources, options
end
# Overrides Rails' image_tag with themes and plugins support.
# Examples:
# image_tag('image.png') # => picks image.png from the current theme or defaults
# image_tag('image.png', :plugin => 'foo) # => picks image.png from plugin's assets
#
def image_tag(source, options={})
if plugin = options.delete(:plugin)
source = "/plugin_assets/#{plugin}/images/#{source}"
elsif current_theme && current_theme.images.include?(source)
source = current_theme.image_path(source)
end
super source, options
end
# Overrides Rails' javascript_include_tag with plugins support
# Examples:
# javascript_include_tag('scripts') # => picks scripts.js from defaults
# javascript_include_tag('scripts', :plugin => 'foo) # => picks scripts.js from plugin's assets
#
def javascript_include_tag(*sources)
options = sources.last.is_a?(Hash) ? sources.pop : {}
if plugin = options.delete(:plugin)
sources = sources.map do |source|
if plugin
"/plugin_assets/#{plugin}/javascripts/#{source}"
start_of_week = case Setting.start_of_week.to_i
when 1
'Calendar._FD = 1;' # Monday
when 7
'Calendar._FD = 0;' # Sunday
when 6
'Calendar._FD = 6;' # Saturday
else
source
'' # use language
end
javascript_include_tag('calendar/calendar') +
javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
javascript_tag(start_of_week) +
javascript_include_tag('calendar/calendar-setup') +
stylesheet_link_tag('calendar')
end
end
super sources, options
end
def content_for(name, content = nil, &block)
@@ -1178,14 +1052,6 @@ module ApplicationHelper
(@has_content && @has_content[name]) || false
end
def sidebar_content?
has_content?(:sidebar) || view_layouts_base_sidebar_hook_response.present?
end
def view_layouts_base_sidebar_hook_response
@view_layouts_base_sidebar_hook_response ||= call_hook(:view_layouts_base_sidebar)
end
def email_delivery_enabled?
!!ActionMailer::Base.perform_deliveries
end
@@ -1194,7 +1060,7 @@ module ApplicationHelper
# +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
def avatar(user, options = { })
if Setting.gravatar_enabled?
options.merge!({:ssl => (request && request.ssl?), :default => Setting.gravatar_default})
options.merge!({:ssl => (defined?(request) && request.ssl?), :default => Setting.gravatar_default})
email = nil
if user.respond_to?(:mail)
email = user.mail
@@ -1208,19 +1074,14 @@ module ApplicationHelper
end
def sanitize_anchor_name(anchor)
if ''.respond_to?(:encoding) || RUBY_PLATFORM == 'java'
anchor.gsub(%r{[^\p{Word}\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
else
# TODO: remove when ruby1.8 is no longer supported
anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
end
anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
end
# Returns the javascript tags that are included in the html layout head
def javascript_heads
tags = javascript_include_tag('jquery-1.7.2-ui-1.8.21-ujs-2.0.3', 'application')
tags = javascript_include_tag(:defaults)
unless User.current.pref.warn_on_leaving_unsaved == '0'
tags << "\n".html_safe + javascript_tag("$(window).load(function(){ warnLeavingUnsaved('#{escape_javascript l(:text_warn_on_leaving_unsaved)}'); });")
tags << "\n".html_safe + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
end
tags
end

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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,14 +21,12 @@ module AttachmentsHelper
# Displays view/delete links to the attachments of the given object
# Options:
# :author -- author names are not displayed if set to false
# :thumbails -- display thumbnails if enabled in settings
def link_to_attachments(container, options = {})
options.assert_valid_keys(:author, :thumbnails)
options.assert_valid_keys(:author)
if container.attachments.any?
options = {:deletable => container.attachments_deletable?, :author => true}.merge(options)
render :partial => 'attachments/links',
:locals => {:attachments => container.attachments, :options => options, :thumbnails => (options[:thumbnails] && Setting.thumbnails_enabled?)}
render :partial => 'attachments/links', :locals => {:attachments => container.attachments, :options => options}
end
end

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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,24 +18,4 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module BoardsHelper
def board_breadcrumb(item)
board = item.is_a?(Message) ? item.board : item
links = [link_to(l(:label_board_plural), project_boards_path(item.project))]
boards = board.ancestors.reverse
if item.is_a?(Message)
boards << board
end
links += boards.map {|ancestor| link_to(h(ancestor.name), project_board_path(ancestor.project, ancestor))}
breadcrumb links
end
def boards_options_for_select(boards)
options = []
Board.board_tree(boards) do |board, level|
label = (level > 0 ? '&nbsp;' * 2 * level + '&#187; ' : '').html_safe
label << board.name
options << [label, board.id]
end
options
end
end

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -36,7 +36,7 @@ module ContextMenusHelper
def bulk_update_custom_field_context_menu_link(field, text, value)
context_menu_link h(text),
{:controller => 'issues', :action => 'bulk_update', :ids => @issue_ids, :issue => {'custom_field_values' => {field.id => value}}, :back_url => @back},
{:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'custom_field_values' => {field.id => value}}, :back_url => @back},
:method => :post,
:selected => (@issue && @issue.custom_field_value(field) == value)
end

View File

@@ -39,79 +39,73 @@ module CustomFieldsHelper
field_name << "[]" if custom_field.multiple?
field_id = "#{name}_custom_field_values_#{custom_field.id}"
tag_options = {:id => field_id, :class => "#{custom_field.field_format}_cf"}
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
case field_format.try(:edit_as)
when "date"
text_field_tag(field_name, custom_value.value, tag_options.merge(:size => 10)) +
text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
calendar_for(field_id)
when "text"
text_area_tag(field_name, custom_value.value, tag_options.merge(:rows => 3))
text_area_tag(field_name, custom_value.value, :id => field_id, :rows => 3, :style => 'width:90%')
when "bool"
hidden_field_tag(field_name, '0') + check_box_tag(field_name, '1', custom_value.true?, tag_options)
hidden_field_tag(field_name, '0') + check_box_tag(field_name, '1', custom_value.true?, :id => field_id)
when "list"
blank_option = ''.html_safe
blank_option = ''
unless custom_field.multiple?
if custom_field.is_required?
unless custom_field.default_value.present?
blank_option = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---", :value => '')
blank_option = "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>"
end
else
blank_option = content_tag('option')
blank_option = '<option></option>'
end
end
s = select_tag(field_name, blank_option + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value),
tag_options.merge(:multiple => custom_field.multiple?))
s = select_tag(field_name, blank_option.html_safe + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value),
:id => field_id, :multiple => custom_field.multiple?)
if custom_field.multiple?
s << hidden_field_tag(field_name, '')
end
s
else
text_field_tag(field_name, custom_value.value, tag_options)
text_field_tag(field_name, custom_value.value, :id => field_id)
end
end
# Return custom field label tag
def custom_field_label_tag(name, custom_value, options={})
required = options[:required] || custom_value.custom_field.is_required?
def custom_field_label_tag(name, custom_value)
content_tag "label", h(custom_value.custom_field.name) +
(required ? " <span class=\"required\">*</span>".html_safe : ""),
:for => "#{name}_custom_field_values_#{custom_value.custom_field.id}"
(custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>".html_safe : ""),
:for => "#{name}_custom_field_values_#{custom_value.custom_field.id}"
end
# Return custom field tag with its label tag
def custom_field_tag_with_label(name, custom_value, options={})
custom_field_label_tag(name, custom_value, options) + custom_field_tag(name, custom_value)
def custom_field_tag_with_label(name, custom_value)
custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
end
def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil)
field_name = "#{name}[custom_field_values][#{custom_field.id}]"
field_name << "[]" if custom_field.multiple?
field_id = "#{name}_custom_field_values_#{custom_field.id}"
tag_options = {:id => field_id, :class => "#{custom_field.field_format}_cf"}
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
case field_format.try(:edit_as)
when "date"
text_field_tag(field_name, '', tag_options.merge(:size => 10)) +
text_field_tag(field_name, '', :id => field_id, :size => 10) +
calendar_for(field_id)
when "text"
text_area_tag(field_name, '', tag_options.merge(:rows => 3))
text_area_tag(field_name, '', :id => field_id, :rows => 3, :style => 'width:90%')
when "bool"
select_tag(field_name, options_for_select([[l(:label_no_change_option), ''],
[l(:general_text_yes), '1'],
[l(:general_text_no), '0']]), tag_options)
[l(:general_text_no), '0']]), :id => field_id)
when "list"
options = []
options << [l(:label_no_change_option), ''] unless custom_field.multiple?
options << [l(:label_none), '__none__'] unless custom_field.is_required?
options += custom_field.possible_values_options(projects)
select_tag(field_name, options_for_select(options), tag_options.merge(:multiple => custom_field.multiple?))
select_tag(field_name, options_for_select(options),
:id => field_id, :multiple => custom_field.multiple?)
else
text_field_tag(field_name, '', tag_options)
text_field_tag(field_name, '', :id => field_id)
end
end

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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
@@ -92,48 +92,6 @@ module IssuesHelper
s.html_safe
end
class IssueFieldsRows
include ActionView::Helpers::TagHelper
def initialize
@left = []
@right = []
end
def left(*args)
args.any? ? @left << cells(*args) : @left
end
def right(*args)
args.any? ? @right << cells(*args) : @right
end
def size
@left.size > @right.size ? @left.size : @right.size
end
def to_html
html = ''.html_safe
blank = content_tag('th', '') + content_tag('td', '')
size.times do |i|
left = @left[i] || blank
right = @right[i] || blank
html << content_tag('tr', left + right)
end
html
end
def cells(label, text, options={})
content_tag('th', "#{label}:", options) + content_tag('td', text, options)
end
end
def issue_fields_rows
r = IssueFieldsRows.new
yield r
r.to_html
end
def render_custom_fields_rows(issue)
return if issue.custom_field_values.empty?
ordered_values = []
@@ -290,7 +248,7 @@ module IssuesHelper
unless no_html
label = content_tag('strong', label)
old_value = content_tag("i", h(old_value)) if detail.old_value
old_value = content_tag("del", old_value) if detail.old_value and detail.value.blank?
old_value = content_tag("strike", old_value) if detail.old_value and detail.value.blank?
if detail.property == 'attachment' && !value.blank? && atta = Attachment.find_by_id(detail.prop_key)
# Link to the attachment if it has not been removed
value = link_to_attachment(atta, :download => true, :only_path => options[:only_path])

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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,13 +23,11 @@ module JournalsHelper
editable = User.current.logged? && (User.current.allowed_to?(:edit_issue_notes, issue.project) || (journal.user == User.current && User.current.allowed_to?(:edit_own_issue_notes, issue.project)))
links = []
if !journal.notes.blank?
links << link_to(image_tag('comment.png'),
{:controller => 'journals', :action => 'new', :id => issue, :journal_id => journal},
:remote => true,
:method => 'post',
:title => l(:button_quote)) if options[:reply_links]
links << link_to_remote(image_tag('comment.png'),
{ :url => {:controller => 'journals', :action => 'new', :id => issue, :journal_id => journal} },
:title => l(:button_quote)) if options[:reply_links]
links << link_to_in_place_notes_editor(image_tag('edit.png'), "journal-#{journal.id}-notes",
{ :controller => 'journals', :action => 'edit', :id => journal, :format => 'js' },
{ :controller => 'journals', :action => 'edit', :id => journal },
:title => l(:button_edit)) if editable
end
content << content_tag('div', links.join(' ').html_safe, :class => 'contextual') unless links.empty?
@@ -40,7 +38,7 @@ module JournalsHelper
end
def link_to_in_place_notes_editor(text, field_id, url, options={})
onclick = "$.ajax({url: '#{url_for(url)}', type: 'get'}); return false;"
onclick = "new Ajax.Request('#{url_for(url)}', {asynchronous:true, evalScripts:true, method:'get'}); return false;"
link_to text, '#', options.merge(:onclick => onclick)
end
end

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -51,15 +51,38 @@ module ProjectsHelper
content_tag('select', options.html_safe, :name => 'project[parent_id]', :id => 'project_parent_id')
end
# Renders the projects index
# Renders a tree of projects as a nested set of unordered lists
# The given collection may be a subset of the whole project tree
# (eg. some intermediate nodes are private and can not be seen)
def render_project_hierarchy(projects)
render_project_nested_lists(projects) do |project|
s = link_to_project(project, {}, :class => "#{project.css_classes} #{User.current.member_of?(project) ? 'my-project' : nil}")
if project.description.present?
s << content_tag('div', textilizable(project.short_description, :project => project), :class => 'wiki description')
s = ''
if projects.any?
ancestors = []
original_project = @project
projects.each do |project|
# set the project environment to please macros.
@project = project
if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>\n"
else
ancestors.pop
s << "</li>"
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
ancestors.pop
s << "</ul></li>\n"
end
end
classes = (ancestors.empty? ? 'root' : 'child')
s << "<li class='#{classes}'><div class='#{classes}'>" +
link_to_project(project, {}, :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}")
s << "<div class='wiki description'>#{textilizable(project.short_description, :project => project)}</div>" unless project.description.blank?
s << "</div>\n"
ancestors << project
end
s
s << ("</li></ul>\n" * ancestors.size)
@project = original_project
end
s.html_safe
end
# Returns a set of options for a select field, grouped by project.
@@ -68,6 +91,10 @@ module ProjectsHelper
versions.each do |version|
grouped[version.project.name] << [version.name, version.id]
end
# Add in the selected
if selected && !versions.include?(selected)
grouped[selected.project.name] << [selected.name, selected.id]
end
if grouped.keys.size > 1
grouped_options_for_select(grouped, selected && selected.id)

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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,12 +18,9 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module QueriesHelper
def filters_options_for_select(query)
options = [[]]
options += query.available_filters.sort {|a,b| a[1][:order] <=> b[1][:order]}.map do |field, field_options|
[field_options[:name], field]
end
options_for_select(options)
def operators_for_select(filter_type)
Query.operators_by_filter_type[filter_type].collect {|o| [l(Query.operators[o]), o]}
end
def column_header(column)

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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,17 +46,17 @@ module RepositoriesHelper
end
def render_changeset_changes
changes = @changeset.filechanges.find(:all, :limit => 1000, :order => 'path').collect do |change|
changes = @changeset.changes.find(:all, :limit => 1000, :order => 'path').collect do |change|
case change.action
when 'A'
# Detects moved/copied files
if !change.from_path.blank?
change.action =
@changeset.filechanges.detect {|c| c.action == 'D' && c.path == change.from_path} ? 'R' : 'C'
@changeset.changes.detect {|c| c.action == 'D' && c.path == change.from_path} ? 'R' : 'C'
end
change
when 'D'
@changeset.filechanges.detect {|c| c.from_path == change.path} ? nil : change
@changeset.changes.detect {|c| c.from_path == change.path} ? nil : change
else
change
end
@@ -141,7 +141,12 @@ module RepositoriesHelper
select_tag('repository_scm',
options_for_select(scm_options, repository.class.name.demodulize),
:disabled => (repository && !repository.new_record?),
:data => {:remote => true, :method => 'get'})
:onchange => remote_function(
:url => new_project_repository_path(@project),
:method => :get,
:update => 'content',
:with => "Form.serialize(this.form)")
)
end
def with_leading_slash(path)
@@ -154,7 +159,7 @@ module RepositoriesHelper
def subversion_field_tags(form, repository)
content_tag('p', form.text_field(:url, :size => 60, :required => true,
:disabled => !repository.safe_attribute?('url')) +
:disabled => (repository && !repository.root_url.blank?)) +
'<br />'.html_safe +
'(file:///, http://, https://, svn://, svn+[tunnelscheme]://)') +
content_tag('p', form.text_field(:login, :size => 30)) +
@@ -169,7 +174,7 @@ module RepositoriesHelper
content_tag('p', form.text_field(
:url, :label => l(:field_path_to_repository),
:size => 60, :required => true,
:disabled => !repository.safe_attribute?('url'))) +
:disabled => (repository && !repository.new_record?))) +
content_tag('p', form.select(
:log_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_commit_logs_encoding), :required => true))
@@ -179,7 +184,7 @@ module RepositoriesHelper
content_tag('p', form.text_field(
:url, :label => l(:field_path_to_repository),
:size => 60, :required => true,
:disabled => !repository.safe_attribute?('url')
:disabled => (repository && !repository.root_url.blank?)
) +
'<br />'.html_safe + l(:text_mercurial_repository_note)) +
content_tag('p', form.select(
@@ -193,7 +198,7 @@ module RepositoriesHelper
content_tag('p', form.text_field(
:url, :label => l(:field_path_to_repository),
:size => 60, :required => true,
:disabled => !repository.safe_attribute?('url')
:disabled => (repository && !repository.root_url.blank?)
) +
'<br />'.html_safe +
l(:text_git_repository_note)) +
@@ -213,12 +218,12 @@ module RepositoriesHelper
:root_url,
:label => l(:field_cvsroot),
:size => 60, :required => true,
:disabled => !repository.safe_attribute?('root_url'))) +
:disabled => !repository.new_record?)) +
content_tag('p', form.text_field(
:url,
:label => l(:field_cvs_module),
:size => 30, :required => true,
:disabled => !repository.safe_attribute?('url'))) +
:disabled => !repository.new_record?)) +
content_tag('p', form.select(
:log_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_commit_logs_encoding), :required => true)) +
@@ -233,7 +238,7 @@ module RepositoriesHelper
content_tag('p', form.text_field(
:url, :label => l(:field_path_to_repository),
:size => 60, :required => true,
:disabled => !repository.safe_attribute?('url'))) +
:disabled => (repository && !repository.new_record?))) +
content_tag('p', form.select(
:log_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_commit_logs_encoding), :required => true))
@@ -243,7 +248,7 @@ module RepositoriesHelper
content_tag('p', form.text_field(
:url, :label => l(:field_root_directory),
:size => 60, :required => true,
:disabled => !repository.safe_attribute?('url'))) +
:disabled => (repository && !repository.root_url.blank?))) +
content_tag('p', form.select(
:path_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_scm_path_encoding)

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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 +44,7 @@ module SettingsHelper
setting_values = Setting.send(setting)
setting_values = [] unless setting_values.is_a?(Array)
content_tag("label", l(options[:label] || "setting_#{setting}")) +
setting_label(setting, options).html_safe +
hidden_field_tag("settings[#{setting}][]", '').html_safe +
choices.collect do |choice|
text, value = (choice.is_a?(Array) ? choice : [choice, choice])
@@ -53,8 +53,7 @@ module SettingsHelper
check_box_tag(
"settings[#{setting}][]",
value,
Setting.send(setting).include?(value),
:id => nil
Setting.send(setting).include?(value)
) + text.to_s,
:class => 'block'
)
@@ -73,13 +72,13 @@ module SettingsHelper
def setting_check_box(setting, options={})
setting_label(setting, options).html_safe +
hidden_field_tag("settings[#{setting}]", 0, :id => nil).html_safe +
hidden_field_tag("settings[#{setting}]", 0).html_safe +
check_box_tag("settings[#{setting}]", 1, Setting.send("#{setting}?"), options).html_safe
end
def setting_label(setting, options={})
label = options.delete(:label)
label != false ? label_tag("settings_#{setting}", l(label || "setting_#{setting}")).html_safe : ''
label != false ? content_tag("label", l(label || "setting_#{setting}")).html_safe : ''
end
# Renders a notification field for a Redmine::Notifiable option
@@ -87,7 +86,7 @@ module SettingsHelper
return content_tag(:label,
check_box_tag('settings[notified_events][]',
notifiable.name,
Setting.notified_events.include?(notifiable.name), :id => nil).html_safe +
Setting.notified_events.include?(notifiable.name)).html_safe +
l_or_humanize(notifiable.name, :prefix => 'label_').html_safe,
:class => notifiable.parent.present? ? "parent" : '').html_safe
end

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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,18 +19,10 @@
module VersionsHelper
def version_anchor(version)
if @project == version.project
anchor version.name
else
anchor "#{version.project.try(:identifier)}-#{version.name}"
end
end
STATUS_BY_CRITERIAS = %w(tracker status priority author assigned_to category)
STATUS_BY_CRITERIAS = %w(category tracker status priority author assigned_to)
def render_issue_status_by(version, criteria)
criteria = 'tracker' unless STATUS_BY_CRITERIAS.include?(criteria)
criteria = 'category' unless STATUS_BY_CRITERIAS.include?(criteria)
h = Hash.new {|k,v| k[v] = [0, 0]}
begin

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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
@@ -30,8 +30,10 @@ module WatchersHelper
:action => (watched ? 'unwatch' : 'watch'),
:object_type => object.class.to_s.underscore,
:object_id => object.id}
link_to((watched ? l(:button_unwatch) : l(:button_watch)), url,
:remote => true, :method => 'post', :class => (watched ? 'icon icon-fav' : 'icon icon-fav-off'))
link_to_remote((watched ? l(:button_unwatch) : l(:button_watch)),
{:url => url},
:href => url_for(url),
:class => (watched ? 'icon icon-fav' : 'icon icon-fav-off'))
end
@@ -43,33 +45,30 @@ module WatchersHelper
# Returns a comma separated list of users watching the given object
def watchers_list(object)
remove_allowed = User.current.allowed_to?("delete_#{object.class.name.underscore}_watchers".to_sym, object.project)
content = ''.html_safe
lis = object.watcher_users.collect do |user|
s = ''.html_safe
s << avatar(user, :size => "16").to_s
s << link_to_user(user, :class => 'user')
s = avatar(user, :size => "16").to_s + link_to_user(user, :class => 'user').to_s
if remove_allowed
url = {:controller => 'watchers',
:action => 'destroy',
:object_type => object.class.to_s.underscore,
:object_id => object.id,
:user_id => user}
s << ' '
s << link_to(image_tag('delete.png'), url,
:remote => true, :method => 'post', :style => "vertical-align: middle", :class => "delete")
s += ' ' + link_to_remote(image_tag('delete.png'),
{:url => url},
:href => url_for(url),
:style => "vertical-align: middle",
:class => "delete")
end
content << content_tag('li', s)
content_tag :li, s.html_safe
end
content.present? ? content_tag('ul', content) : content
(lis.empty? ? "" : "<ul>#{ lis.join("\n") }</ul>").html_safe
end
def watchers_checkboxes(object, users, checked=nil)
users.map do |user|
c = checked.nil? ? object.watched_by?(user) : checked
tag = check_box_tag 'issue[watcher_user_ids][]', user.id, c, :id => nil
content_tag 'label', "#{tag} #{h(user)}".html_safe,
:id => "issue_watcher_user_ids_#{user.id}",
:class => "floating"
end.join.html_safe
content_tag 'label', "#{tag} #{h(user)}", :id => "issue_watcher_user_ids_#{user.id}", :class => "floating"
end.join
end
end

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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,14 +21,14 @@ module WikiHelper
def wiki_page_options_for_select(pages, selected = nil, parent = nil, level = 0)
pages = pages.group_by(&:parent) unless pages.is_a?(Hash)
s = ''.html_safe
s = ''
if pages.has_key?(parent)
pages[parent].each do |page|
attrs = "value='#{page.id}'"
attrs << " selected='selected'" if selected == page
indent = (level > 0) ? ('&nbsp;' * level * 2 + '&#187; ') : ''
indent = (level > 0) ? ('&nbsp;' * level * 2 + '&#187; ') : nil
s << content_tag('option', (indent + h(page.pretty_title)).html_safe, :value => page.id.to_s, :selected => selected == page) +
s << "<option #{attrs}>#{indent}#{h page.pretty_title}</option>\n" +
wiki_page_options_for_select(pages, selected, page, level + 1)
end
end

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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,15 +18,4 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module WorkflowsHelper
def field_required?(field)
field.is_a?(CustomField) ? field.is_required? : %w(project_id tracker_id subject priority_id is_private).include?(field)
end
def field_permission_tag(permissions, status, field)
name = field.is_a?(CustomField) ? field.id.to_s : field
options = [["", ""], [l(:label_readonly), "readonly"]]
options << [l(:label_required), "required"] unless field_required?(field)
select_tag("permissions[#{name}][#{status.id}]", options_for_select(options, permissions[status.id][name]))
end
end

View File

@@ -24,7 +24,6 @@ class Attachment < ActiveRecord::Base
validates_presence_of :filename, :author
validates_length_of :filename, :maximum => 255
validates_length_of :disk_filename, :maximum => 255
validates_length_of :description, :maximum => 255
validate :validate_max_file_size
acts_as_event :title => :filename,
@@ -45,14 +44,20 @@ class Attachment < ActiveRecord::Base
"LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id"}
cattr_accessor :storage_path
@@storage_path = Redmine::Configuration['attachments_storage_path'] || File.join(Rails.root, "files")
cattr_accessor :thumbnails_storage_path
@@thumbnails_storage_path = File.join(Rails.root, "tmp", "thumbnails")
@@storage_path = Redmine::Configuration['attachments_storage_path'] || "#{Rails.root}/files"
before_save :files_to_final_location
after_destroy :delete_from_disk
def container_with_blank_type_check
if container_type.blank?
nil
else
container_without_blank_type_check
end
end
alias_method_chain :container, :blank_type_check unless method_defined?(:container_without_blank_type_check)
# Returns an unsaved copy of the attachment
def copy(attributes=nil)
copy = self.class.new
@@ -105,15 +110,10 @@ class Attachment < ActiveRecord::Base
logger.info("Saving attachment '#{self.diskfile}' (#{@temp_file.size} bytes)")
md5 = Digest::MD5.new
File.open(diskfile, "wb") do |f|
if @temp_file.respond_to?(:read)
buffer = ""
while (buffer = @temp_file.read(8192))
f.write(buffer)
md5.update(buffer)
end
else
f.write(@temp_file)
md5.update(@temp_file)
buffer = ""
while (buffer = @temp_file.read(8192))
f.write(buffer)
md5.update(buffer)
end
end
self.digest = md5.hexdigest
@@ -127,22 +127,14 @@ class Attachment < ActiveRecord::Base
# Deletes the file from the file system if it's not referenced by other attachments
def delete_from_disk
if Attachment.where("disk_filename = ? AND id <> ?", disk_filename, id).empty?
if Attachment.first(:conditions => ["disk_filename = ? AND id <> ?", disk_filename, id]).nil?
delete_from_disk!
end
end
# Returns file's location on disk
def diskfile
File.join(self.class.storage_path, disk_filename.to_s)
end
def title
title = filename.to_s
if description.present?
title << " (#{description})"
end
title
"#{@@storage_path}/#{self.disk_filename}"
end
def increment_download
@@ -162,43 +154,7 @@ class Attachment < ActiveRecord::Base
end
def image?
!!(self.filename =~ /\.(bmp|gif|jpg|jpe|jpeg|png)$/i)
end
def thumbnailable?
image?
end
# Returns the full path the attachment thumbnail, or nil
# if the thumbnail cannot be generated.
def thumbnail(options={})
if thumbnailable? && readable?
size = options[:size].to_i
if size > 0
# Limit the number of thumbnails per image
size = (size / 50) * 50
# Maximum thumbnail size
size = 800 if size > 800
else
size = Setting.thumbnails_size.to_i
end
size = 100 unless size > 0
target = File.join(self.class.thumbnails_storage_path, "#{id}_#{digest}_#{size}.thumb")
begin
Redmine::Thumbnail.generate(self.diskfile, target, size)
rescue => e
logger.error "An error occured while generating thumbnail for #{disk_filename} to #{target}\nException was: #{e.message}" if logger
return nil
end
end
end
# Deletes all thumbnails
def self.clear_thumbnails
Dir.glob(File.join(thumbnails_storage_path, "*.thumb")).each do |file|
File.delete file
end
self.filename =~ /\.(bmp|gif|jpg|jpe|jpeg|png)$/i
end
def is_text?
@@ -223,7 +179,7 @@ class Attachment < ActiveRecord::Base
def self.find_by_token(token)
if token.to_s =~ /^(\d+)\.([0-9a-f]+)$/
attachment_id, attachment_digest = $1, $2
attachment = Attachment.where(:id => attachment_id, :digest => attachment_digest).first
attachment = Attachment.first(:conditions => {:id => attachment_id, :digest => attachment_digest})
if attachment && attachment.container.nil?
attachment
end
@@ -248,7 +204,8 @@ class Attachment < ActiveRecord::Base
end
def self.prune(age=1.day)
Attachment.where("created_on < ? AND (container_type IS NULL OR container_type = '')", Time.now - age).destroy_all
attachments = Attachment.all(:conditions => ["created_on < ? AND (container_type IS NULL OR container_type = '')", Time.now - age])
attachments.each(&:destroy)
end
private

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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 +18,6 @@
# Generic exception for when the AuthSource can not be reached
# (eg. can not connect to the LDAP)
class AuthSourceException < Exception; end
class AuthSourceTimeoutException < AuthSourceException; end
class AuthSource < ActiveRecord::Base
include Redmine::SubclassFactory
@@ -59,7 +58,7 @@ class AuthSource < ActiveRecord::Base
# Try to authenticate a user not yet registered against available sources
def self.authenticate(login, password)
AuthSource.where(:onthefly_register => true).all.each do |source|
AuthSource.find(:all, :conditions => ["onthefly_register=?", true]).each do |source|
begin
logger.debug "Authenticating '#{login}' against '#{source.name}'" if logger && logger.debug?
attrs = source.authenticate(login, password)

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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 +18,6 @@
require 'iconv'
require 'net/ldap'
require 'net/ldap/dn'
require 'timeout'
class AuthSourceLdap < AuthSource
validates_presence_of :host, :port, :attr_login
@@ -26,11 +25,18 @@ class AuthSourceLdap < AuthSource
validates_length_of :account, :account_password, :base_dn, :filter, :maximum => 255, :allow_blank => true
validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30, :allow_nil => true
validates_numericality_of :port, :only_integer => true
validates_numericality_of :timeout, :only_integer => true, :allow_blank => true
validate :validate_filter
before_validation :strip_ldap_attributes
def self.human_attribute_name(attribute_key_name, *args)
attr_name = attribute_key_name.to_s
if attr_name == "filter"
attr_name = "ldap_filter"
end
super(attr_name, *args)
end
def initialize(attributes=nil, *args)
super
self.port = 389 if self.port == 0
@@ -38,26 +44,22 @@ class AuthSourceLdap < AuthSource
def authenticate(login, password)
return nil if login.blank? || password.blank?
attrs = get_user_dn(login, password)
with_timeout do
attrs = get_user_dn(login, password)
if attrs && attrs[:dn] && authenticate_dn(attrs[:dn], password)
logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
return attrs.except(:dn)
end
if attrs && attrs[:dn] && authenticate_dn(attrs[:dn], password)
logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
return attrs.except(:dn)
end
rescue Net::LDAP::LdapError => e
rescue Net::LDAP::LdapError => e
raise AuthSourceException.new(e.message)
end
# test the connection to the LDAP
def test_connection
with_timeout do
ldap_con = initialize_ldap_con(self.account, self.account_password)
ldap_con.open { }
end
rescue Net::LDAP::LdapError => e
raise AuthSourceException.new(e.message)
ldap_con = initialize_ldap_con(self.account, self.account_password)
ldap_con.open { }
rescue Net::LDAP::LdapError => e
raise "LdapError: " + e.message
end
def auth_method_name
@@ -66,16 +68,6 @@ class AuthSourceLdap < AuthSource
private
def with_timeout(&block)
timeout = self.timeout
timeout = 20 unless timeout && timeout > 0
Timeout.timeout(timeout) do
return yield
end
rescue Timeout::Error => e
raise AuthSourceTimeoutException.new(e.message)
end
def ldap_filter
if filter.present?
Net::LDAP::Filter.construct(filter)

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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,37 +21,26 @@ class Board < ActiveRecord::Base
has_many :topics, :class_name => 'Message', :conditions => "#{Message.table_name}.parent_id IS NULL", :order => "#{Message.table_name}.created_on DESC"
has_many :messages, :dependent => :destroy, :order => "#{Message.table_name}.created_on DESC"
belongs_to :last_message, :class_name => 'Message', :foreign_key => :last_message_id
acts_as_tree :dependent => :nullify
acts_as_list :scope => '(project_id = #{project_id} AND parent_id #{parent_id ? "= #{parent_id}" : "IS NULL"})'
acts_as_list :scope => :project_id
acts_as_watchable
validates_presence_of :name, :description
validates_length_of :name, :maximum => 30
validates_length_of :description, :maximum => 255
validate :validate_board
scope :visible, lambda {|*args| { :include => :project,
named_scope :visible, lambda {|*args| { :include => :project,
:conditions => Project.allowed_to_condition(args.shift || User.current, :view_messages, *args) } }
safe_attributes 'name', 'description', 'parent_id', 'move_to'
safe_attributes 'name', 'description', 'move_to'
def visible?(user=User.current)
!user.nil? && user.allowed_to?(:view_messages, project)
end
def reload(*args)
@valid_parents = nil
super
end
def to_s
name
end
def valid_parents
@valid_parents ||= project.boards - self_and_descendants
end
def reset_counters!
self.class.reset_counters!(id)
end
@@ -64,26 +53,4 @@ class Board < ActiveRecord::Base
" last_message_id = (SELECT MAX(id) FROM #{Message.table_name} WHERE board_id=#{board_id})",
["id = ?", board_id])
end
def self.board_tree(boards, parent_id=nil, level=0)
tree = []
boards.select {|board| board.parent_id == parent_id}.sort_by(&:position).each do |board|
tree << [board, level]
tree += board_tree(boards, board.id, level+1)
end
if block_given?
tree.each do |board, level|
yield board, level
end
end
tree
end
protected
def validate_board
if parent_id && parent_id_changed?
errors.add(:parent_id, :invalid) unless valid_parents.include?(parent)
end
end
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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,7 +20,7 @@ require 'iconv'
class Changeset < ActiveRecord::Base
belongs_to :repository
belongs_to :user
has_many :filechanges, :class_name => 'Change', :dependent => :delete_all
has_many :changes, :dependent => :delete_all
has_and_belongs_to_many :issues
has_and_belongs_to_many :parents,
:class_name => "Changeset",
@@ -49,8 +49,7 @@ class Changeset < ActiveRecord::Base
validates_uniqueness_of :revision, :scope => :repository_id
validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
scope :visible,
lambda {|*args| { :include => {:repository => :project},
named_scope :visible, lambda {|*args| { :include => {:repository => :project},
:conditions => Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args) } }
after_create :scan_for_issues
@@ -162,7 +161,7 @@ class Changeset < ActiveRecord::Base
tag = "#{repository.identifier}|#{tag}"
end
if ref_project && project && ref_project != project
tag = "#{project.identifier}:#{tag}"
tag = "#{project.identifier}:#{tag}"
end
tag
end
@@ -176,12 +175,18 @@ class Changeset < ActiveRecord::Base
# Returns the previous changeset
def previous
@previous ||= Changeset.where(["id < ? AND repository_id = ?", id, repository_id]).order('id DESC').first
@previous ||= Changeset.find(:first,
:conditions => ['id < ? AND repository_id = ?',
self.id, self.repository_id],
:order => 'id DESC')
end
# Returns the next changeset
def next
@next ||= Changeset.where(["id > ? AND repository_id = ?", id, repository_id]).order('id ASC').first
@next ||= Changeset.find(:first,
:conditions => ['id > ? AND repository_id = ?',
self.id, self.repository_id],
:order => 'id ASC')
end
# Creates a new Change from it's common parameters

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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 +18,7 @@
class CommentObserver < ActiveRecord::Observer
def after_create(comment)
if comment.commented.is_a?(News) && Setting.notified_events.include?('news_comment_added')
Mailer.news_comment_added(comment).deliver
Mailer.deliver_news_comment_added(comment)
end
end
end

View File

@@ -30,6 +30,11 @@ class CustomField < ActiveRecord::Base
validate :validate_custom_field
before_validation :set_searchable
def initialize(attributes=nil, *args)
super
self.possible_values ||= []
end
def set_searchable
# make sure these fields are not searchable
self.searchable = false if %w(int float date bool).include?(field_format)
@@ -75,7 +80,7 @@ class CustomField < ActiveRecord::Base
when 'bool'
[[l(:general_text_Yes), '1'], [l(:general_text_No), '0']]
else
possible_values || []
read_possible_values_utf8_encoded || []
end
end
@@ -86,20 +91,14 @@ class CustomField < ActiveRecord::Base
when 'bool'
['1', '0']
else
values = super()
if values.is_a?(Array)
values.each do |value|
value.force_encoding('UTF-8') if value.respond_to?(:force_encoding)
end
end
values || []
read_possible_values_utf8_encoded
end
end
# Makes possible_values accept a multiline string
def possible_values=(arg)
if arg.is_a?(Array)
super(arg.compact.collect(&:strip).select {|v| !v.blank?})
write_attribute(:possible_values, arg.compact.collect(&:strip).select {|v| !v.blank?})
else
self.possible_values = arg.to_s.split(/[\n\r]+/)
end
@@ -126,26 +125,16 @@ class CustomField < ActiveRecord::Base
casted
end
def value_from_keyword(keyword, customized)
possible_values_options = possible_values_options(customized)
if possible_values_options.present?
keyword = keyword.to_s.downcase
possible_values_options.detect {|text, id| text.downcase == keyword}.try(:last)
else
keyword
end
end
# Returns a ORDER BY clause that can used to sort customized
# objects by their value of the custom field.
# Returns nil if the custom field can not be used for sorting.
# Returns false, if the custom field can not be used for sorting.
def order_statement
return nil if multiple?
case field_format
when 'string', 'text', 'list', 'date', 'bool'
# COALESCE is here to make sure that blank and NULL values are sorted equally
"COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" +
" WHERE cv_sort.customized_type='#{self.class.customized_class.base_class.name}'" +
" WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
" AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
" AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
when 'int', 'float'
@@ -153,74 +142,18 @@ class CustomField < ActiveRecord::Base
# Postgresql will raise an error if a value can not be casted!
# CustomValue validations should ensure that it doesn't occur
"(SELECT CAST(cv_sort.value AS decimal(60,3)) FROM #{CustomValue.table_name} cv_sort" +
" WHERE cv_sort.customized_type='#{self.class.customized_class.base_class.name}'" +
" WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
" AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
" AND cv_sort.custom_field_id=#{id} AND cv_sort.value <> '' AND cv_sort.value IS NOT NULL LIMIT 1)"
when 'user', 'version'
value_class.fields_for_order_statement(value_join_alias)
else
nil
end
end
# Returns a GROUP BY clause that can used to group by custom value
# Returns nil if the custom field can not be used for grouping.
def group_statement
return nil if multiple?
case field_format
when 'list', 'date', 'bool', 'int'
order_statement
when 'user', 'version'
"COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" +
" WHERE cv_sort.customized_type='#{self.class.customized_class.base_class.name}'" +
" AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
" AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
else
nil
end
end
def join_for_order_statement
case field_format
when 'user', 'version'
"LEFT OUTER JOIN #{CustomValue.table_name} #{join_alias}" +
" ON #{join_alias}.customized_type = '#{self.class.customized_class.base_class.name}'" +
" AND #{join_alias}.customized_id = #{self.class.customized_class.table_name}.id" +
" AND #{join_alias}.custom_field_id = #{id}" +
" AND #{join_alias}.value <> ''" +
" AND #{join_alias}.id = (SELECT max(#{join_alias}_2.id) FROM #{CustomValue.table_name} #{join_alias}_2" +
" WHERE #{join_alias}_2.customized_type = #{join_alias}.customized_type" +
" AND #{join_alias}_2.customized_id = #{join_alias}.customized_id" +
" AND #{join_alias}_2.custom_field_id = #{join_alias}.custom_field_id)" +
" LEFT OUTER JOIN #{value_class.table_name} #{value_join_alias}" +
" ON CAST(#{join_alias}.value as decimal(60,0)) = #{value_join_alias}.id"
else
nil
end
end
def join_alias
"cf_#{id}"
end
def value_join_alias
join_alias + "_" + field_format
end
def <=>(field)
position <=> field.position
end
# Returns the class that values represent
def value_class
case field_format
when 'user', 'version'
field_format.classify.constantize
else
nil
end
end
def self.customized_class
self.name =~ /^(.+)CustomField$/
begin; $1.constantize; rescue nil; end
@@ -261,10 +194,6 @@ class CustomField < ActiveRecord::Base
validate_field_value(value).empty?
end
def format_in?(*args)
args.include?(field_format)
end
protected
# Returns the error message for the given value regarding its format
@@ -289,4 +218,14 @@ class CustomField < ActiveRecord::Base
end
errs
end
def read_possible_values_utf8_encoded
values = read_attribute(:possible_values)
if values.is_a?(Array)
values.each do |value|
value.force_encoding('UTF-8') if value.respond_to?(:force_encoding)
end
end
values
end
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# RedMine - project management software
# Copyright (C) 2006-2011 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
@@ -30,7 +30,7 @@ class Document < ActiveRecord::Base
validates_presence_of :project, :title, :category
validates_length_of :title, :maximum => 60
scope :visible, lambda {|*args| { :include => :project,
named_scope :visible, lambda {|*args| { :include => :project,
:conditions => Project.allowed_to_condition(args.shift || User.current, :view_documents, *args) } }
safe_attributes 'category_id', 'title', 'description'

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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,10 +31,4 @@ class DocumentCategory < Enumeration
def transfer_relations(to)
documents.update_all("category_id = #{to.id}")
end
def self.default
d = super
d = first if d.nil?
d
end
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 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,6 +17,6 @@
class DocumentObserver < ActiveRecord::Observer
def after_create(document)
Mailer.document_added(document).deliver if Setting.notified_events.include?('document_added')
Mailer.deliver_document_added(document) if Setting.notified_events.include?('document_added')
end
end

Some files were not shown because too many files have changed in this diff Show More