Compare commits

..
57 Commits
Author SHA1 Message Date
Jean-Philippe Lang 72c2aaf5c7 tagged version 2.1.3
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/tags/2.1.3@10833 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-17 12:50:11 +00:00
Jean-Philippe Lang 0e241bb857 Updates for 2.1.3 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10832 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-17 12:49:43 +00:00
Jean-Philippe Lang 9dcde53dab Merged r10829 from trunk (#12358).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10830 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-17 12:18:40 +00:00
Jean-Philippe Lang a44521527a Merged r10745 from trunk (#12231).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10824 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-17 09:24:19 +00:00
Jean-Philippe Lang f12590fa5c Merged r10806 from trunk (#12195).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10823 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-17 09:02:07 +00:00
Jean-Philippe Lang a1c6f710aa Backported r10792 and r10793 from trunk (#12310).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10822 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-17 08:58:26 +00:00
Jean-Philippe Lang 3ad82e4665 Merged r10810 from trunk (#12359).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10821 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-17 08:52:51 +00:00
Jean-Philippe Lang eef3b5b5ca Merged r10790 from trunk (#12294).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10820 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-17 08:51:17 +00:00
Jean-Philippe Lang ab69845ea2 Merged r10801 from trunk (#12349).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10819 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-17 08:50:19 +00:00
Toshi MARUYAMA af51a68b34 Merged r10813 from trunk (#12375)
svn propset svn:eol-style native to fixtures.


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10817 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-16 07:57:10 +00:00
Toshi MARUYAMA d5c08680f6 Merged r10812 from trunk (#12375)
fix receiving mail subject broken which does not begin with encoding name.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10816 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-16 07:52:05 +00:00
Toshi MARUYAMA 9b82b948e3 Merged r10811 from trunk (#12375)
add test of receiving mail subject begins with encoding name.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10815 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-16 07:51:53 +00:00
Toshi MARUYAMA 1c8510bc80 Merged r10763 from trunk (#12237)
German Translation updated by Daniel Felix.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10764 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-10-29 16:03:23 +00:00
Toshi MARUYAMA 23861c7cc1 Merged from r10758 trunk (#12237)
German Translation updated by Daniel Felix.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10760 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-10-29 13:08:12 +00:00
Toshi MARUYAMA 6550343cb9 Merged r10733 from trunk (#12232, #12235)
German translation for 2.1-stable updated by Daniel Felix.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10738 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-10-28 01:59:00 +00:00
Jean-Philippe Lang a70cf2f833 Merged r10715 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10724 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-10-26 03:16:15 +00:00
Jean-Philippe Lang 0f219b973a Merged r10714 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10723 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-10-26 03:15:21 +00:00
Jean-Philippe Lang d02e5e54a8 Merged r10580 and r10581 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10722 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-10-26 03:13:34 +00:00
Toshi MARUYAMA c050028102 Merged r10709 from trunk to 2.1-stable (#12196)
fix "Page not found" on OK button in SCM "View all revisions" page.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10710 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-10-24 10:22:17 +00:00
Toshi MARUYAMA 012ffdf9e8 Merged r10702 from trunk to 2.1-stable (#12189)
add tmp/pdf directory.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10703 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-10-23 10:56:43 +00:00
Toshi MARUYAMA 9e3aeeef88 Merged r10699 from trunk (#12188)
Simplified Chinese translation for 2.1-stable updated by Steven Wong.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10701 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-10-23 07:57:06 +00:00
Toshi MARUYAMA 21b162a624 Merged r10689 from trunk to 2.1-stable
Partial backout r10263.

Backout replacing shoulda context of test/unit/helpers/issues_helper_test.rb.
Test methods in shoulda context do not run on Ruby 1.8.7-p370.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10691 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-10-22 03:24:01 +00:00
Toshi MARUYAMA b7e999d1aa Merged r10688 from trunk to 2.1-stable
Backout r10253.

Backout replacing shoulda context of test/unit/helpers/issues_helper_test.rb.
Test methods in shoulda context do not run on Ruby 1.8.7-p370.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10690 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-10-22 03:23:28 +00:00
Toshi MARUYAMA 284d03e1f8 Merged r10596 from trunk to 2.1-stable (#9732)
German translation updated by Raphael Kallensee.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10600 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-10-11 03:47:22 +00:00
Toshi MARUYAMA 3b4126e14f Merged r10572 from trunk
add missing fixtures to test/unit/issue_relation_test.rb

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10577 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-10-09 11:56:45 +00:00
Toshi MARUYAMA 46a76ecb6e Merged r10571 from trunk
add missing fixture to test/integration/api_test/issues_test.rb

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10576 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-10-09 11:56:31 +00:00
Toshi MARUYAMA 5a74edef89 Merged r10563 from trunk (#12021)
Russian translation for 2.1-stable updated.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10565 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-10-04 23:47:53 +00:00
Jean-Philippe Lang 2f9c2e6bd3 Merged r10535 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10536 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-30 10:42:57 +00:00
Jean-Philippe Lang 2df7c0ff3a Merged r10465 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10534 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-30 08:33:25 +00:00
Jean-Philippe Lang f5d5077d2b Merged r10531 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10532 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-30 08:07:17 +00:00
Jean-Philippe Lang 94a1eb21a4 Merged r10527 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10528 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-29 17:13:15 +00:00
Jean-Philippe Lang 1f80a4b0d9 Merged r10433, r10437 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10526 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-29 16:38:53 +00:00
Jean-Philippe Lang 55220950d2 Merged r10412 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10525 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-29 16:34:30 +00:00
Jean-Philippe Lang daea57a37c Merged r10438 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10524 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-29 16:32:11 +00:00
Jean-Philippe Lang fe61739108 Merged r10494 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10523 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-29 16:31:13 +00:00
Jean-Philippe Lang a4cd96e8b0 Merged r10436 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10522 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-29 16:28:40 +00:00
Jean-Philippe Lang 804302ce61 Merged r10413 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10521 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-29 16:27:03 +00:00
Jean-Philippe Lang 89ecbe9cdc Merged r10439 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10520 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-29 16:26:08 +00:00
Toshi MARUYAMA aac3e1e51c Merged r10503 from trunk to 2.1-stable (#11982, #11966)
add functional test of sub repository diff path.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10511 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-28 06:38:50 +00:00
Toshi MARUYAMA 595fef0d68 Merged r10502 from trunk to 2.1-stable (#11982, #11966)
add functional test of main repository diff path.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10510 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-28 06:38:23 +00:00
Toshi MARUYAMA 65a6e3985e Merged r10490 from trunk to 2.1-stable (#11982, #11966)
explicitly define route at scm diff view.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10509 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-28 06:37:48 +00:00
Toshi MARUYAMA f27a0eca5b Merged r10501 from trunk
set user preference diff type nil at functional tests.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10505 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-28 03:54:07 +00:00
Toshi MARUYAMA 2d34ab9235 Merged r10498 from trunk
scm: git: use with_settings instead of assigned setting at functional test

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10499 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-28 00:17:59 +00:00
Toshi MARUYAMA 03bff3308f Merged r10487 from trunk (#11885)
fix broken issue list filter.

Array indexOf() is not defined on IE8.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10489 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-27 07:29:22 +00:00
Toshi MARUYAMA 006a1b4fd7 Merged r10486 from trunk (#11885)
fix javascript syntax mistake in application.js.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10488 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-27 07:29:07 +00:00
Toshi MARUYAMA 9095874a07 Merged r10478 from trunk
use set_language_if_valid 'en' at test_parent_should_be_in_same_project at unit board test.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10482 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-26 11:59:44 +00:00
Toshi MARUYAMA 8ecfd1dcf8 Merged r10477 from trunk
set default_language en at test_commit_closing_a_subproject_issue of unit changeset test.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10481 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-26 11:59:19 +00:00
Toshi MARUYAMA 35584bc53a Merged r10476 from trunk (#11922)
fix number_to_currency() raises exception on Rails3 bs and de locale.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10479 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-26 11:09:24 +00:00
Toshi MARUYAMA 21a16e0958 Merged r10472 from trunk (#11945)
add more error message if public/plugin_assets cannot be created.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10474 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-26 05:39:40 +00:00
Toshi MARUYAMA 76d24e44bf Merged r10471 from trunk (#11945)
fix "can't convert Errno::EACCES into String" in case of no permission of public/plugin_assets on Ruby 1.9.3.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10473 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-26 05:39:23 +00:00
Toshi MARUYAMA efbdef9357 Merged r10434 from trunk (#11897)
Traditional Chinese translation updated by ChunChang Lo.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10435 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-20 11:35:55 +00:00
Jean-Philippe Lang 5eab4af70d Merged r10391 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10392 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-16 12:43:43 +00:00
Toshi MARUYAMA 96b2e00015 Merged r10379 from trunk (#11836)
Japanese translation updated by Go MAEDA.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10380 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-15 12:02:36 +00:00
Jean-Philippe Lang 9939fbeef6 Merged r10375 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10376 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-12 19:16:31 +00:00
Jean-Philippe Lang b369f82d15 Merged r10368 to r10373 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10374 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 19:40:59 +00:00
Jean-Philippe Lang 0ad5dfaba0 Set branch to stable.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10367 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 16:33:49 +00:00
Jean-Philippe Lang d7d18689b7 Added 2.1-stable branch.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10366 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 16:30:57 +00:00
1047 changed files with 16724 additions and 45764 deletions

No files matched your search

-9
View File
@@ -1,7 +1,5 @@
/.project
/.loadpath
/.powrc
/.rvmrc
/config/additional_environment.rb
/config/configuration.yml
/config/database.yml
@@ -17,21 +15,14 @@
/lib/redmine/scm/adapters/mercurial/redminehelper.pyo
/log/*.log*
/log/mongrel_debug
/plugins/*
!/plugins/README
/public/dispatch.*
/public/plugin_assets
/public/themes/*
!/public/themes/alternate
!/public/themes/classic
!/public/themes/README
/tmp/*
/tmp/cache/*
/tmp/pdf/*
/tmp/sessions/*
/tmp/sockets/*
/tmp/test/*
/tmp/thumbnails/*
/vendor/cache
/vendor/rails
*.rbc
-3
View File
@@ -2,8 +2,6 @@ syntax: glob
.project
.loadpath
.powrc
.rvmrc
config/additional_environment.rb
config/configuration.yml
config/database.yml
@@ -27,7 +25,6 @@ tmp/pdf/*
tmp/sessions/*
tmp/sockets/*
tmp/test/*
tmp/thumbnails/*
vendor/cache
vendor/rails
*.rbc
-8
View File
@@ -1,8 +0,0 @@
**Do not send a pull requst to this github repository**.
For more detail, please see [official website] wiki [Contribute].
[official website]: http://www.redmine.org
[Contribute]: http://www.redmine.org/projects/redmine/wiki/Contribute
+43 -51
View File
@@ -1,9 +1,9 @@
source 'https://rubygems.org'
source 'http://rubygems.org'
gem "rails", "3.2.13"
gem 'rails', '3.2.8'
gem "jquery-rails", "~> 2.0.2"
gem "i18n", "~> 0.6.0"
gem "coderay", "~> 1.1.0"
gem "coderay", "~> 1.0.6"
gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby]
gem "builder", "3.0.0"
@@ -14,7 +14,7 @@ end
# Optional gem for OpenID authentication
group :openid do
gem "ruby-openid", "~> 2.3.0", :require => "openid"
gem "ruby-openid", "~> 2.1.4", :require => "openid"
gem "rack-openid"
end
@@ -28,47 +28,43 @@ platforms :mri, :mingw do
end
end
platforms :jruby do
# jruby-openssl is bundled with JRuby 1.7.0
gem "jruby-openssl" if Object.const_defined?(:JRUBY_VERSION) && JRUBY_VERSION < '1.7.0'
gem "activerecord-jdbc-adapter", "~> 1.2.6"
# Database gems
platforms :mri, :mingw do
group :postgresql do
gem "pg", ">= 0.11.0"
end
group :sqlite do
gem "sqlite3"
end
end
# Include database gems for the adapters found in the database
# configuration file
require 'erb'
require 'yaml'
database_file = File.join(File.dirname(__FILE__), "config/database.yml")
if File.exist?(database_file)
database_config = YAML::load(ERB.new(IO.read(database_file)).result)
adapters = database_config.values.map {|c| c['adapter']}.compact.uniq
if adapters.any?
adapters.each do |adapter|
case adapter
when 'mysql2'
gem "mysql2", "~> 0.3.11", :platforms => [:mri, :mingw]
gem "activerecord-jdbcmysql-adapter", :platforms => :jruby
when 'mysql'
gem "mysql", "~> 2.8.1", :platforms => [:mri, :mingw]
gem "activerecord-jdbcmysql-adapter", :platforms => :jruby
when /postgresql/
gem "pg", ">= 0.11.0", :platforms => [:mri, :mingw]
gem "activerecord-jdbcpostgresql-adapter", :platforms => :jruby
when /sqlite3/
gem "sqlite3", :platforms => [:mri, :mingw]
gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
when /sqlserver/
gem "tiny_tds", "~> 0.5.1", :platforms => [:mri, :mingw]
gem "activerecord-sqlserver-adapter", :platforms => [:mri, :mingw]
else
warn("Unknown database adapter `#{adapter}` found in config/database.yml, use Gemfile.local to load your own database gems")
end
end
else
warn("No adapter found in config/database.yml, please configure it first")
platforms :mri_18, :mingw_18 do
group :mysql do
gem "mysql"
end
end
platforms :mri_19, :mingw_19 do
group :mysql do
gem "mysql2", "~> 0.3.11"
end
end
platforms :jruby do
gem "jruby-openssl"
group :mysql do
gem "activerecord-jdbcmysql-adapter"
end
group :postgresql do
gem "activerecord-jdbcpostgresql-adapter"
end
group :sqlite do
gem "activerecord-jdbcsqlite3-adapter"
end
else
warn("Please configure your config/database.yml first")
end
group :development do
@@ -77,13 +73,10 @@ group :development do
end
group :test do
gem "shoulda", "~> 3.3.2"
gem "mocha", ">= 0.14", :require => 'mocha/api'
if RUBY_VERSION >= '1.9.3'
gem "capybara", "~> 2.1.0"
gem "selenium-webdriver"
gem "database_cleaner"
end
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"
end
local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
@@ -95,6 +88,5 @@ end
# Load plugins' Gemfiles
Dir.glob File.expand_path("../plugins/*/Gemfile", __FILE__) do |file|
puts "Loading #{file} ..." if $DEBUG # `ruby -d` or `bundle -v`
#TODO: switch to "eval_gemfile file" when bundler >= 1.2.0 will be required (rails 4)
instance_eval File.read(file), file
instance_eval File.read(file)
end
+32 -79
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -20,14 +20,12 @@ class AccountController < ApplicationController
include CustomFieldsHelper
# prevents login action to be filtered by check_if_login_required application scope filter
skip_before_filter :check_if_login_required, :check_password_change
skip_before_filter :check_if_login_required
# Login request and validation
def login
if request.get?
if User.current.logged?
redirect_to home_url
end
logout_user
else
authenticate_user
end
@@ -38,20 +36,15 @@ class AccountController < ApplicationController
# Log out current user and redirect to welcome page
def logout
if User.current.anonymous?
redirect_to home_url
elsif request.post?
logout_user
redirect_to home_url
end
# display the logout form
logout_user
redirect_to home_url
end
# Lets user choose a new password
def lost_password
(redirect_to(home_url); return) unless Setting.lost_password?
redirect_to(home_url) && return unless Setting.lost_password?
if params[:token]
@token = Token.find_token("recovery", params[:token].to_s)
@token = Token.find_by_action_and_value("recovery", params[:token].to_s)
if @token.nil? || @token.expired?
redirect_to home_url
return
@@ -75,15 +68,11 @@ class AccountController < ApplicationController
else
if request.post?
user = User.find_by_mail(params[:mail].to_s)
# user not found
unless user
# user not found or not active
unless user && user.active?
flash.now[:error] = l(:notice_account_unknown_email)
return
end
unless user.active?
handle_inactive_user(user, lost_password_path)
return
end
# user cannot change its password
unless user.change_password_allowed?
flash.now[:error] = l(:notice_can_t_change_password)
@@ -103,10 +92,10 @@ class AccountController < ApplicationController
# User self-registration
def register
(redirect_to(home_url); return) unless Setting.self_registration? || session[:auth_source_registration]
redirect_to(home_url) && return unless Setting.self_registration? || session[:auth_source_registration]
if request.get?
session[:auth_source_registration] = nil
@user = User.new(:language => current_language.to_s)
@user = User.new(:language => Setting.default_language)
else
user_params = params[:user] || {}
@user = User.new
@@ -121,7 +110,7 @@ class AccountController < ApplicationController
session[:auth_source_registration] = nil
self.logged_user = @user
flash[:notice] = l(:notice_account_activated)
redirect_to my_account_path
redirect_to :controller => 'my', :action => 'account'
end
else
@user.login = params[:user][:login]
@@ -143,11 +132,11 @@ class AccountController < ApplicationController
# Token based account activation
def activate
(redirect_to(home_url); return) unless Setting.self_registration? && params[:token].present?
token = Token.find_token('register', params[:token].to_s)
(redirect_to(home_url); return) unless token and !token.expired?
redirect_to(home_url) && return unless Setting.self_registration? && params[:token]
token = Token.find_by_action_and_value('register', params[:token])
redirect_to(home_url) && return unless token and !token.expired?
user = token.user
(redirect_to(home_url); return) unless user.registered?
redirect_to(home_url) && return unless user.registered?
user.activate
if user.save
token.destroy
@@ -156,19 +145,6 @@ class AccountController < ApplicationController
redirect_to signin_path
end
# Sends a new account activation email
def activation_email
if session[:registered_user_id] && Setting.self_registration == '1'
user_id = session.delete(:registered_user_id).to_i
user = User.find_by_id(user_id)
if user && user.registered?
register_by_email_activation(user)
return
end
end
redirect_to(home_url)
end
private
def authenticate_user
@@ -180,7 +156,7 @@ class AccountController < ApplicationController
end
def password_authentication
user = User.try_to_login(params[:username], params[:password], false)
user = User.try_to_login(params[:username], params[:password])
if user.nil?
invalid_credentials
@@ -188,31 +164,25 @@ class AccountController < ApplicationController
onthefly_creation_failed(user, {:login => user.login, :auth_source_id => user.auth_source_id })
else
# Valid user
if user.active?
successful_authentication(user)
else
handle_inactive_user(user)
end
successful_authentication(user)
end
end
def open_id_authenticate(openid_url)
back_url = signin_url(:autologin => params[:autologin])
authenticate_with_open_id(
openid_url, :required => [:nickname, :fullname, :email],
:return_to => back_url, :method => :post
) do |result, identity_url, registration|
authenticate_with_open_id(openid_url, :required => [:nickname, :fullname, :email], :return_to => signin_url, :method => :post) do |result, identity_url, registration|
if result.successful?
user = User.find_or_initialize_by_identity_url(identity_url)
if user.new_record?
# Self-registration off
(redirect_to(home_url); return) unless Setting.self_registration?
redirect_to(home_url) && return unless Setting.self_registration?
# Create on the fly
user.login = registration['nickname'] unless registration['nickname'].nil?
user.mail = registration['email'] unless registration['email'].nil?
user.firstname, user.lastname = registration['fullname'].split(' ') unless registration['fullname'].nil?
user.random_password
user.register
case Setting.self_registration
when '1'
register_by_email_activation(user) do
@@ -232,7 +202,7 @@ class AccountController < ApplicationController
if user.active?
successful_authentication(user)
else
handle_inactive_user(user)
account_pending
end
end
end
@@ -248,11 +218,12 @@ class AccountController < ApplicationController
set_autologin_cookie(user)
end
call_hook(:controller_account_success_authentication_after, {:user => user })
redirect_back_or_default my_page_path
redirect_back_or_default :controller => 'my', :action => 'page'
end
def set_autologin_cookie(user)
token = Token.create(:user => user, :action => 'autologin')
cookie_name = Redmine::Configuration['autologin_cookie_name'] || 'autologin'
cookie_options = {
:value => token.value,
:expires => 1.year.from_now,
@@ -260,7 +231,7 @@ class AccountController < ApplicationController
:secure => (Redmine::Configuration['autologin_cookie_secure'] ? true : false),
:httponly => true
}
cookies[autologin_cookie_name] = cookie_options
cookies[cookie_name] = cookie_options
end
# Onthefly creation failed, display the registration form to fill/fix attributes
@@ -282,7 +253,7 @@ class AccountController < ApplicationController
token = Token.new(:user => user, :action => "register")
if user.save and token.save
Mailer.register(token).deliver
flash[:notice] = l(:notice_account_register_done, :email => user.mail)
flash[:notice] = l(:notice_account_register_done)
redirect_to signin_path
else
yield if block_given?
@@ -299,7 +270,7 @@ class AccountController < ApplicationController
if user.save
self.logged_user = user
flash[:notice] = l(:notice_account_activated)
redirect_to my_account_path
redirect_to :controller => 'my', :action => 'account'
else
yield if block_given?
end
@@ -312,32 +283,14 @@ class AccountController < ApplicationController
if user.save
# Sends an email to the administrators
Mailer.account_activation_request(user).deliver
account_pending(user)
account_pending
else
yield if block_given?
end
end
def handle_inactive_user(user, redirect_path=signin_path)
if user.registered?
account_pending(user, redirect_path)
else
account_locked(user, redirect_path)
end
end
def account_pending(user, redirect_path=signin_path)
if Setting.self_registration == '1'
flash[:error] = l(:notice_account_not_activated_yet, :url => activation_email_path)
session[:registered_user_id] = user.id
else
flash[:error] = l(:notice_account_pending)
end
redirect_to redirect_path
end
def account_locked(user, redirect_path=signin_path)
flash[:error] = l(:notice_account_locked)
redirect_to redirect_path
def account_pending
flash[:notice] = l(:notice_account_pending)
redirect_to signin_path
end
end
+1 -1
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+7 -6
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -32,9 +32,10 @@ class AdminController < ApplicationController
def projects
@status = params[:status] || 1
scope = Project.status(@status).order('lft')
scope = Project.status(@status)
scope = scope.like(params[:name]) if params[:name].present?
@projects = scope.all
@projects = scope.all(:order => 'lft')
render :action => "projects", :layout => false if request.xhr?
end
@@ -54,7 +55,7 @@ class AdminController < ApplicationController
flash[:error] = l(:error_can_t_load_default_data, e.message)
end
end
redirect_to admin_path
redirect_to :action => 'index'
end
def test_email
@@ -65,10 +66,10 @@ class AdminController < ApplicationController
@test = Mailer.test_email(User.current).deliver
flash[:notice] = l(:notice_email_sent, User.current.mail)
rescue Exception => e
flash[:error] = l(:notice_email_error, Redmine::CodesetUtil.replace_invalid_utf8(e.message))
flash[:error] = l(:notice_email_error, e.message)
end
ActionMailer::Base.raise_delivery_errors = raise_delivery_errors
redirect_to settings_path(:tab => 'notifications')
redirect_to :controller => 'settings', :action => 'edit', :tab => 'notifications'
end
def info
+31 -86
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -22,9 +22,6 @@ class Unauthorized < Exception; end
class ApplicationController < ActionController::Base
include Redmine::I18n
include Redmine::Pagination
include RoutesHelper
helper :routes
class_attribute :accept_api_auth_actions
class_attribute :accept_rss_auth_actions
@@ -35,10 +32,10 @@ class ApplicationController < ActionController::Base
protect_from_forgery
def handle_unverified_request
super
cookies.delete(autologin_cookie_name)
cookies.delete(:autologin)
end
before_filter :session_expiration, :user_setup, :check_if_login_required, :check_password_change, :set_localization
before_filter :session_expiration, :user_setup, :check_if_login_required, :set_localization
rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
rescue_from ::Unauthorized, :with => :deny_access
@@ -78,9 +75,6 @@ class ApplicationController < ActionController::Base
session[:user_id] = user.id
session[:ctime] = Time.now.utc.to_i
session[:atime] = Time.now.utc.to_i
if user.must_change_password?
session[:pwd] = '1'
end
end
def user_setup
@@ -115,33 +109,15 @@ class ApplicationController < ActionController::Base
authenticate_with_http_basic do |username, password|
user = User.try_to_login(username, password) || User.find_by_api_key(username)
end
if user && user.must_change_password?
render_error :message => 'You must change your password', :status => 403
return
end
end
# Switch user if requested by an admin user
if user && user.admin? && (username = api_switch_user_from_request)
su = User.find_by_login(username)
if su && su.active?
logger.info(" User switched by: #{user.login} (id=#{user.id})") if logger
user = su
else
render_error :message => 'Invalid X-Redmine-Switch-User header', :status => 412
end
end
end
user
end
def autologin_cookie_name
Redmine::Configuration['autologin_cookie_name'].presence || 'autologin'
end
def try_to_autologin
if cookies[autologin_cookie_name] && Setting.autologin?
if cookies[:autologin] && Setting.autologin?
# auto-login feature starts a new session
user = User.try_to_autologin(cookies[autologin_cookie_name])
user = User.try_to_autologin(cookies[:autologin])
if user
reset_session
start_user_session(user)
@@ -164,7 +140,7 @@ class ApplicationController < ActionController::Base
# Logs out current user
def logout_user
if User.current.logged?
cookies.delete(autologin_cookie_name)
cookies.delete :autologin
Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin'])
self.logged_user = nil
end
@@ -177,16 +153,6 @@ class ApplicationController < ActionController::Base
require_login if Setting.login_required?
end
def check_password_change
if session[:pwd]
if User.current.must_change_password?
redirect_to my_password_path
else
session.delete(:pwd)
end
end
end
def set_localization
lang = nil
if User.current.logged?
@@ -212,13 +178,7 @@ class ApplicationController < ActionController::Base
url = url_for(:controller => params[:controller], :action => params[:action], :id => params[:id], :project_id => params[:project_id])
end
respond_to do |format|
format.html {
if request.xhr?
head :unauthorized
else
redirect_to :controller => "account", :action => "login", :back_url => url
end
}
format.html { redirect_to :controller => "account", :action => "login", :back_url => url }
format.atom { redirect_to :controller => "account", :action => "login", :back_url => url }
format.xml { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
format.js { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
@@ -306,40 +266,20 @@ class ApplicationController < ActionController::Base
self.model_object = model
end
# Find the issue whose id is the :id parameter
# Raises a Unauthorized exception if the issue is not visible
def find_issue
# Issue.visible.find(...) can not be used to redirect user to the login form
# if the issue actually exists but requires authentication
@issue = Issue.find(params[:id])
raise Unauthorized unless @issue.visible?
@project = @issue.project
rescue ActiveRecord::RecordNotFound
render_404
end
# Find issues with a single :id param or :ids array param
# Raises a Unauthorized exception if one of the issues is not visible
# Filter for bulk issue operations
def find_issues
@issues = Issue.where(:id => (params[:id] || params[:ids])).preload(:project, :status, :tracker, :priority, :author, :assigned_to, :relations_to).to_a
@issues = Issue.find_all_by_id(params[:id] || params[:ids])
raise ActiveRecord::RecordNotFound if @issues.empty?
raise Unauthorized unless @issues.all?(&:visible?)
if @issues.detect {|issue| !issue.visible?}
deny_access
return
end
@projects = @issues.collect(&:project).compact.uniq
@project = @projects.first if @projects.size == 1
rescue ActiveRecord::RecordNotFound
render_404
end
def find_attachments
if (attachments = params[:attachments]).present?
att = attachments.values.collect do |attachment|
Attachment.find_by_token( attachment[:token] ) if attachment[:token].present?
end
att.compact!
end
@attachments = att || []
end
# make sure that the user is a member of the project (or admin) if project is private
# used as a before_filter for actions that do not require any particular permission on the project
def check_project_privacy
@@ -462,7 +402,7 @@ class ApplicationController < ActionController::Base
@items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
@items = @items.slice(0, Setting.feeds_limit.to_i)
@title = options[:title] || Setting.app_title
render :template => "common/feed", :formats => [:atom], :layout => false,
render :template => "common/feed.atom", :layout => false,
:content_type => 'application/atom+xml'
end
@@ -568,16 +508,26 @@ class ApplicationController < ActionController::Base
end
end
# Returns the API 'switch user' value if present
def api_switch_user_from_request
request.headers["X-Redmine-Switch-User"].to_s.presence
end
# Renders a warning flash if obj has unsaved attachments
def render_attachment_warning_if_needed(obj)
flash[:warning] = l(:warning_attachments_not_saved, obj.unsaved_attachments.size) if obj.unsaved_attachments.present?
end
# Sets the `flash` notice or error based the number of issues that did not save
#
# @param [Array, Issue] issues all of the saved and unsaved Issues
# @param [Array, Integer] unsaved_issue_ids the issue ids that were not saved
def set_flash_from_bulk_issue_save(issues, unsaved_issue_ids)
if unsaved_issue_ids.empty?
flash[:notice] = l(:notice_successful_update) unless issues.empty?
else
flash[:error] = l(:notice_failed_to_save_issues,
:count => unsaved_issue_ids.size,
:total => issues.size,
:ids => '#' + unsaved_issue_ids.join(', #'))
end
end
# Rescues an invalid query statement. Just in case...
def query_statement_invalid(exception)
logger.error "Query::StatementInvalid: #{exception.message}" if logger
@@ -588,13 +538,8 @@ class ApplicationController < ActionController::Base
# Renders a 200 response for successfull updates or deletions via the API
def render_api_ok
render_api_head :ok
end
# Renders a head API response
def render_api_head(status)
# #head would return a response body with one space
render :text => '', :status => status, :layout => nil
# head :ok would return a response body with one space
render :text => '', :status => :ok, :layout => nil
end
# Renders API response on validation failure
+14 -29
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -84,18 +84,16 @@ class AttachmentsController < ApplicationController
@attachment = Attachment.new(:file => request.raw_post)
@attachment.author = User.current
@attachment.filename = params[:filename].presence || Redmine::Utils.random_hex(16)
saved = @attachment.save
@attachment.filename = Redmine::Utils.random_hex(16)
respond_to do |format|
format.js
format.api {
if saved
render :action => 'upload', :status => :created
else
render_validation_errors(@attachment)
end
}
if @attachment.save
respond_to do |format|
format.api { render :action => 'upload', :status => :created }
end
else
respond_to do |format|
format.api { render_validation_errors(@attachment) }
end
end
end
@@ -103,17 +101,9 @@ class AttachmentsController < ApplicationController
if @attachment.container.respond_to?(:init_journal)
@attachment.container.init_journal(User.current)
end
if @attachment.container
# Make sure association callbacks are called
@attachment.container.attachments.delete(@attachment)
else
@attachment.destroy
end
respond_to do |format|
format.html { redirect_to_referer_or project_path(@project) }
format.js
end
# Make sure association callbacks are called
@attachment.container.attachments.delete(@attachment)
redirect_to_referer_or project_path(@project)
end
private
@@ -128,12 +118,7 @@ private
# Checks that the file exists and is readable
def file_readable
if @attachment.readable?
true
else
logger.error "Cannot send attachment, #{@attachment.diskfile} does not exist or is unreadable."
render_404
end
@attachment.readable? ? true : render_404
end
def read_authorize
+11 -31
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -20,77 +20,57 @@ class AuthSourcesController < ApplicationController
menu_item :ldap_authentication
before_filter :require_admin
before_filter :find_auth_source, :only => [:edit, :update, :test_connection, :destroy]
def index
@auth_source_pages, @auth_sources = paginate AuthSource, :per_page => 25
@auth_source_pages, @auth_sources = paginate AuthSource, :per_page => 10
end
def new
klass_name = params[:type] || 'AuthSourceLdap'
@auth_source = AuthSource.new_subclass_instance(klass_name, params[:auth_source])
render_404 unless @auth_source
end
def create
@auth_source = AuthSource.new_subclass_instance(params[:type], params[:auth_source])
if @auth_source.save
flash[:notice] = l(:notice_successful_create)
redirect_to auth_sources_path
redirect_to :action => 'index'
else
render :action => 'new'
end
end
def edit
@auth_source = AuthSource.find(params[:id])
end
def update
@auth_source = AuthSource.find(params[:id])
if @auth_source.update_attributes(params[:auth_source])
flash[:notice] = l(:notice_successful_update)
redirect_to auth_sources_path
redirect_to :action => 'index'
else
render :action => 'edit'
end
end
def test_connection
@auth_source = AuthSource.find(params[:id])
begin
@auth_source.test_connection
flash[:notice] = l(:notice_successful_connection)
rescue Exception => e
flash[:error] = l(:error_unable_to_connect, e.message)
end
redirect_to auth_sources_path
redirect_to :action => 'index'
end
def destroy
unless @auth_source.users.exists?
@auth_source = AuthSource.find(params[:id])
unless @auth_source.users.find(:first)
@auth_source.destroy
flash[:notice] = l(:notice_successful_delete)
end
redirect_to auth_sources_path
end
def autocomplete_for_new_user
results = AuthSource.search(params[:term])
render :json => results.map {|result| {
'value' => result[:login],
'label' => "#{result[:login]} (#{result[:firstname]} #{result[:lastname]})",
'login' => result[:login].to_s,
'firstname' => result[:firstname].to_s,
'lastname' => result[:lastname].to_s,
'mail' => result[:mail].to_s,
'auth_source_id' => result[:auth_source_id].to_s
}}
end
private
def find_auth_source
@auth_source = AuthSource.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
redirect_to :action => 'index'
end
end
+4 -4
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -23,10 +23,10 @@ class AutoCompletesController < ApplicationController
q = (params[:q] || params[:term]).to_s.strip
if q.present?
scope = (params[:scope] == "all" || @project.nil? ? Issue : @project.issues).visible
if q.match(/\A#?(\d+)\z/)
@issues << scope.find_by_id($1.to_i)
if q.match(/^\d+$/)
@issues << scope.find_by_id(q.to_i)
end
@issues += scope.where("LOWER(#{Issue.table_name}.subject) LIKE LOWER(?)", "%#{q}%").order("#{Issue.table_name}.id DESC").limit(10).all
@issues += scope.where("LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%").order("#{Issue.table_name}.id DESC").limit(10).all
@issues.compact!
end
render :layout => false
+13 -19
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -25,7 +25,7 @@ class BoardsController < ApplicationController
helper :watchers
def index
@boards = @project.boards.includes(:project, :last_message => :author).all
@boards = @project.boards.includes(:last_message => :author).all
# show the board if there is only one
if @boards.size == 1
@board = @boards.first
@@ -37,29 +37,23 @@ class BoardsController < ApplicationController
respond_to do |format|
format.html {
sort_init 'updated_on', 'desc'
sort_update 'created_on' => "#{Message.table_name}.created_on",
sort_update 'created_on' => "#{Message.table_name}.created_on",
'replies' => "#{Message.table_name}.replies_count",
'updated_on' => "COALESCE(last_replies_messages.created_on, #{Message.table_name}.created_on)"
'updated_on' => "#{Message.table_name}.updated_on"
@topic_count = @board.topics.count
@topic_pages = Paginator.new @topic_count, per_page_option, params['page']
@topics = @board.topics.
reorder("#{Message.table_name}.sticky DESC").
includes(:last_reply).
limit(@topic_pages.per_page).
offset(@topic_pages.offset).
order(sort_clause).
preload(:author, {:last_reply => :author}).
all
@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(
:include => [:author, {:last_reply => :author}],
:limit => @topic_pages.items_per_page,
:offset => @topic_pages.current.offset)
@message = Message.new(:board => @board)
render :action => 'show', :layout => !request.xhr?
}
format.atom {
@messages = @board.messages.
reorder('created_on DESC').
includes(:author, :board).
limit(Setting.feeds_limit.to_i).
all
@messages = @board.messages.find :all, :order => 'created_on DESC',
:include => [:author, :board],
:limit => Setting.feeds_limit.to_i
render_feed(@messages, :title => "#{@project}: #{@board}")
}
end
@@ -100,7 +94,7 @@ class BoardsController < ApplicationController
private
def redirect_to_settings_in_projects
redirect_to settings_project_path(@project, :tab => 'boards')
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'boards'
end
def find_board_if_available
+1 -1
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+3 -3
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -32,12 +32,12 @@ class CommentsController < ApplicationController
flash[:notice] = l(:label_comment_added)
end
redirect_to news_path(@news)
redirect_to :controller => 'news', :action => 'show', :id => @news
end
def destroy
@news.comments.find(params[:comment_id]).destroy
redirect_to news_path(@news)
redirect_to :controller => 'news', :action => 'show', :id => @news
end
private
+6 -6
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -19,15 +19,16 @@ class ContextMenusController < ApplicationController
helper :watchers
helper :issues
before_filter :find_issues, :only => :issues
def issues
@issues = Issue.visible.all(:conditions => {:id => params[:ids]}, :include => :project)
if (@issues.size == 1)
@issue = @issues.first
end
@issue_ids = @issues.map(&:id).sort
@allowed_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
@projects = @issues.collect(&:project).compact.uniq
@project = @projects.first if @projects.size == 1
@can = {:edit => User.current.allowed_to?(:edit_issues, @projects),
:log_time => (@project && User.current.allowed_to?(:log_time, @project)),
@@ -71,9 +72,8 @@ class ContextMenusController < ApplicationController
end
def time_entries
@time_entries = TimeEntry.where(:id => params[:ids]).preload(:project).to_a
(render_404; return) unless @time_entries.present?
@time_entries = TimeEntry.all(
:conditions => {:id => params[:ids]}, :include => :project)
@projects = @time_entries.collect(&:project).compact.uniq
@project = @projects.first if @projects.size == 1
@activities = TimeEntryActivity.shared.active
+12 -23
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -21,28 +21,20 @@ class CustomFieldsController < ApplicationController
before_filter :require_admin
before_filter :build_new_custom_field, :only => [:new, :create]
before_filter :find_custom_field, :only => [:edit, :update, :destroy]
accept_api_auth :index
def index
respond_to do |format|
format.html {
@custom_fields_by_type = CustomField.all.group_by {|f| f.class.name }
@tab = params[:tab] || 'IssueCustomField'
}
format.api {
@custom_fields = CustomField.all
}
end
@custom_fields_by_type = CustomField.find(:all).group_by {|f| f.class.name }
@tab = params[:tab] || 'IssueCustomField'
end
def new
end
def create
if @custom_field.save
if request.post? and @custom_field.save
flash[:notice] = l(:notice_successful_create)
call_hook(:controller_custom_fields_new_after_save, :params => params, :custom_field => @custom_field)
redirect_to custom_fields_path(:tab => @custom_field.class.name)
redirect_to :action => 'index', :tab => @custom_field.class.name
else
render :action => 'new'
end
@@ -52,22 +44,21 @@ class CustomFieldsController < ApplicationController
end
def update
if @custom_field.update_attributes(params[:custom_field])
if request.put? and @custom_field.update_attributes(params[:custom_field])
flash[:notice] = l(:notice_successful_update)
call_hook(:controller_custom_fields_edit_after_save, :params => params, :custom_field => @custom_field)
redirect_to custom_fields_path(:tab => @custom_field.class.name)
redirect_to :action => 'index', :tab => @custom_field.class.name
else
render :action => 'edit'
end
end
def destroy
begin
@custom_field.destroy
rescue
flash[:error] = l(:error_can_not_delete_custom_field)
end
redirect_to custom_fields_path(:tab => @custom_field.class.name)
@custom_field.destroy
redirect_to :action => 'index', :tab => @custom_field.class.name
rescue
flash[:error] = l(:error_can_not_delete_custom_field)
redirect_to :action => 'index'
end
private
@@ -76,8 +67,6 @@ class CustomFieldsController < ApplicationController
@custom_field = CustomField.new_subclass_instance(params[:type], params[:custom_field])
if @custom_field.nil?
render_404
else
@custom_field.default_value = nil
end
end
+7 -7
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -27,7 +27,7 @@ class DocumentsController < ApplicationController
def index
@sort_by = %w(category date title author).include?(params[:sort_by]) ? params[:sort_by] : 'category'
documents = @project.documents.includes(:attachments, :category).all
documents = @project.documents.find :all, :include => [:attachments, :category]
case @sort_by
when 'date'
@grouped = documents.group_by {|d| d.updated_on.to_date }
@@ -43,7 +43,7 @@ class DocumentsController < ApplicationController
end
def show
@attachments = @document.attachments.all
@attachments = @document.attachments.find(:all, :order => "created_on DESC")
end
def new
@@ -58,7 +58,7 @@ class DocumentsController < ApplicationController
if @document.save
render_attachment_warning_if_needed(@document)
flash[:notice] = l(:notice_successful_create)
redirect_to project_documents_path(@project)
redirect_to :action => 'index', :project_id => @project
else
render :action => 'new'
end
@@ -71,7 +71,7 @@ class DocumentsController < ApplicationController
@document.safe_attributes = params[:document]
if request.put? and @document.save
flash[:notice] = l(:notice_successful_update)
redirect_to document_path(@document)
redirect_to :action => 'show', :id => @document
else
render :action => 'edit'
end
@@ -79,7 +79,7 @@ class DocumentsController < ApplicationController
def destroy
@document.destroy if request.delete?
redirect_to project_documents_path(@project)
redirect_to :controller => 'documents', :action => 'index', :project_id => @project
end
def add_attachment
@@ -89,6 +89,6 @@ class DocumentsController < ApplicationController
if attachments.present? && attachments[:files].present? && Setting.notified_events.include?('document_added')
Mailer.attachments_added(attachments[:files]).deliver
end
redirect_to document_path(@document)
redirect_to :action => 'show', :id => @document
end
end
+12 -23
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -18,26 +18,13 @@
class EnumerationsController < ApplicationController
layout 'admin'
before_filter :require_admin, :except => :index
before_filter :require_admin_or_api_request, :only => :index
before_filter :require_admin
before_filter :build_new_enumeration, :only => [:new, :create]
before_filter :find_enumeration, :only => [:edit, :update, :destroy]
accept_api_auth :index
helper :custom_fields
def index
respond_to do |format|
format.html
format.api {
@klass = Enumeration.get_subclass(params[:type])
if @klass
@enumerations = @klass.shared.sorted.all
else
render_404
end
}
end
end
def new
@@ -46,7 +33,7 @@ class EnumerationsController < ApplicationController
def create
if request.post? && @enumeration.save
flash[:notice] = l(:notice_successful_create)
redirect_to enumerations_path
redirect_to :action => 'index', :type => @enumeration.type
else
render :action => 'new'
end
@@ -58,7 +45,7 @@ class EnumerationsController < ApplicationController
def update
if request.put? && @enumeration.update_attributes(params[:enumeration])
flash[:notice] = l(:notice_successful_update)
redirect_to enumerations_path
redirect_to :action => 'index', :type => @enumeration.type
else
render :action => 'edit'
end
@@ -68,14 +55,16 @@ class EnumerationsController < ApplicationController
if !@enumeration.in_use?
# No associated objects
@enumeration.destroy
redirect_to enumerations_path
return
elsif params[:reassign_to_id].present? && (reassign_to = @enumeration.class.find_by_id(params[:reassign_to_id].to_i))
@enumeration.destroy(reassign_to)
redirect_to enumerations_path
redirect_to :action => 'index'
return
elsif params[:reassign_to_id]
if reassign_to = @enumeration.class.find_by_id(params[:reassign_to_id])
@enumeration.destroy(reassign_to)
redirect_to :action => 'index'
return
end
end
@enumerations = @enumeration.class.system.all - [@enumeration]
@enumerations = @enumeration.class.all - [@enumeration]
end
private
+3 -3
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -31,8 +31,8 @@ class FilesController < ApplicationController
'size' => "#{Attachment.table_name}.filesize",
'downloads' => "#{Attachment.table_name}.downloads"
@containers = [ Project.includes(:attachments).reorder(sort_clause).find(@project.id)]
@containers += @project.versions.includes(:attachments).reorder(sort_clause).all.sort.reverse
@containers = [ Project.find(@project.id, :include => :attachments, :order => sort_clause)]
@containers += @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse
render :layout => !request.xhr?
end
+1 -1
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+8 -9
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -84,7 +84,7 @@ class GroupsController < ApplicationController
@group.destroy
respond_to do |format|
format.html { redirect_to(groups_path) }
format.html { redirect_to(groups_url) }
format.api { render_api_ok }
end
end
@@ -93,7 +93,7 @@ class GroupsController < ApplicationController
@users = User.find_all_by_id(params[:user_id] || params[:user_ids])
@group.users << @users if request.post?
respond_to do |format|
format.html { redirect_to edit_group_path(@group, :tab => 'users') }
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'users' }
format.js
format.api { render_api_ok }
end
@@ -102,23 +102,22 @@ class GroupsController < ApplicationController
def remove_user
@group.users.delete(User.find(params[:user_id])) if request.delete?
respond_to do |format|
format.html { redirect_to edit_group_path(@group, :tab => 'users') }
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'users' }
format.js
format.api { render_api_ok }
end
end
def autocomplete_for_user
respond_to do |format|
format.js
end
@users = User.active.not_in_group(@group).like(params[:q]).all(:limit => 100)
render :layout => false
end
def edit_membership
@membership = Member.edit_membership(params[:membership_id], params[:membership], @group)
@membership.save if request.post?
respond_to do |format|
format.html { redirect_to edit_group_path(@group, :tab => 'memberships') }
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
format.js
end
end
@@ -126,7 +125,7 @@ class GroupsController < ApplicationController
def destroy_membership
Member.find(params[:membership_id]).destroy if request.post?
respond_to do |format|
format.html { redirect_to edit_group_path(@group, :tab => 'memberships') }
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
format.js
end
end
+7 -12
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -26,14 +26,14 @@ class IssueCategoriesController < ApplicationController
def index
respond_to do |format|
format.html { redirect_to_settings_in_projects }
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project }
format.api { @categories = @project.issue_categories.all }
end
end
def show
respond_to do |format|
format.html { redirect_to_settings_in_projects }
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project }
format.api
end
end
@@ -55,7 +55,7 @@ class IssueCategoriesController < ApplicationController
respond_to do |format|
format.html do
flash[:notice] = l(:notice_successful_create)
redirect_to_settings_in_projects
redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project
end
format.js
format.api { render :action => 'show', :status => :created, :location => issue_category_path(@category) }
@@ -78,7 +78,7 @@ class IssueCategoriesController < ApplicationController
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_to_settings_in_projects
redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project
}
format.api { render_api_ok }
end
@@ -99,7 +99,7 @@ class IssueCategoriesController < ApplicationController
end
@category.destroy(reassign_to)
respond_to do |format|
format.html { redirect_to_settings_in_projects }
format.html { redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'categories' }
format.api { render_api_ok }
end
return
@@ -107,12 +107,7 @@ class IssueCategoriesController < ApplicationController
@categories = @project.issue_categories - [@category]
end
private
def redirect_to_settings_in_projects
redirect_to settings_project_path(@project, :tab => 'categories')
end
private
# Wrap ApplicationController's find_model_object method to set
# @category instead of just @issue_category
def find_model_object
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -48,9 +48,9 @@ class IssueRelationsController < ApplicationController
saved = @relation.save
respond_to do |format|
format.html { redirect_to issue_path(@issue) }
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @issue }
format.js {
@relations = @issue.reload.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
}
format.api {
if saved
@@ -67,7 +67,7 @@ class IssueRelationsController < ApplicationController
@relation.destroy
respond_to do |format|
format.html { redirect_to issue_path(@relation.issue_from) }
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 }
end
+9 -9
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -25,7 +25,7 @@ class IssueStatusesController < ApplicationController
def index
respond_to do |format|
format.html {
@issue_status_pages, @issue_statuses = paginate IssueStatus.sorted, :per_page => 25
@issue_status_pages, @issue_statuses = paginate :issue_statuses, :per_page => 25, :order => "position"
render :action => "index", :layout => false if request.xhr?
}
format.api {
@@ -40,9 +40,9 @@ class IssueStatusesController < ApplicationController
def create
@issue_status = IssueStatus.new(params[:issue_status])
if @issue_status.save
if request.post? && @issue_status.save
flash[:notice] = l(:notice_successful_create)
redirect_to issue_statuses_path
redirect_to :action => 'index'
else
render :action => 'new'
end
@@ -54,9 +54,9 @@ class IssueStatusesController < ApplicationController
def update
@issue_status = IssueStatus.find(params[:id])
if @issue_status.update_attributes(params[:issue_status])
if request.put? && @issue_status.update_attributes(params[:issue_status])
flash[:notice] = l(:notice_successful_update)
redirect_to issue_statuses_path
redirect_to :action => 'index'
else
render :action => 'edit'
end
@@ -64,10 +64,10 @@ class IssueStatusesController < ApplicationController
def destroy
IssueStatus.find(params[:id]).destroy
redirect_to issue_statuses_path
redirect_to :action => 'index'
rescue
flash[:error] = l(:error_unable_delete_issue_status)
redirect_to issue_statuses_path
redirect_to :action => 'index'
end
def update_issue_done_ratio
@@ -76,6 +76,6 @@ class IssueStatusesController < ApplicationController
else
flash[:error] = l(:error_issue_done_ratios_not_updated)
end
redirect_to issue_statuses_path
redirect_to :action => 'index'
end
end
+50 -89
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -21,11 +21,11 @@ class IssuesController < ApplicationController
before_filter :find_issue, :only => [:show, :edit, :update]
before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :destroy]
before_filter :find_project, :only => [:new, :create, :update_form]
before_filter :find_project, :only => [:new, :create]
before_filter :authorize, :except => [:index]
before_filter :find_optional_project, :only => [:index]
before_filter :check_for_default_issue_status, :only => [:new, :create]
before_filter :build_new_issue_from_params, :only => [:new, :create, :update_form]
before_filter :build_new_issue_from_params, :only => [:new, :create]
accept_rss_auth :index, :show
accept_api_auth :index, :show, :create, :update, :destroy
@@ -56,7 +56,6 @@ class IssuesController < ApplicationController
retrieve_query
sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
sort_update(@query.sortable_columns)
@query.sort_criteria = sort_criteria.to_a
if @query.valid?
case params[:format]
@@ -71,8 +70,8 @@ class IssuesController < ApplicationController
end
@issue_count = @query.issue_count
@issue_pages = Paginator.new @issue_count, @limit, params['page']
@offset ||= @issue_pages.offset
@issue_pages = Paginator.new self, @issue_count, @limit, params['page']
@offset ||= @issue_pages.current.offset
@issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
:order => sort_clause,
:offset => @offset,
@@ -82,11 +81,11 @@ class IssuesController < ApplicationController
respond_to do |format|
format.html { render :template => 'issues/index', :layout => !request.xhr? }
format.api {
Issue.load_visible_relations(@issues) if include_in_api_response?('relations')
Issue.load_relations(@issues) if include_in_api_response?('relations')
}
format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
format.csv { send_data(query_to_csv(@issues, @query, params), :type => 'text/csv; header=present', :filename => 'issues.csv') }
format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'issues.pdf') }
format.csv { send_data(issues_to_csv(@issues, @project, @query, params), :type => 'text/csv; header=present', :filename => 'export.csv') }
format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
end
else
respond_to do |format|
@@ -100,12 +99,8 @@ class IssuesController < ApplicationController
end
def show
@journals = @issue.journals.includes(:user, :details).reorder("#{Journal.table_name}.id ASC").all
@journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
@journals.each_with_index {|j,i| j.indice = i+1}
@journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
Journal.preload_journals_details_custom_fields(@journals)
# TODO: use #select! when ruby1.8 support is dropped
@journals.reject! {|journal| !journal.notes? && journal.visible_details.empty?}
@journals.reverse! if User.current.wants_comments_in_reverse_order?
@changesets = @issue.changesets.visible.all
@@ -116,8 +111,6 @@ class IssuesController < ApplicationController
@edit_allowed = User.current.allowed_to?(:edit_issues, @project)
@priorities = IssuePriority.active
@time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
@relation = IssueRelation.new
respond_to do |format|
format.html {
retrieve_previous_and_next_issue_ids
@@ -125,10 +118,7 @@ class IssuesController < ApplicationController
}
format.api
format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
format.pdf {
pdf = issue_to_pdf(@issue, :journals => @journals)
send_data(pdf, :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf")
}
format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
end
end
@@ -137,6 +127,7 @@ class IssuesController < ApplicationController
def new
respond_to do |format|
format.html { render :action => 'new', :layout => !request.xhr? }
format.js { render :partial => 'update_form' }
end
end
@@ -149,12 +140,8 @@ class IssuesController < ApplicationController
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))
if params[:continue]
attrs = {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?}
redirect_to new_project_issue_path(@issue.project, :issue => attrs)
else
redirect_to issue_path(@issue)
end
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 })
}
format.api { render :action => 'show', :status => :created, :location => issue_url(@issue) }
end
@@ -181,12 +168,11 @@ class IssuesController < ApplicationController
@issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
saved = false
begin
saved = save_issue_with_child_records
saved = @issue.save_issue_with_child_records(params, @time_entry)
rescue ActiveRecord::StaleObjectError
@conflict = true
if params[:last_journal_id]
@conflict_journals = @issue.journals_after(params[:last_journal_id]).all
@conflict_journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
end
end
@@ -195,7 +181,7 @@ class IssuesController < ApplicationController
flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
respond_to do |format|
format.html { redirect_back_or_default issue_path(@issue) }
format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
format.api { render_api_ok }
end
else
@@ -206,11 +192,6 @@ class IssuesController < ApplicationController
end
end
# Updates the issue form when changing the project, status or tracker
# on issue creation/update
def update_form
end
# Bulk edit/copy a set of issues
def bulk_edit
@issues.sort!
@@ -233,7 +214,7 @@ class IssuesController < ApplicationController
else
@available_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
end
@custom_fields = target_projects.map{|p|p.all_issue_custom_fields.visible}.reduce(:&)
@custom_fields = target_projects.map{|p|p.all_issue_custom_fields}.reduce(:&)
@assignables = target_projects.map(&:assignable_users).reduce(:&)
@trackers = target_projects.map(&:trackers).reduce(:&)
@versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&)
@@ -244,9 +225,7 @@ class IssuesController < ApplicationController
end
@safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
@issue_params = params[:issue] || {}
@issue_params[:custom_field_values] ||= {}
render :layout => false if request.xhr?
end
def bulk_update
@@ -254,8 +233,8 @@ class IssuesController < ApplicationController
@copy = params[:copy].present?
attributes = parse_params_for_bulk_issue_attributes(params)
unsaved_issues = []
saved_issues = []
unsaved_issue_ids = []
moved_issues = []
if @copy && params[:copy_subtasks].present?
# Descendant issues will be copied with the parent task
@@ -263,48 +242,39 @@ class IssuesController < ApplicationController
@issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}}
end
@issues.each do |orig_issue|
orig_issue.reload
@issues.each do |issue|
issue.reload
if @copy
issue = orig_issue.copy({},
issue = issue.copy({},
:attachments => params[:copy_attachments].present?,
:subtasks => params[:copy_subtasks].present?
)
else
issue = orig_issue
end
journal = issue.init_journal(User.current, params[:notes])
issue.safe_attributes = attributes
call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
if issue.save
saved_issues << issue
moved_issues << issue
else
unsaved_issues << orig_issue
# Keep unsaved issue ids to display them in flash error
unsaved_issue_ids << issue.id
end
end
set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
if unsaved_issues.empty?
flash[:notice] = l(:notice_successful_update) unless saved_issues.empty?
if params[:follow]
if @issues.size == 1 && saved_issues.size == 1
redirect_to issue_path(saved_issues.first)
elsif saved_issues.map(&:project).uniq.size == 1
redirect_to project_issues_path(saved_issues.map(&:project).first)
end
else
redirect_back_or_default _project_issues_path(@project)
if params[:follow]
if @issues.size == 1 && moved_issues.size == 1
redirect_to :controller => 'issues', :action => 'show', :id => moved_issues.first
elsif moved_issues.map(&:project).uniq.size == 1
redirect_to :controller => 'issues', :action => 'index', :project_id => moved_issues.map(&:project).first
end
else
@saved_issues = @issues
@unsaved_issues = unsaved_issues
@issues = Issue.visible.find_all_by_id(@unsaved_issues.map(&:id))
bulk_edit
render :action => 'bulk_edit'
redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project})
end
end
def destroy
@hours = TimeEntry.where(:issue_id => @issues.map(&:id)).sum(:hours).to_f
@hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
if @hours > 0
case params[:todo]
when 'destroy'
@@ -332,12 +302,24 @@ class IssuesController < ApplicationController
end
end
respond_to do |format|
format.html { redirect_back_or_default _project_issues_path(@project) }
format.html { redirect_back_or_default(:action => 'index', :project_id => @project) }
format.api { render_api_ok }
end
end
private
private
def find_issue
# Issue.visible.find(...) can not be used to redirect user to the login form
# if the issue actually exists but requires authentication
@issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
unless @issue.visible?
deny_access
return
end
@project = @issue.project
rescue ActiveRecord::RecordNotFound
render_404
end
def find_project
project_id = params[:project_id] || (params[:issue] && params[:issue][:project_id])
@@ -372,7 +354,8 @@ class IssuesController < ApplicationController
@time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
@time_entry.attributes = params[:time_entry]
@issue.init_journal(User.current)
@notes = params[:notes] || (params[:issue].present? ? params[:issue][:notes] : nil)
@issue.init_journal(User.current, @notes)
issue_attributes = params[:issue]
if issue_attributes && params[:conflict_resolution]
@@ -381,7 +364,7 @@ class IssuesController < ApplicationController
issue_attributes = issue_attributes.dup
issue_attributes.delete(:lock_version)
when 'add_notes'
issue_attributes = issue_attributes.slice(:notes)
issue_attributes = {}
when 'cancel'
redirect_to issue_path(@issue)
return false
@@ -452,26 +435,4 @@ class IssuesController < ApplicationController
end
attributes
end
# Saves @issue and a time_entry from the parameters
def save_issue_with_child_records
Issue.transaction do
if params[:time_entry] && (params[:time_entry][:hours].present? || params[:time_entry][:comments].present?) && User.current.allowed_to?(:log_time, @issue.project)
time_entry = @time_entry || TimeEntry.new
time_entry.project = @issue.project
time_entry.issue = @issue
time_entry.user = User.current
time_entry.spent_on = User.current.today
time_entry.attributes = params[:time_entry]
@issue.time_entries << time_entry
end
call_hook(:controller_issues_edit_before_save, { :params => params, :issue => @issue, :time_entry => time_entry, :journal => @issue.current_journal})
if @issue.save
call_hook(:controller_issues_edit_after_save, { :params => params, :issue => @issue, :time_entry => time_entry, :journal => @issue.current_journal})
else
raise ActiveRecord::Rollback
end
end
end
end
+15 -9
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -57,10 +57,10 @@ class JournalsController < ApplicationController
end
def new
@journal = Journal.visible.find(params[:journal_id]) if params[:journal_id]
if @journal
user = @journal.user
text = @journal.notes
journal = Journal.find(params[:journal_id]) if params[:journal_id]
if journal
user = journal.user
text = journal.notes
else
user = @issue.author
text = @issue.description
@@ -69,8 +69,6 @@ class JournalsController < ApplicationController
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"
rescue ActiveRecord::RecordNotFound
render_404
end
def edit
@@ -80,7 +78,7 @@ class JournalsController < ApplicationController
@journal.destroy if @journal.details.empty? && @journal.notes.blank?
call_hook(:controller_journals_edit_post, { :journal => @journal, :params => params})
respond_to do |format|
format.html { redirect_to issue_path(@journal.journalized) }
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @journal.journalized_id }
format.js { render :action => 'update' }
end
else
@@ -97,9 +95,17 @@ class JournalsController < ApplicationController
private
def find_journal
@journal = Journal.visible.find(params[:id])
@journal = Journal.find(params[:id])
@project = @journal.journalized.project
rescue ActiveRecord::RecordNotFound
render_404
end
# TODO: duplicated in IssuesController
def find_issue
@issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
@project = @issue.project
rescue ActiveRecord::RecordNotFound
render_404
end
end
+1 -1
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+8 -15
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -26,8 +26,8 @@ class MembersController < ApplicationController
def index
@offset, @limit = api_offset_and_limit
@member_count = @project.member_principals.count
@member_pages = Paginator.new @member_count, @limit, params['page']
@offset ||= @member_pages.offset
@member_pages = Paginator.new self, @member_count, @limit, params['page']
@offset ||= @member_pages.current.offset
@members = @project.member_principals.all(
:order => "#{Member.table_name}.id",
:limit => @limit,
@@ -63,7 +63,7 @@ class MembersController < ApplicationController
end
respond_to do |format|
format.html { redirect_to_settings_in_projects }
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
format.js { @members = members }
format.api {
@member = members.first
@@ -82,7 +82,7 @@ class MembersController < ApplicationController
end
saved = @member.save
respond_to do |format|
format.html { redirect_to_settings_in_projects }
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
format.js
format.api {
if saved
@@ -99,7 +99,7 @@ class MembersController < ApplicationController
@member.destroy
end
respond_to do |format|
format.html { redirect_to_settings_in_projects }
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
format.js
format.api {
if @member.destroyed?
@@ -112,14 +112,7 @@ class MembersController < ApplicationController
end
def autocomplete
respond_to do |format|
format.js
end
end
private
def redirect_to_settings_in_projects
redirect_to settings_project_path(@project, :tab => 'members')
@principals = Principal.active.not_member_of(@project).like(params[:q]).all(:limit => 100)
render :layout => false
end
end
+9 -12
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -19,7 +19,6 @@ class MessagesController < ApplicationController
menu_item :boards
default_search_scope :messages
before_filter :find_board, :only => [:new, :preview]
before_filter :find_attachments, :only => [:preview]
before_filter :find_message, :except => [:new, :preview]
before_filter :authorize, :except => [:preview, :edit, :destroy]
@@ -35,18 +34,16 @@ class MessagesController < ApplicationController
page = params[:page]
# Find the page of the requested reply
if params[:r] && page.nil?
offset = @topic.children.where("#{Message.table_name}.id < ?", params[:r].to_i).count
offset = @topic.children.count(:conditions => ["#{Message.table_name}.id < ?", params[:r].to_i])
page = 1 + offset / REPLIES_PER_PAGE
end
@reply_count = @topic.children.count
@reply_pages = Paginator.new @reply_count, REPLIES_PER_PAGE, page
@replies = @topic.children.
includes(:author, :attachments, {:board => :project}).
reorder("#{Message.table_name}.created_on ASC").
limit(@reply_pages.per_page).
offset(@reply_pages.offset).
all
@reply_pages = Paginator.new self, @reply_count, REPLIES_PER_PAGE, page
@replies = @topic.children.find(:all, :include => [:author, :attachments, {:board => :project}],
:order => "#{Message.table_name}.created_on ASC",
:limit => @reply_pages.items_per_page,
:offset => @reply_pages.current.offset)
@reply = Message.new(:subject => "RE: #{@message.subject}")
render :action => "show", :layout => false if request.xhr?
@@ -118,6 +115,7 @@ class MessagesController < ApplicationController
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'
@@ -125,7 +123,7 @@ class MessagesController < ApplicationController
private
def find_message
return unless find_board
find_board
@message = @board.messages.find(params[:id], :include => :parent)
@topic = @message.root
rescue ActiveRecord::RecordNotFound
@@ -137,6 +135,5 @@ private
@project = @board.project
rescue ActiveRecord::RecordNotFound
render_404
nil
end
end
+23 -27
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -17,8 +17,6 @@
class MyController < ApplicationController
before_filter :require_login
# let user change user's password when user has to
skip_before_filter :check_password_change, :only => :password
helper :issues
helper :users
@@ -55,11 +53,13 @@ class MyController < ApplicationController
if request.post?
@user.safe_attributes = params[:user]
@user.pref.attributes = params[:pref]
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
if @user.save
@user.pref.save
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
set_language_if_valid @user.language
flash[:notice] = l(:notice_account_updated)
redirect_to my_account_path
redirect_to :action => 'account'
return
end
end
@@ -69,7 +69,7 @@ class MyController < ApplicationController
def destroy
@user = User.current
unless @user.own_account_deletable?
redirect_to my_account_path
redirect_to :action => 'account'
return
end
@@ -88,21 +88,18 @@ class MyController < ApplicationController
@user = User.current
unless @user.change_password_allowed?
flash[:error] = l(:notice_can_t_change_password)
redirect_to my_account_path
redirect_to :action => 'account'
return
end
if request.post?
if !@user.check_password?(params[:password])
flash.now[:error] = l(:notice_account_wrong_password)
elsif params[:password] == params[:new_password]
flash.now[:error] = l(:notice_new_password_must_be_different)
else
if @user.check_password?(params[:password])
@user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
@user.must_change_passwd = false
if @user.save
flash[:notice] = l(:notice_account_password_updated)
redirect_to my_account_path
redirect_to :action => 'account'
end
else
flash[:error] = l(:notice_account_wrong_password)
end
end
end
@@ -117,7 +114,7 @@ class MyController < ApplicationController
User.current.rss_key
flash[:notice] = l(:notice_feeds_access_key_reseted)
end
redirect_to my_account_path
redirect_to :action => 'account'
end
# Create a new API key
@@ -130,7 +127,7 @@ class MyController < ApplicationController
User.current.api_key
flash[:notice] = l(:notice_api_access_key_reseted)
end
redirect_to my_account_path
redirect_to :action => 'account'
end
# User's page layout configuration
@@ -150,17 +147,16 @@ class MyController < ApplicationController
# params[:block] : id of the block to add
def add_block
block = params[:block].to_s.underscore
if block.present? && BLOCKS.key?(block)
@user = User.current
layout = @user.pref[:my_page_layout] || {}
# remove if already present in a group
%w(top left right).each {|f| (layout[f] ||= []).delete block }
# add it on top
layout['top'].unshift block
@user.pref[:my_page_layout] = layout
@user.pref.save
end
redirect_to my_page_layout_path
(render :nothing => true; return) unless block && (BLOCKS.keys.include? block)
@user = User.current
layout = @user.pref[:my_page_layout] || {}
# remove if already present in a group
%w(top left right).each {|f| (layout[f] ||= []).delete block }
# add it on top
layout['top'].unshift block
@user.pref[:my_page_layout] = layout
@user.pref.save
redirect_to :action => 'page_layout'
end
# Remove a block to user's page
@@ -173,7 +169,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 my_page_layout_path
redirect_to :action => 'page_layout'
end
# Change blocks order on user's page
+6 -6
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -40,8 +40,8 @@ class NewsController < ApplicationController
scope = @project ? @project.news.visible : News.visible
@news_count = scope.count
@news_pages = Paginator.new @news_count, @limit, params['page']
@offset ||= @news_pages.offset
@news_pages = Paginator.new self, @news_count, @limit, params['page']
@offset ||= @news_pages.current.offset
@newss = scope.all(:include => [:author, :project],
:order => "#{News.table_name}.created_on DESC",
:offset => @offset,
@@ -73,7 +73,7 @@ class NewsController < ApplicationController
if @news.save
render_attachment_warning_if_needed(@news)
flash[:notice] = l(:notice_successful_create)
redirect_to project_news_index_path(@project)
redirect_to :controller => 'news', :action => 'index', :project_id => @project
else
render :action => 'new'
end
@@ -88,7 +88,7 @@ class NewsController < ApplicationController
if @news.save
render_attachment_warning_if_needed(@news)
flash[:notice] = l(:notice_successful_update)
redirect_to news_path(@news)
redirect_to :action => 'show', :id => @news
else
render :action => 'edit'
end
@@ -96,7 +96,7 @@ class NewsController < ApplicationController
def destroy
@news.destroy
redirect_to project_news_index_path(@project)
redirect_to :action => 'index', :project_id => @project
end
private
+4 -7
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -16,17 +16,17 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class PreviewsController < ApplicationController
before_filter :find_project, :find_attachments
before_filter :find_project
def issue
@issue = @project.issues.find_by_id(params[:id]) unless params[:id].blank?
if @issue
@attachements = @issue.attachments
@description = params[:issue] && params[:issue][:description]
if @description && @description.gsub(/(\r?\n|\n\r?)/, "\n") == @issue.description.to_s.gsub(/(\r?\n|\n\r?)/, "\n")
@description = nil
end
# params[:notes] is useful for preview of notes in issue history
@notes = params[:notes] || (params[:issue] ? params[:issue][:notes] : nil)
@notes = params[:notes]
else
@description = (params[:issue] ? params[:issue][:description] : nil)
end
@@ -34,9 +34,6 @@ class PreviewsController < ApplicationController
end
def news
if params[:id].present? && news = News.visible.find_by_id(params[:id])
@previewed = news
end
@text = (params[:news] ? params[:news][:description] : nil)
render :partial => 'common/preview'
end
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -29,7 +29,7 @@ class ProjectEnumerationsController < ApplicationController
flash[:notice] = l(:notice_successful_update)
end
redirect_to settings_project_path(@project, :tab => 'activities')
redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project
end
def destroy
@@ -37,6 +37,7 @@ class ProjectEnumerationsController < ApplicationController
time_entry_activity.destroy(time_entry_activity.parent)
end
flash[:notice] = l(:notice_successful_update)
redirect_to settings_project_path(@project, :tab => 'activities')
redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project
end
end
+35 -30
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -43,7 +43,6 @@ class ProjectsController < ApplicationController
helper :repositories
include RepositoriesHelper
include ProjectsHelper
helper :members
# Lists visible projects
def index
@@ -58,31 +57,32 @@ class ProjectsController < ApplicationController
format.api {
@offset, @limit = api_offset_and_limit
@project_count = Project.visible.count
@projects = Project.visible.offset(@offset).limit(@limit).order('lft').all
@projects = Project.visible.all(:offset => @offset, :limit => @limit, :order => 'lft')
}
format.atom {
projects = Project.visible.order('created_on DESC').limit(Setting.feeds_limit.to_i).all
projects = Project.visible.find(:all, :order => 'created_on DESC',
:limit => Setting.feeds_limit.to_i)
render_feed(projects, :title => "#{Setting.app_title}: #{l(:label_project_latest)}")
}
end
end
def new
@issue_custom_fields = IssueCustomField.sorted.all
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.sorted.all
@project = Project.new
@project.safe_attributes = params[:project]
end
def create
@issue_custom_fields = IssueCustomField.sorted.all
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.sorted.all
@project = Project.new
@project.safe_attributes = params[:project]
if validate_parent_id && @project.save
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
# Add current user as a project member if current user is not admin
# Add current user as a project member if he is not admin
unless User.current.admin?
r = Role.givable.find_by_id(Setting.new_project_user_role_id.to_i) || Role.givable.first
m = Member.new(:user => User.current, :roles => [r])
@@ -91,12 +91,10 @@ class ProjectsController < ApplicationController
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_create)
if params[:continue]
attrs = {:parent_id => @project.parent_id}.reject {|k,v| v.nil?}
redirect_to new_project_path(attrs)
else
redirect_to settings_project_path(@project)
end
redirect_to(params[:continue] ?
{:controller => 'projects', :action => 'new', :project => {:parent_id => @project.parent_id}.reject {|k,v| v.nil?}} :
{:controller => 'projects', :action => 'settings', :id => @project}
)
}
format.api { render :action => 'show', :status => :created, :location => url_for(:controller => 'projects', :action => 'show', :id => @project.id) }
end
@@ -106,15 +104,23 @@ class ProjectsController < ApplicationController
format.api { render_validation_errors(@project) }
end
end
end
def copy
@issue_custom_fields = IssueCustomField.sorted.all
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.sorted.all
@root_projects = Project.find(:all,
:conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}",
:order => 'name')
@source_project = Project.find(params[:id])
if request.get?
@project = Project.copy_from(@source_project)
@project.identifier = Project.next_identifier if Setting.sequential_project_identifiers?
if @project
@project.identifier = Project.next_identifier if Setting.sequential_project_identifiers?
else
redirect_to :controller => 'admin', :action => 'projects'
end
else
Mailer.with_deliveries(params[:notifications] == '1') do
@project = Project.new
@@ -122,31 +128,30 @@ class ProjectsController < ApplicationController
if validate_parent_id && @project.copy(@source_project, :only => params[:only])
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
flash[:notice] = l(:notice_successful_create)
redirect_to settings_project_path(@project)
redirect_to :controller => 'projects', :action => 'settings', :id => @project
elsif !@project.new_record?
# Project was created
# But some objects were not copied due to validation failures
# (eg. issues from disabled trackers)
# TODO: inform about that
redirect_to settings_project_path(@project)
redirect_to :controller => 'projects', :action => 'settings', :id => @project
end
end
end
rescue ActiveRecord::RecordNotFound
# source_project not found
render_404
redirect_to :controller => 'admin', :action => 'projects'
end
# Show @project
def show
# try to redirect to the requested menu item
if params[:jump] && redirect_to_project_menu_item(@project, params[:jump])
return
if params[:jump]
# try to redirect to the requested menu item
redirect_to_project_menu_item(@project, params[:jump]) && return
end
@users_by_role = @project.users_by_role
@subprojects = @project.children.visible.all
@news = @project.news.limit(5).includes(:author, :project).reorder("#{News.table_name}.created_on DESC").all
@news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
@trackers = @project.rolled_up_trackers
cond = @project.project_condition(Setting.display_subprojects_issues?)
@@ -155,7 +160,7 @@ class ProjectsController < ApplicationController
@total_issues_by_tracker = Issue.visible.where(cond).count(:group => :tracker)
if User.current.allowed_to?(:view_time_entries, @project)
@total_hours = TimeEntry.visible.where(cond).sum(:hours).to_f
@total_hours = TimeEntry.visible.sum(:hours, :include => :project, :conditions => cond).to_f
end
@key = User.current.rss_key
@@ -167,7 +172,7 @@ class ProjectsController < ApplicationController
end
def settings
@issue_custom_fields = IssueCustomField.sorted.all
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@issue_category ||= IssueCategory.new
@member ||= @project.members.new
@trackers = Tracker.sorted.all
@@ -184,7 +189,7 @@ class ProjectsController < ApplicationController
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_to settings_project_path(@project)
redirect_to :action => 'settings', :id => @project
}
format.api { render_api_ok }
end
@@ -202,7 +207,7 @@ class ProjectsController < ApplicationController
def modules
@project.enabled_module_names = params[:enabled_module_names]
flash[:notice] = l(:notice_successful_update)
redirect_to settings_project_path(@project, :tab => 'modules')
redirect_to :action => 'settings', :id => @project, :tab => 'modules'
end
def archive
@@ -211,12 +216,12 @@ class ProjectsController < ApplicationController
flash[:error] = l(:error_can_not_archive_project)
end
end
redirect_to admin_projects_path(:status => params[:status])
redirect_to(url_for(:controller => 'admin', :action => 'projects', :status => params[:status]))
end
def unarchive
@project.unarchive if request.post? && !@project.active?
redirect_to admin_projects_path(:status => params[:status])
redirect_to(url_for(:controller => 'admin', :action => 'projects', :status => params[:status]))
end
def close
@@ -235,7 +240,7 @@ class ProjectsController < ApplicationController
if api_request? || params[:confirm]
@project_to_destroy.destroy
respond_to do |format|
format.html { redirect_to admin_projects_path }
format.html { redirect_to :controller => 'admin', :action => 'projects' }
format.api { render_api_ok }
end
end
+17 -28
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -32,34 +32,35 @@ class QueriesController < ApplicationController
@limit = per_page_option
end
@query_count = IssueQuery.visible.count
@query_pages = Paginator.new @query_count, @limit, params['page']
@queries = IssueQuery.visible.all(:limit => @limit, :offset => @offset, :order => "#{Query.table_name}.name")
@query_count = Query.visible.count
@query_pages = Paginator.new self, @query_count, @limit, params['page']
@queries = Query.visible.all(:limit => @limit, :offset => @offset, :order => "#{Query.table_name}.name")
respond_to do |format|
format.html { render :nothing => true }
format.api
end
end
def new
@query = IssueQuery.new
@query = Query.new
@query.user = User.current
@query.project = @project
@query.visibility = IssueQuery::VISIBILITY_PRIVATE unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
@query.build_from_params(params)
@query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
build_query_from_params
end
def create
@query = IssueQuery.new(params[:query])
@query = Query.new(params[:query])
@query.user = User.current
@query.project = params[:query_is_for_all] ? nil : @project
@query.visibility = IssueQuery::VISIBILITY_PRIVATE unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
@query.build_from_params(params)
@query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
build_query_from_params
@query.column_names = nil if params[:default_columns]
if @query.save
flash[:notice] = l(:notice_successful_create)
redirect_to_issues(:query_id => @query)
redirect_to :controller => 'issues', :action => 'index', :project_id => @project, :query_id => @query
else
render :action => 'new', :layout => !request.xhr?
end
@@ -71,13 +72,13 @@ class QueriesController < ApplicationController
def update
@query.attributes = params[:query]
@query.project = nil if params[:query_is_for_all]
@query.visibility = IssueQuery::VISIBILITY_PRIVATE unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
@query.build_from_params(params)
@query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
build_query_from_params
@query.column_names = nil if params[:default_columns]
if @query.save
flash[:notice] = l(:notice_successful_update)
redirect_to_issues(:query_id => @query)
redirect_to :controller => 'issues', :action => 'index', :project_id => @project, :query_id => @query
else
render :action => 'edit'
end
@@ -85,12 +86,12 @@ class QueriesController < ApplicationController
def destroy
@query.destroy
redirect_to_issues(:set_filter => 1)
redirect_to :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1
end
private
def find_query
@query = IssueQuery.find(params[:id])
@query = Query.find(params[:id])
@project = @query.project
render_403 unless @query.editable_by?(User.current)
rescue ActiveRecord::RecordNotFound
@@ -103,16 +104,4 @@ private
rescue ActiveRecord::RecordNotFound
render_404
end
def redirect_to_issues(options)
if params[:gantt]
if @project
redirect_to project_gantt_path(@project, options)
else
redirect_to issues_gantt_path(options)
end
else
redirect_to _project_issues_path(@project, options)
end
end
end
+2 -2
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -90,6 +90,6 @@ class ReportsController < ApplicationController
private
def find_issue_statuses
@statuses = IssueStatus.sorted.all
@statuses = IssueStatus.find(:all, :order => 'position')
end
end
+16 -20
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -111,7 +111,7 @@ class RepositoriesController < ApplicationController
end
def show
@repository.fetch_changesets if @project.active? && Setting.autofetch_changesets? && @path.empty?
@repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty?
@entries = @repository.entries(@path, @rev)
@changeset = @repository.find_changeset_by_name(@rev)
@@ -138,14 +138,13 @@ class RepositoriesController < ApplicationController
def revisions
@changeset_count = @repository.changesets.count
@changeset_pages = Paginator.new @changeset_count,
@changeset_pages = Paginator.new self, @changeset_count,
per_page_option,
params['page']
@changesets = @repository.changesets.
limit(@changeset_pages.per_page).
offset(@changeset_pages.offset).
includes(:user, :repository, :parents).
all
@changesets = @repository.changesets.find(:all,
:limit => @changeset_pages.items_per_page,
:offset => @changeset_pages.current.offset,
:include => [:user, :repository, :parents])
respond_to do |format|
format.html { render :layout => false if request.xhr? }
@@ -352,18 +351,15 @@ 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.
where("repository_id = ? AND commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to).
group(:commit_date).
count
commits_by_day = Changeset.count(
:all, :group => :commit_date,
:conditions => ["repository_id = ? AND commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
commits_by_month = [0] * 12
commits_by_day.each {|c| commits_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last }
changes_by_day = Change.
joins(:changeset).
where("#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to).
group(:commit_date).
count
changes_by_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_month = [0] * 12
changes_by_day.each {|c| changes_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last }
@@ -396,10 +392,10 @@ class RepositoriesController < ApplicationController
end
def graph_commits_per_author(repository)
commits_by_author = Changeset.where("repository_id = ?", repository.id).group(:committer).count
commits_by_author = Changeset.count(:all, :group => :committer, :conditions => ["repository_id = ?", repository.id])
commits_by_author.to_a.sort! {|x, y| x.last <=> y.last}
changes_by_author = Change.joins(:changeset).where("#{Changeset.table_name}.repository_id = ?", repository.id).group(:committer).count
changes_by_author = Change.count(:all, :group => :committer, :include => :changeset, :conditions => ["#{Changeset.table_name}.repository_id = ?", repository.id])
h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
fields = commits_by_author.collect {|r| r.first}
@@ -414,7 +410,7 @@ class RepositoriesController < ApplicationController
fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
graph = SVG::Graph::BarHorizontal.new(
:height => 30 * commits_data.length,
:height => 400,
:width => 800,
:fields => fields,
:stack => :side,
+11 -17
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -18,15 +18,15 @@
class RolesController < ApplicationController
layout 'admin'
before_filter :require_admin, :except => [:index, :show]
before_filter :require_admin_or_api_request, :only => [:index, :show]
before_filter :find_role, :only => [:show, :edit, :update, :destroy]
accept_api_auth :index, :show
before_filter :require_admin, :except => :index
before_filter :require_admin_or_api_request, :only => :index
before_filter :find_role, :only => [:edit, :update, :destroy]
accept_api_auth :index
def index
respond_to do |format|
format.html {
@role_pages, @roles = paginate Role.sorted, :per_page => 25
@role_pages, @roles = paginate :roles, :per_page => 25, :order => 'builtin, position'
render :action => "index", :layout => false if request.xhr?
}
format.api {
@@ -35,12 +35,6 @@ class RolesController < ApplicationController
end
end
def show
respond_to do |format|
format.api
end
end
def new
# Prefills the form with 'Non member' role permissions by default
@role = Role.new(params[:role] || {:permissions => Role.non_member.permissions})
@@ -58,7 +52,7 @@ class RolesController < ApplicationController
@role.workflow_rules.copy(copy_from)
end
flash[:notice] = l(:notice_successful_create)
redirect_to roles_path
redirect_to :action => 'index'
else
@roles = Role.sorted.all
render :action => 'new'
@@ -71,7 +65,7 @@ class RolesController < ApplicationController
def update
if request.put? and @role.update_attributes(params[:role])
flash[:notice] = l(:notice_successful_update)
redirect_to roles_path
redirect_to :action => 'index'
else
render :action => 'edit'
end
@@ -79,10 +73,10 @@ class RolesController < ApplicationController
def destroy
@role.destroy
redirect_to roles_path
redirect_to :action => 'index'
rescue
flash[:error] = l(:error_can_not_remove_role)
redirect_to roles_path
redirect_to :action => 'index'
end
def permissions
@@ -94,7 +88,7 @@ class RolesController < ApplicationController
role.save
end
flash[:notice] = l(:notice_successful_update)
redirect_to roles_path
redirect_to :action => 'index'
end
end
+6 -3
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -18,6 +18,9 @@
class SearchController < ApplicationController
before_filter :find_optional_project
helper :messages
include MessagesHelper
def index
@question = params[:q] || ""
@question.strip!
@@ -40,8 +43,8 @@ class SearchController < ApplicationController
begin; offset = params[:offset].to_time if params[:offset]; rescue; end
# quick jump to an issue
if (m = @question.match(/^#?(\d+)$/)) && (issue = Issue.visible.find_by_id(m[1].to_i))
redirect_to issue_path(issue)
if @question.match(/^#?(\d+)$/) && Issue.visible.find_by_id($1.to_i)
redirect_to :controller => "issues", :action => "show", :id => $1
return
end
+7 -16
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -19,8 +19,6 @@ class SettingsController < ApplicationController
layout 'admin'
menu_item :plugins, :only => :plugin
helper :queries
before_filter :require_admin
def index
@@ -33,37 +31,30 @@ class SettingsController < ApplicationController
if request.post? && params[:settings] && params[:settings].is_a?(Hash)
settings = (params[:settings] || {}).dup.symbolize_keys
settings.each do |name, value|
Setting.set_from_params name, value
# remove blank values in array settings
value.delete_if {|v| v.blank? } if value.is_a?(Array)
Setting[name] = value
end
flash[:notice] = l(:notice_successful_update)
redirect_to settings_path(:tab => params[:tab])
redirect_to :action => 'edit', :tab => params[:tab]
else
@options = {}
user_format = User::USER_FORMATS.collect{|key, value| [key, value[:setting_order]]}.sort{|a, b| a[1] <=> b[1]}
@options[:user_format] = user_format.collect{|f| [User.current.name(f[0]), f[0].to_s]}
@options[:user_format] = User::USER_FORMATS.keys.collect {|f| [User.current.name(f), f.to_s] }
@deliveries = ActionMailer::Base.perform_deliveries
@guessed_host_and_path = request.host_with_port.dup
@guessed_host_and_path << ('/'+ Redmine::Utils.relative_url_root.gsub(%r{^\/}, '')) unless Redmine::Utils.relative_url_root.blank?
@commit_update_keywords = Setting.commit_update_keywords.dup
@commit_update_keywords = [{}] unless @commit_update_keywords.is_a?(Array) && @commit_update_keywords.any?
Redmine::Themes.rescan
end
end
def plugin
@plugin = Redmine::Plugin.find(params[:id])
unless @plugin.configurable?
render_404
return
end
if request.post?
Setting.send "plugin_#{@plugin.id}=", params[:settings]
flash[:notice] = l(:notice_successful_update)
redirect_to plugin_settings_path(@plugin)
redirect_to :action => 'plugin', :id => @plugin.id
else
@partial = @plugin.settings[:partial]
@settings = Setting.send "plugin_#{@plugin.id}"
+6 -2
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -19,7 +19,11 @@ class SysController < ActionController::Base
before_filter :check_enabled
def projects
p = Project.active.has_module(:repository).order("#{Project.table_name}.identifier").preload(:repository).all
p = Project.active.has_module(:repository).find(
:all,
:include => :repository,
:order => "#{Project.table_name}.identifier"
)
# extra_info attribute from repository breaks activeresource client
render :xml => p.to_xml(
:only => [:id, :identifier, :name, :is_public, :status],
+89 -42
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -30,31 +30,42 @@ class TimelogController < ApplicationController
accept_rss_auth :index
accept_api_auth :index, :show, :create, :update, :destroy
rescue_from Query::StatementInvalid, :with => :query_statement_invalid
helper :sort
include SortHelper
helper :issues
include TimelogHelper
helper :custom_fields
include CustomFieldsHelper
helper :queries
include QueriesHelper
def index
@query = TimeEntryQuery.build_from_params(params, :project => @project, :name => '_')
sort_init 'spent_on', 'desc'
sort_update 'spent_on' => ['spent_on', "#{TimeEntry.table_name}.created_on"],
'user' => 'user_id',
'activity' => 'activity_id',
'project' => "#{Project.table_name}.name",
'issue' => 'issue_id',
'hours' => 'hours'
sort_init(@query.sort_criteria.empty? ? [['spent_on', 'desc']] : @query.sort_criteria)
sort_update(@query.sortable_columns)
scope = time_entry_scope(:order => sort_clause).
includes(:project, :activity, :user, :issue).
preload(:issue => [:project, :tracker, :status, :assigned_to, :priority])
retrieve_date_range
scope = TimeEntry.visible.spent_between(@from, @to)
if @issue
scope = scope.on_issue(@issue)
elsif @project
scope = scope.on_project(@project, Setting.display_subprojects_issues?)
end
respond_to do |format|
format.html {
# Paginate results
@entry_count = scope.count
@entry_pages = Paginator.new @entry_count, per_page_option, params['page']
@entries = scope.offset(@entry_pages.offset).limit(@entry_pages.per_page).all
@entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
@entries = scope.all(
:include => [:project, :activity, :user, {:issue => :tracker}],
:order => sort_clause,
:limit => @entry_pages.items_per_page,
:offset => @entry_pages.current.offset
)
@total_hours = scope.sum(:hours).to_f
render :layout => !request.xhr?
@@ -62,25 +73,35 @@ class TimelogController < ApplicationController
format.api {
@entry_count = scope.count
@offset, @limit = api_offset_and_limit
@entries = scope.offset(@offset).limit(@limit).preload(:custom_values => :custom_field).all
@entries = scope.all(
:include => [:project, :activity, :user, {:issue => :tracker}],
:order => sort_clause,
:limit => @limit,
:offset => @offset
)
}
format.atom {
entries = scope.limit(Setting.feeds_limit.to_i).reorder("#{TimeEntry.table_name}.created_on DESC").all
entries = scope.all(
:include => [:project, :activity, :user, {:issue => :tracker}],
:order => "#{TimeEntry.table_name}.created_on DESC",
:limit => Setting.feeds_limit.to_i
)
render_feed(entries, :title => l(:label_spent_time))
}
format.csv {
# Export all entries
@entries = scope.all
send_data(query_to_csv(@entries, @query, params), :type => 'text/csv; header=present', :filename => 'timelog.csv')
@entries = scope.all(
:include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}],
:order => sort_clause
)
send_data(entries_to_csv(@entries), :type => 'text/csv; header=present', :filename => 'timelog.csv')
}
end
end
def report
@query = TimeEntryQuery.build_from_params(params, :project => @project, :name => '_')
scope = time_entry_scope
@report = Redmine::Helpers::TimeReport.new(@project, @issue, params[:criteria], params[:columns], scope)
retrieve_date_range
@report = Redmine::Helpers::TimeReport.new(@project, @issue, params[:criteria], params[:columns], @from, @to)
respond_to do |format|
format.html { render :layout => !request.xhr? }
@@ -113,24 +134,16 @@ class TimelogController < ApplicationController
flash[:notice] = l(:notice_successful_create)
if params[:continue]
if params[:project_id]
options = {
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]
}
if @time_entry.issue
redirect_to new_project_issue_time_entry_path(@time_entry.project, @time_entry.issue, options)
else
redirect_to new_project_time_entry_path(@time_entry.project, options)
end
else
options = {
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 new_time_entry_path(options)
end
else
redirect_back_or_default project_time_entries_path(@time_entry.project)
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
end
}
format.api { render :action => 'show', :status => :created, :location => time_entry_url(@time_entry) }
@@ -156,7 +169,7 @@ class TimelogController < ApplicationController
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default project_time_entries_path(@time_entry.project)
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
}
format.api { render_api_ok }
end
@@ -182,13 +195,12 @@ class TimelogController < ApplicationController
time_entry.safe_attributes = attributes
call_hook(:controller_time_entries_bulk_edit_before_save, { :params => params, :time_entry => time_entry })
unless time_entry.save
logger.info "time entry could not be updated: #{time_entry.errors.full_messages}" if logger && logger.info
# Keep unsaved time_entry ids to display them in flash error
unsaved_time_entry_ids << time_entry.id
end
end
set_flash_from_bulk_time_entry_save(@time_entries, unsaved_time_entry_ids)
redirect_back_or_default project_time_entries_path(@projects.first)
redirect_back_or_default({:controller => 'timelog', :action => 'index', :project_id => @projects.first})
end
def destroy
@@ -207,7 +219,7 @@ class TimelogController < ApplicationController
else
flash[:error] = l(:notice_unable_delete_time_entry)
end
redirect_back_or_default project_time_entries_path(@projects.first)
redirect_back_or_default(:action => 'index', :project_id => @projects.first)
}
format.api {
if destroyed
@@ -279,13 +291,48 @@ private
end
end
# Returns the TimeEntry scope for index and report actions
def time_entry_scope(options={})
scope = @query.results_scope(options)
if @issue
scope = scope.on_issue(@issue)
# Retrieves the date range based on predefined ranges or specific from/to param dates
def retrieve_date_range
@free_period = false
@from, @to = nil, nil
if params[:period_type] == '1' || (params[:period_type].nil? && !params[:period].nil?)
case params[:period].to_s
when 'today'
@from = @to = Date.today
when 'yesterday'
@from = @to = Date.today - 1
when 'current_week'
@from = Date.today - (Date.today.cwday - 1)%7
@to = @from + 6
when 'last_week'
@from = Date.today - 7 - (Date.today.cwday - 1)%7
@to = @from + 6
when '7_days'
@from = Date.today - 7
@to = Date.today
when 'current_month'
@from = Date.civil(Date.today.year, Date.today.month, 1)
@to = (@from >> 1) - 1
when 'last_month'
@from = Date.civil(Date.today.year, Date.today.month, 1) << 1
@to = (@from >> 1) - 1
when '30_days'
@from = Date.today - 30
@to = Date.today
when 'current_year'
@from = Date.civil(Date.today.year, 1, 1)
@to = Date.civil(Date.today.year, 12, 31)
end
elsif params[:period_type] == '2' || (params[:period_type].nil? && (!params[:from].nil? || !params[:to].nil?))
begin; @from = params[:from].to_s.to_date unless params[:from].blank?; rescue; end
begin; @to = params[:to].to_s.to_date unless params[:to].blank?; rescue; end
@free_period = true
else
# default
end
scope
@from, @to = @to, @from if @from && @to && @from > @to
end
def parse_params_for_bulk_time_entry_attributes(params)
+11 -11
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -25,7 +25,7 @@ class TrackersController < ApplicationController
def index
respond_to do |format|
format.html {
@tracker_pages, @trackers = paginate Tracker.sorted, :per_page => 25
@tracker_pages, @trackers = paginate :trackers, :per_page => 10, :order => 'position'
render :action => "index", :layout => false if request.xhr?
}
format.api {
@@ -36,19 +36,19 @@ class TrackersController < ApplicationController
def new
@tracker ||= Tracker.new(params[:tracker])
@trackers = Tracker.sorted.all
@projects = Project.all
@trackers = Tracker.find :all, :order => 'position'
@projects = Project.find(:all)
end
def create
@tracker = Tracker.new(params[:tracker])
if @tracker.save
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)
end
flash[:notice] = l(:notice_successful_create)
redirect_to trackers_path
redirect_to :action => 'index'
return
end
new
@@ -57,14 +57,14 @@ class TrackersController < ApplicationController
def edit
@tracker ||= Tracker.find(params[:id])
@projects = Project.all
@projects = Project.find(:all)
end
def update
@tracker = Tracker.find(params[:id])
if @tracker.update_attributes(params[:tracker])
if request.put? and @tracker.update_attributes(params[:tracker])
flash[:notice] = l(:notice_successful_update)
redirect_to trackers_path
redirect_to :action => 'index'
return
end
edit
@@ -78,7 +78,7 @@ class TrackersController < ApplicationController
else
@tracker.destroy
end
redirect_to trackers_path
redirect_to :action => 'index'
end
def fields
@@ -92,7 +92,7 @@ class TrackersController < ApplicationController
end
end
flash[:notice] = l(:notice_successful_update)
redirect_to fields_trackers_path
redirect_to :action => 'fields'
return
end
@trackers = Tracker.sorted.all
+26 -22
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -45,9 +45,12 @@ class UsersController < ApplicationController
scope = scope.in_group(params[:group_id]) if params[:group_id].present?
@user_count = scope.count
@user_pages = Paginator.new @user_count, @limit, params['page']
@offset ||= @user_pages.offset
@users = scope.order(sort_clause).limit(@limit).offset(@offset).all
@user_pages = Paginator.new self, @user_count, @limit, params['page']
@offset ||= @user_pages.current.offset
@users = scope.find :all,
:order => sort_clause,
:limit => @limit,
:offset => @offset
respond_to do |format|
format.html {
@@ -60,7 +63,7 @@ class UsersController < ApplicationController
def show
# show projects based on current user visibility
@memberships = @user.memberships.where(Project.visible_condition(User.current)).all
@memberships = @user.memberships.all(:conditions => Project.visible_condition(User.current))
events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
@events_by_day = events.group_by(&:event_date)
@@ -80,8 +83,7 @@ class UsersController < ApplicationController
def new
@user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
@user.safe_attributes = params[:user]
@auth_sources = AuthSource.all
@auth_sources = AuthSource.find(:all)
end
def create
@@ -93,24 +95,24 @@ class UsersController < ApplicationController
if @user.save
@user.pref.attributes = params[:pref]
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
@user.pref.save
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
Mailer.account_information(@user, @user.password).deliver if params[:send_information]
Mailer.account_information(@user, params[:user][:password]).deliver if params[:send_information]
respond_to do |format|
format.html {
flash[:notice] = l(:notice_user_successful_create, :id => view_context.link_to(@user.login, user_path(@user)))
if params[:continue]
attrs = params[:user].slice(:generate_password)
redirect_to new_user_path(:user => attrs)
else
redirect_to edit_user_path(@user)
end
redirect_to(params[:continue] ?
{:controller => 'users', :action => 'new'} :
{:controller => 'users', :action => 'edit', :id => @user}
)
}
format.api { render :action => 'show', :status => :created, :location => user_url(@user) }
end
else
@auth_sources = AuthSource.all
@auth_sources = AuthSource.find(:all)
# Clear password input
@user.password = @user.password_confirmation = nil
@@ -122,7 +124,7 @@ class UsersController < ApplicationController
end
def edit
@auth_sources = AuthSource.all
@auth_sources = AuthSource.find(:all)
@membership ||= Member.new
end
@@ -137,14 +139,16 @@ class UsersController < ApplicationController
was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
# TODO: Similar to My#account
@user.pref.attributes = params[:pref]
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
if @user.save
@user.pref.save
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
if was_activated
Mailer.account_activated(@user).deliver
elsif @user.active? && params[:send_information] && @user.password.present? && @user.auth_source_id.nil?
Mailer.account_information(@user, @user.password).deliver
elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.auth_source_id.nil?
Mailer.account_information(@user, params[:user][:password]).deliver
end
respond_to do |format|
@@ -155,7 +159,7 @@ class UsersController < ApplicationController
format.api { render_api_ok }
end
else
@auth_sources = AuthSource.all
@auth_sources = AuthSource.find(:all)
@membership ||= Member.new
# Clear password input
@user.password = @user.password_confirmation = nil
@@ -170,7 +174,7 @@ class UsersController < ApplicationController
def destroy
@user.destroy
respond_to do |format|
format.html { redirect_back_or_default(users_path) }
format.html { redirect_back_or_default(users_url) }
format.api { render_api_ok }
end
end
@@ -179,7 +183,7 @@ class UsersController < ApplicationController
@membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
@membership.save
respond_to do |format|
format.html { redirect_to edit_user_path(@user, :tab => 'memberships') }
format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
format.js
end
end
@@ -190,7 +194,7 @@ class UsersController < ApplicationController
@membership.destroy
end
respond_to do |format|
format.html { redirect_to edit_user_path(@user, :tab => 'memberships') }
format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
format.js
end
end
+23 -18
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -20,7 +20,7 @@ class VersionsController < ApplicationController
model_object Version
before_filter :find_model_object, :except => [:index, :new, :create, :close_completed]
before_filter :find_project_from_association, :except => [:index, :new, :create, :close_completed]
before_filter :find_project_by_project_id, :only => [:index, :new, :create, :close_completed]
before_filter :find_project, :only => [:index, :new, :create, :close_completed]
before_filter :authorize
accept_api_auth :index, :show, :create, :update, :destroy
@@ -31,7 +31,7 @@ class VersionsController < ApplicationController
def index
respond_to do |format|
format.html {
@trackers = @project.trackers.sorted.all
@trackers = @project.trackers.find(:all, :order => 'position')
retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?})
@with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
@@ -46,11 +46,11 @@ class VersionsController < ApplicationController
@issues_by_version = {}
if @selected_tracker_ids.any? && @versions.any?
issues = Issue.visible.
includes(:project, :tracker).
preload(:status, :priority, :fixed_version).
where(:tracker_id => @selected_tracker_ids, :project_id => project_ids, :fixed_version_id => @versions.map(&:id)).
order("#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id")
issues = Issue.visible.all(
:include => [:project, :status, :tracker, :priority, :fixed_version],
:conditions => {:tracker_id => @selected_tracker_ids, :project_id => project_ids, :fixed_version_id => @versions.map(&:id)},
:order => "#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id"
)
@issues_by_version = issues.group_by(&:fixed_version)
end
@versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?}
@@ -64,10 +64,9 @@ class VersionsController < ApplicationController
def show
respond_to do |format|
format.html {
@issues = @version.fixed_issues.visible.
includes(:status, :tracker, :priority).
reorder("#{Tracker.table_name}.position, #{Issue.table_name}.id").
all
@issues = @version.fixed_issues.visible.find(:all,
:include => [:status, :tracker, :priority],
:order => "#{Tracker.table_name}.position, #{Issue.table_name}.id")
}
format.api
end
@@ -96,7 +95,7 @@ class VersionsController < ApplicationController
respond_to do |format|
format.html do
flash[:notice] = l(:notice_successful_create)
redirect_back_or_default settings_project_path(@project, :tab => 'versions')
redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
end
format.js
format.api do
@@ -125,7 +124,7 @@ class VersionsController < ApplicationController
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default settings_project_path(@project, :tab => 'versions')
redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
}
format.api { render_api_ok }
end
@@ -142,21 +141,21 @@ class VersionsController < ApplicationController
if request.put?
@project.close_completed_versions
end
redirect_to settings_project_path(@project, :tab => 'versions')
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
end
def destroy
if @version.fixed_issues.empty?
@version.destroy
respond_to do |format|
format.html { redirect_back_or_default settings_project_path(@project, :tab => 'versions') }
format.html { redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project }
format.api { render_api_ok }
end
else
respond_to do |format|
format.html {
flash[:error] = l(:notice_unable_delete_version)
redirect_to settings_project_path(@project, :tab => 'versions')
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
}
format.api { head :unprocessable_entity }
end
@@ -170,7 +169,12 @@ class VersionsController < ApplicationController
end
end
private
private
def find_project
@project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound
render_404
end
def retrieve_selected_tracker_ids(selectable_trackers, default_trackers=nil)
if ids = params[:tracker_ids]
@@ -179,4 +183,5 @@ class VersionsController < ApplicationController
@selected_tracker_ids = (default_trackers || selectable_trackers).collect {|t| t.id.to_s }
end
end
end
+21 -35
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -16,36 +16,35 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class WatchersController < ApplicationController
before_filter :require_login, :find_watchables, :only => [:watch, :unwatch]
before_filter :find_project
before_filter :require_login, :check_project_privacy, :only => [:watch, :unwatch]
before_filter :authorize, :only => [:new, :destroy]
def watch
set_watcher(@watchables, User.current, true)
if @watched.respond_to?(:visible?) && !@watched.visible?(User.current)
render_403
else
set_watcher(User.current, true)
end
end
def unwatch
set_watcher(@watchables, User.current, false)
set_watcher(User.current, false)
end
before_filter :find_project, :authorize, :only => [:new, :create, :append, :destroy, :autocomplete_for_user]
accept_api_auth :create, :destroy
def new
end
def create
user_ids = []
if params[:watcher].is_a?(Hash)
user_ids << (params[:watcher][:user_ids] || params[:watcher][:user_id])
else
user_ids << params[:user_id]
end
user_ids.flatten.compact.uniq.each do |user_id|
Watcher.create(:watchable => @watched, :user_id => user_id)
if params[:watcher].is_a?(Hash) && request.post?
user_ids = params[:watcher][:user_ids] || [params[:watcher][:user_id]]
user_ids.each do |user_id|
Watcher.create(:watchable => @watched, :user_id => user_id)
end
end
respond_to do |format|
format.html { redirect_to_referer_or {render :text => 'Watcher added.', :layout => true}}
format.js
format.api { render_api_ok }
end
end
@@ -57,24 +56,22 @@ class WatchersController < ApplicationController
end
def destroy
@watched.set_watcher(User.find(params[:user_id]), false)
@watched.set_watcher(User.find(params[:user_id]), false) if request.post?
respond_to do |format|
format.html { redirect_to :back }
format.js
format.api { render_api_ok }
end
end
def autocomplete_for_user
@users = User.active.sorted.like(params[:q]).limit(100).all
@users = User.active.like(params[:q]).find(:all, :limit => 100)
if @watched
@users -= @watched.watcher_users
end
render :layout => false
end
private
private
def find_project
if params[:object_type] && params[:object_id]
klass = Object.const_get(params[:object_type].camelcase)
@@ -88,22 +85,11 @@ class WatchersController < ApplicationController
render_404
end
def find_watchables
klass = Object.const_get(params[:object_type].camelcase) rescue nil
if klass && klass.respond_to?('watched_by')
@watchables = klass.find_all_by_id(Array.wrap(params[:object_id]))
raise Unauthorized if @watchables.any? {|w| w.respond_to?(:visible?) && !w.visible?}
end
render_404 unless @watchables.present?
end
def set_watcher(watchables, user, watching)
watchables.each do |watchable|
watchable.set_watcher(user, watching)
end
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 => watchables} }
format.js { render :partial => 'set_watcher', :locals => {:user => user, :watched => @watched} }
end
end
end
+1 -1
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+54 -92
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -15,6 +15,8 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'diff'
# The WikiController follows the Rails REST controller pattern but with
# a few differences
#
@@ -33,9 +35,7 @@ class WikiController < ApplicationController
default_search_scope :wiki_pages
before_filter :find_wiki, :authorize
before_filter :find_existing_or_new_page, :only => [:show, :edit, :update]
before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy, :destroy_version]
accept_api_auth :index, :show, :update, :destroy
before_filter :find_attachments, :only => [:preview]
before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy]
helper :attachments
include AttachmentsHelper
@@ -45,13 +45,7 @@ class WikiController < ApplicationController
# List of pages, sorted alphabetically and by parent (hierarchy)
def index
load_pages_for_index
respond_to do |format|
format.html {
@pages_by_parent_id = @pages.group_by(&:parent_id)
}
format.api
end
@pages_by_parent_id = @pages.group_by(&:parent_id)
end
# List of page, by last update
@@ -62,13 +56,8 @@ class WikiController < ApplicationController
# display a page (in editing mode if it doesn't exist)
def show
if params[:version] && !User.current.allowed_to?(:view_wiki_edits, @project)
deny_access
return
end
@content = @page.content_for_version(params[:version])
if @content.nil?
if User.current.allowed_to?(:edit_wiki_pages, @project) && editable? && !api_request?
if @page.new_record?
if User.current.allowed_to?(:edit_wiki_pages, @project) && editable?
edit
render :action => 'edit'
else
@@ -76,6 +65,12 @@ class WikiController < ApplicationController
end
return
end
if params[:version] && !User.current.allowed_to?(:view_wiki_edits, @project)
# Redirects user to the current version if he's not allowed to view previous versions
redirect_to :version => nil
return
end
@content = @page.content_for_version(params[:version])
if User.current.allowed_to?(:export_wiki_pages, @project)
if params[:format] == 'pdf'
send_data(wiki_page_to_pdf(@page, @project), :type => 'application/pdf', :filename => "#{@page.title}.pdf")
@@ -94,29 +89,26 @@ class WikiController < ApplicationController
@content.current_version? &&
Redmine::WikiFormatting.supports_section_edit?
respond_to do |format|
format.html
format.api
end
render :action => 'show'
end
# edit an existing page or a new one
def edit
return render_403 unless editable?
if @page.new_record?
@page.content = WikiContent.new(:page => @page)
if params[:parent].present?
@page.parent = @page.wiki.find_page(params[:parent].to_s)
end
end
@content = @page.content_for_version(params[:version])
@content ||= WikiContent.new(:page => @page)
@content.text = initial_page_content(@page) if @content.text.blank?
# don't keep previous comment
@content.comments = nil
# To prevent StaleObjectError exception when reverting to a previous version
@content.version = @page.content.version if @page.content
@content.version = @page.content.version
@text = @content.text
if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
@@ -129,67 +121,50 @@ class WikiController < ApplicationController
# Creates a new page or updates an existing one
def update
return render_403 unless editable?
was_new_page = @page.new_record?
@page.content = WikiContent.new(:page => @page) if @page.new_record?
@page.safe_attributes = params[:wiki_page]
@content = @page.content || WikiContent.new(:page => @page)
content_params = params[:content]
if content_params.nil? && params[:wiki_page].is_a?(Hash)
content_params = params[:wiki_page].slice(:text, :comments, :version)
end
content_params ||= {}
@content = @page.content_for_version(params[:version])
@content.text = initial_page_content(@page) if @content.text.blank?
# don't keep previous comment
@content.comments = nil
@content.comments = content_params[:comments]
@text = content_params[:text]
if !@page.new_record? && params[:content].present? && @content.text == params[:content][:text]
attachments = Attachment.attach_files(@page, params[:attachments])
render_attachment_warning_if_needed(@page)
# don't save content if text wasn't changed
@page.save
redirect_to :action => 'show', :project_id => @project, :id => @page.title
return
end
@content.comments = params[:content][:comments]
@text = params[:content][:text]
if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
@section = params[:section].to_i
@section_hash = params[:section_hash]
@content.text = Redmine::WikiFormatting.formatter.new(@content.text).update_section(@section, @text, @section_hash)
@content.text = Redmine::WikiFormatting.formatter.new(@content.text).update_section(params[:section].to_i, @text, @section_hash)
else
@content.version = content_params[:version] if content_params[:version]
@content.version = params[:content][:version]
@content.text = @text
end
@content.author = User.current
if @page.save_with_content(@content)
@page.content = @content
if @page.save
attachments = Attachment.attach_files(@page, params[:attachments])
render_attachment_warning_if_needed(@page)
call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
respond_to do |format|
format.html {
anchor = @section ? "section-#{@section}" : nil
redirect_to project_wiki_page_path(@project, @page.title, :anchor => anchor)
}
format.api {
if was_new_page
render :action => 'show', :status => :created, :location => project_wiki_page_path(@project, @page.title)
else
render_api_ok
end
}
end
redirect_to :action => 'show', :project_id => @project, :id => @page.title
else
respond_to do |format|
format.html { render :action => 'edit' }
format.api { render_validation_errors(@content) }
end
render :action => 'edit'
end
rescue ActiveRecord::StaleObjectError, Redmine::WikiFormatting::StaleSectionError
# Optimistic locking exception
respond_to do |format|
format.html {
flash.now[:error] = l(:notice_locking_conflict)
render :action => 'edit'
}
format.api { render_api_head :conflict }
end
flash.now[:error] = l(:notice_locking_conflict)
render :action => 'edit'
rescue ActiveRecord::RecordNotSaved
respond_to do |format|
format.html { render :action => 'edit' }
format.api { render_validation_errors(@content) }
end
render :action => 'edit'
end
# rename a page
@@ -200,26 +175,25 @@ class WikiController < ApplicationController
@original_title = @page.pretty_title
if request.post? && @page.update_attributes(params[:wiki_page])
flash[:notice] = l(:notice_successful_update)
redirect_to project_wiki_page_path(@project, @page.title)
redirect_to :action => 'show', :project_id => @project, :id => @page.title
end
end
def protect
@page.update_attribute :protected, params[:protected]
redirect_to project_wiki_page_path(@project, @page.title)
redirect_to :action => 'show', :project_id => @project, :id => @page.title
end
# show page history
def history
@version_count = @page.content.versions.count
@version_pages = Paginator.new @version_count, per_page_option, params['page']
@version_pages = Paginator.new self, @version_count, per_page_option, params['p']
# don't load text
@versions = @page.content.versions.
select("id, author_id, comments, updated_on, version").
reorder('version DESC').
limit(@version_pages.per_page + 1).
offset(@version_pages.offset).
all
@versions = @page.content.versions.find :all,
:select => "id, author_id, comments, updated_on, version",
:order => 'version DESC',
:limit => @version_pages.items_per_page + 1,
:offset => @version_pages.current.offset
render :layout => false if request.xhr?
end
@@ -256,28 +230,16 @@ class WikiController < ApplicationController
end
else
@reassignable_to = @wiki.pages - @page.self_and_descendants
# display the destroy form if it's a user request
return unless api_request?
return
end
end
@page.destroy
respond_to do |format|
format.html { redirect_to project_wiki_index_path(@project) }
format.api { render_api_ok }
end
end
def destroy_version
return render_403 unless editable?
@content = @page.content_for_version(params[:version])
@content.destroy
redirect_to_referer_or history_project_wiki_page_path(@project, @page.title)
redirect_to :action => 'index', :project_id => @project
end
# Export wiki to a single pdf or html file
def export
@pages = @wiki.pages.all(:order => 'title', :include => [:content, {:attachments => :author}])
@pages = @wiki.pages.all(:order => 'title', :include => [:content, :attachments], :limit => 75)
respond_to do |format|
format.html {
export = render_to_string :action => 'export_multiple', :layout => false
@@ -294,7 +256,7 @@ class WikiController < ApplicationController
# page is nil when previewing a new page
return render_403 unless page.nil? || editable?(page)
if page
@attachments += page.attachments
@attachements = page.attachments
@previewed = page.content
end
@text = params[:content][:text]
@@ -351,6 +313,6 @@ private
end
def load_pages_for_index
@pages = @wiki.pages.with_updated_on.reorder("#{WikiPage.table_name}.title").includes(:wiki => :project).includes(:parent).all
@pages = @wiki.pages.with_updated_on.all(:order => 'title', :include => {:wiki => :project})
end
end
+2 -2
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -30,7 +30,7 @@ class WikisController < ApplicationController
def destroy
if request.post? && params[:confirm] && @project.wiki
@project.wiki.destroy
redirect_to settings_project_path(@project, :tab => 'wiki')
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'wiki'
end
end
end
+5 -5
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -38,7 +38,7 @@ class WorkflowsController < ApplicationController
}
}
if @role.save
redirect_to workflows_edit_path(:role_id => @role, :tracker_id => @tracker, :used_statuses_only => params[:used_statuses_only])
redirect_to :action => 'edit', :role_id => @role, :tracker_id => @tracker, :used_statuses_only => params[:used_statuses_only]
return
end
end
@@ -64,7 +64,7 @@ class WorkflowsController < ApplicationController
if request.post? && @role && @tracker
WorkflowPermission.replace_permissions(@tracker, @role, params[:permissions] || {})
redirect_to workflows_permissions_path(:role_id => @role, :tracker_id => @tracker, :used_statuses_only => params[:used_statuses_only])
redirect_to :action => 'permissions', :role_id => @role, :tracker_id => @tracker, :used_statuses_only => params[:used_statuses_only]
return
end
@@ -106,12 +106,12 @@ class WorkflowsController < ApplicationController
if request.post?
if params[:source_tracker_id].blank? || params[:source_role_id].blank? || (@source_tracker.nil? && @source_role.nil?)
flash.now[:error] = l(:error_workflow_copy_source)
elsif @target_trackers.blank? || @target_roles.blank?
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)
flash[:notice] = l(:notice_successful_update)
redirect_to workflows_copy_path(:source_tracker_id => @source_tracker, :source_role_id => @source_role)
redirect_to :action => 'copy', :source_tracker_id => @source_tracker, :source_role_id => @source_role
end
end
end
+1 -1
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+1 -1
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+115 -116
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -24,7 +24,6 @@ module ApplicationHelper
include Redmine::WikiFormatting::Macros::Definitions
include Redmine::I18n
include GravatarHelper::PublicMethods
include Redmine::Pagination::Helper
extend Forwardable
def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
@@ -48,8 +47,8 @@ module ApplicationHelper
def link_to_user(user, options={})
if user.is_a?(User)
name = h(user.name(options[:format]))
if user.active? || (User.current.admin? && user.logged?)
link_to name, user_path(user), :class => user.css_classes
if user.active?
link_to name, :controller => 'users', :action => 'show', :id => user
else
name
end
@@ -65,12 +64,10 @@ module ApplicationHelper
# link_to_issue(issue, :truncate => 6) # => Defect #6: This i...
# link_to_issue(issue, :subject => false) # => Defect #6
# link_to_issue(issue, :project => true) # => Foo - Defect #6
# link_to_issue(issue, :subject => false, :tracker => false) # => #6
#
def link_to_issue(issue, options={})
title = nil
subject = nil
text = options[:tracker] == false ? "##{issue.id}" : "#{issue.tracker} ##{issue.id}"
if options[:subject] == false
title = truncate(issue.subject, :length => 60)
else
@@ -79,7 +76,9 @@ module ApplicationHelper
subject = truncate(subject, :length => options[:truncate])
end
end
s = link_to text, issue_path(issue), :class => issue.css_classes, :title => title
s = link_to "#{h(issue.tracker)} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue},
:class => issue.css_classes,
:title => title
s << h(": #{subject}") if subject
s = h("#{issue.project} - ") + s if options[:project]
s
@@ -91,10 +90,14 @@ module ApplicationHelper
# * :download - Force download (default: false)
def link_to_attachment(attachment, options={})
text = options.delete(:text) || attachment.filename
route_method = options.delete(:download) ? :download_named_attachment_path : :named_attachment_path
html_options = options.slice!(:only_path)
url = send(route_method, attachment, attachment.filename, options)
link_to text, url, html_options
action = options.delete(:download) ? 'download' : 'show'
opt_only_path = {}
opt_only_path[:only_path] = (options[:only_path] == false ? false : true)
options.delete(:only_path)
link_to(h(text),
{:controller => 'attachments', :action => action,
:id => attachment, :filename => attachment.filename}.merge(opt_only_path),
options)
end
# Generates a link to a SCM revision
@@ -116,11 +119,13 @@ module ApplicationHelper
# Generates a link to a message
def link_to_message(message, options={}, html_options = nil)
link_to(
truncate(message.subject, :length => 60),
board_message_path(message.board_id, message.parent_id || message.id, {
h(truncate(message.subject, :length => 60)),
{ :controller => 'messages', :action => 'show',
:board_id => message.board_id,
:id => (message.parent_id || message.id),
:r => (message.parent_id && message.id),
:anchor => (message.parent_id ? "message-#{message.id}" : nil)
}.merge(options)),
}.merge(options),
html_options
)
end
@@ -129,39 +134,22 @@ module ApplicationHelper
# Examples:
#
# link_to_project(project) # => link to the specified project overview
# link_to_project(project, :action=>'settings') # => link to project settings
# link_to_project(project, {:only_path => false}, :class => "project") # => 3rd arg adds html options
# 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.name)
elsif options.key?(:action)
ActiveSupport::Deprecation.warn "#link_to_project with :action option is deprecated and will be removed in Redmine 3.0."
h(project)
else
url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
link_to project.name, url, html_options
else
link_to project.name, project_path(project, options), html_options
link_to(h(project), url, html_options)
end
end
# Generates a link to a project settings if active
def link_to_project_settings(project, options={}, html_options=nil)
if project.active?
link_to project.name, settings_project_path(project, options), html_options
elsif project.archived?
h(project.name)
else
link_to project.name, project_path(project, options), html_options
end
end
def wiki_page_path(page, options={})
url_for({:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title}.merge(options))
end
def thumbnail_tag(attachment)
link_to image_tag(thumbnail_path(attachment)),
named_attachment_path(attachment, attachment.filename),
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
@@ -195,7 +183,7 @@ module ApplicationHelper
def format_version_name(version)
if version.project == @project
h(version)
h(version)
else
h("#{version.project} - #{version}")
end
@@ -246,7 +234,7 @@ module ApplicationHelper
content << "<ul class=\"pages-hierarchy\">\n"
pages[node].each do |page|
content << "<li>"
content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title, :version => nil},
content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title},
:title => (options[:timestamp] && page.updated_on ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
content << "\n" + render_page_hierarchy(pages, page.id, options) if pages[page.id]
content << "</li>\n"
@@ -316,8 +304,8 @@ module ApplicationHelper
def principals_check_box_tags(name, principals)
s = ''
principals.each do |principal|
s << "<label>#{ check_box_tag name, principal.id, false, :id => nil } #{h principal}</label>\n"
principals.sort.each do |principal|
s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
end
s.html_safe
end
@@ -330,7 +318,7 @@ module ApplicationHelper
end
groups = ''
collection.sort.each do |element|
selected_attribute = ' selected="selected"' if option_value_selected?(element, selected) || element.id.to_s == selected
selected_attribute = ' selected="selected"' if option_value_selected?(element, selected)
(element.is_a?(Group) ? groups : s) << %(<option value="#{element.id}"#{selected_attribute}>#{h element.name}</option>)
end
unless groups.empty?
@@ -339,19 +327,6 @@ module ApplicationHelper
s.html_safe
end
# Options for the new membership projects combo-box
def options_for_membership_project_select(principal, projects)
options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---")
options << project_tree_options_for_select(projects) do |p|
{:disabled => principal.projects.to_a.include?(p)}
end
options
end
def option_tag(name, text, value, selected=nil, options={})
content_tag 'option', value, options.merge(:value => value, :selected => (value == selected))
end
# Truncates and returns the string as a single line
def truncate_single_line(string, *args)
truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
@@ -384,7 +359,7 @@ module ApplicationHelper
if @project
link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => User.current.time_to_date(time)}, :title => format_time(time))
else
content_tag('abbr', text, :title => format_time(time))
content_tag('acronym', text, :title => format_time(time))
end
end
@@ -403,6 +378,59 @@ module ApplicationHelper
str.blank? ? nil : str
end
def pagination_links_full(paginator, count=nil, options={})
page_param = options.delete(:page_param) || :page
per_page_links = options.delete(:per_page_links)
url_param = params.dup
html = ''
if paginator.current.previous
# \xc2\xab(utf-8) = &#171;
html << link_to_content_update(
"\xc2\xab " + l(:label_previous),
url_param.merge(page_param => paginator.current.previous)) + ' '
end
html << (pagination_links_each(paginator, options) do |n|
link_to_content_update(n.to_s, url_param.merge(page_param => n))
end || '')
if paginator.current.next
# \xc2\xbb(utf-8) = &#187;
html << ' ' + link_to_content_update(
(l(:label_next) + " \xc2\xbb"),
url_param.merge(page_param => paginator.current.next))
end
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)
html << " | #{links}"
end
end
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|
n == selected ? n : link_to_content_update(n, params.merge(:per_page => n))
end
l(:label_display_per_page, links.join(', '))
end
def reorder_links(name, url, method = :post)
link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)),
url.merge({"#{name}[move_to]" => 'highest'}),
@@ -449,31 +477,12 @@ module ApplicationHelper
end
end
# Returns a h2 tag and sets the html title with the given arguments
def title(*args)
strings = args.map do |arg|
if arg.is_a?(Array) && arg.size >= 2
link_to(*arg)
else
h(arg.to_s)
end
end
html_title args.reverse.map {|s| (s.is_a?(Array) ? s.first : s).to_s}
content_tag('h2', strings.join(' &#187; ').html_safe)
end
# Sets the html title
# Returns the html title when called without arguments
# Current project name and app_title and automatically appended
# Exemples:
# html_title 'Foo', 'Bar'
# html_title # => 'Foo - Bar - My Project - Redmine'
def html_title(*args)
if args.empty?
title = @html_title || []
title << @project.name if @project
title << Setting.app_title unless Setting.app_title == title.last
title.reject(&:blank?).join(' - ')
title.select {|t| !t.blank? }.join(' - ')
else
@html_title ||= []
@html_title += args
@@ -488,18 +497,13 @@ module ApplicationHelper
css << 'theme-' + theme.name
end
css << 'project-' + @project.identifier if @project && @project.identifier.present?
css << 'controller-' + controller_name
css << 'action-' + action_name
css.join(' ')
end
def accesskey(s)
@used_accesskeys ||= []
key = Redmine::AccessKeys.key_for(s)
return nil if @used_accesskeys.include?(key)
@used_accesskeys << key
key
Redmine::AccessKeys.key_for s
end
# Formats text according to system settings.
@@ -580,14 +584,14 @@ module ApplicationHelper
def parse_inline_attachments(text, project, obj, attr, only_path, options)
# when using an image link, try to use an attachment, if possible
attachments = options[:attachments] || []
attachments += obj.attachments if obj.respond_to?(:attachments)
if attachments.present?
if options[:attachments] || (obj && obj.respond_to?(:attachments))
attachments = options[:attachments] || obj.attachments
text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpe|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
filename, ext, alt, alttext = $1.downcase, $2, $3, $4
# search for the picture in attachments
if found = Attachment.latest_attach(attachments, filename)
image_url = download_named_attachment_path(found, found.filename, :only_path => only_path)
image_url = url_for :only_path => only_path, :controller => 'attachments',
:action => 'download', :id => found
desc = found.description.to_s.gsub('"', '')
if !desc.blank? && alttext.blank?
alt = " title=\"#{desc}\" alt=\"#{desc}\""
@@ -616,9 +620,9 @@ module ApplicationHelper
esc, all, page, title = $1, $2, $3, $5
if esc.nil?
if page =~ /^([^\:]+)\:(.*)$/
identifier, page = $1, $2
link_project = Project.find_by_identifier(identifier) || Project.find_by_name(identifier)
title ||= identifier if page.blank?
link_project = Project.find_by_identifier($1) || Project.find_by_name($1)
page = $2
title ||= $1 if page.blank?
end
if link_project && link_project.wiki
@@ -640,7 +644,7 @@ module ApplicationHelper
wiki_page_id = page.present? ? Wiki.titleize(page) : nil
parent = wiki_page.nil? && obj.is_a?(WikiContent) && obj.page && project == link_project ? obj.page.title : nil
url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project,
:id => wiki_page_id, :version => nil, :anchor => anchor, :parent => parent)
:id => wiki_page_id, :anchor => anchor, :parent => parent)
end
end
link_to(title.present? ? title.html_safe : h(page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
@@ -680,20 +684,16 @@ module ApplicationHelper
# export:some/file -> Force the download of the file
# Forum messages:
# message#1218 -> Link to message with id 1218
# Projects:
# project:someproject -> Link to project named "someproject"
# project#3 -> Link to project with id 3
#
# Links can refer other objects from other projects, using project identifier:
# identifier:r52
# identifier:document:"Some document"
# identifier:version:1.0.0
# identifier:source:some/file
def parse_redmine_links(text, default_project, obj, attr, only_path, options)
text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-_]+):)?(attachment|document|version|forum|news|message|project|commit|source|export)?(((#)|((([a-z0-9\-_]+)\|)?(r)))((\d+)((#note)?-(\d+))?)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]][^A-Za-z0-9_/])|,|\s|\]|<|$)}) do |m|
def parse_redmine_links(text, project, obj, attr, only_path, options)
text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-_]+):)?(attachment|document|version|forum|news|message|project|commit|source|export)?(((#)|((([a-z0-9\-]+)\|)?(r)))((\d+)((#note)?-(\d+))?)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]][^A-Za-z0-9_/])|,|\s|\]|<|$)}) do |m|
leading, esc, project_prefix, project_identifier, prefix, repo_prefix, repo_identifier, sep, identifier, comment_suffix, comment_id = $1, $2, $3, $4, $5, $10, $11, $8 || $12 || $18, $14 || $19, $15, $17
link = nil
project = default_project
if project_identifier
project = Project.visible.find_by_identifier(project_identifier)
end
@@ -719,7 +719,7 @@ module ApplicationHelper
when nil
if oid.to_s == identifier && issue = Issue.visible.find_by_id(oid, :include => :status)
anchor = comment_id ? "note-#{comment_id}" : nil
link = link_to(h("##{oid}#{comment_suffix}"), {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid, :anchor => anchor},
link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid, :anchor => anchor},
:class => issue.css_classes,
:title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
end
@@ -779,21 +779,21 @@ module ApplicationHelper
when 'commit', 'source', 'export'
if project
repository = nil
if name =~ %r{^(([a-z0-9\-_]+)\|)(.+)$}
if name =~ %r{^(([a-z0-9\-]+)\|)(.+)$}
repo_prefix, repo_identifier, name = $1, $2, $3
repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
else
repository = project.repository
end
if prefix == 'commit'
if repository && (changeset = Changeset.visible.where("repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%").first)
if repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%"]))
link = link_to h("#{project_prefix}#{repo_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :repository_id => repository.identifier_param, :rev => changeset.identifier},
:class => 'changeset',
:title => truncate_single_line(changeset.comments, :length => 100)
:title => truncate_single_line(h(changeset.comments), :length => 100)
end
else
if repository && User.current.allowed_to?(:browse_repository, project)
name =~ %r{^[/\\]*(.*?)(@([^/\\@]+?))?(#(L\d+))?$}
name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
path, rev, anchor = $1, $3, $5
link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:controller => 'repositories', :action => (prefix == 'export' ? 'raw' : 'entry'), :id => project, :repository_id => repository.identifier_param,
:path => to_path_param(path),
@@ -806,11 +806,12 @@ module ApplicationHelper
end
when 'attachment'
attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
if attachments && attachment = Attachment.latest_attach(attachments, name)
link = link_to_attachment(attachment, :only_path => only_path, :download => true, :class => 'attachment')
if attachments && attachment = attachments.detect {|a| a.filename == name }
link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
:class => 'attachment'
end
when 'project'
if p = Project.visible.where("identifier = :s OR LOWER(name) = :s", :s => name.downcase).first
if p = Project.visible.find(:first, :conditions => ["identifier = :s OR LOWER(name) = :s", {:s => name.downcase}])
link = link_to_project(p, {:only_path => only_path}, :class => 'project')
end
end
@@ -831,8 +832,7 @@ module ApplicationHelper
content_tag('div',
link_to(image_tag('edit.png'), options[:edit_section_links].merge(:section => @current_section)),
:class => 'contextual',
:title => l(:button_edit_section),
:id => "section-#{@current_section}") + heading.html_safe
:title => l(:button_edit_section)) + heading.html_safe
else
heading
end
@@ -958,7 +958,8 @@ module ApplicationHelper
end
def lang_options_for_select(blank=true)
(blank ? [["(auto)", ""]] : []) + languages_options
(blank ? [["(auto)", ""]] : []) +
valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
end
def label_tag_for(name, option_tags = nil, options = {})
@@ -1063,8 +1064,8 @@ module ApplicationHelper
(pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : ''.html_safe) +
(pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : ''.html_safe) +
(pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : ''.html_safe)
), :class => 'progress progress-#{pcts[0]}', :style => "width: #{width};").html_safe +
content_tag('p', legend, :class => 'percent').html_safe
), :class => 'progress', :style => "width: #{width};").html_safe +
content_tag('p', legend, :class => 'pourcent').html_safe
end
def checked_image(checked=true)
@@ -1096,7 +1097,6 @@ module ApplicationHelper
def include_calendar_headers_tags
unless @calendar_headers_tags_included
tags = javascript_include_tag("datepicker")
@calendar_headers_tags_included = true
content_for :header_tags do
start_of_week = Setting.start_of_week
@@ -1104,13 +1104,12 @@ module ApplicationHelper
# Redmine uses 1..7 (monday..sunday) in settings and locales
# JQuery uses 0..6 (sunday..saturday), 7 needs to be changed to 0
start_of_week = start_of_week.to_i % 7
tags << javascript_tag(
tags = javascript_tag(
"var datepickerOptions={dateFormat: 'yy-mm-dd', firstDay: #{start_of_week}, " +
"showOn: 'button', buttonImageOnly: true, buttonImage: '" +
path_to_image('/images/calendar.png') +
"', showButtonPanel: true, showWeek: true, showOtherMonths: true, " +
"selectOtherMonths: true, changeMonth: true, changeYear: true, " +
"beforeShow: beforeShowDatePicker};")
"', 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")
@@ -1214,7 +1213,7 @@ module ApplicationHelper
def sanitize_anchor_name(anchor)
if ''.respond_to?(:encoding) || RUBY_PLATFORM == 'java'
anchor.gsub(%r{[^\s\-\p{Word}]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
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*)?}, '-')
@@ -1223,7 +1222,7 @@ module ApplicationHelper
# Returns the javascript tags that are included in the html layout head
def javascript_heads
tags = javascript_include_tag('jquery-1.8.3-ui-1.9.2-ujs-2.0.3', 'application')
tags = javascript_include_tag('jquery-1.7.2-ui-1.8.21-ujs-2.0.3', 'application')
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)}'); });")
end
+1 -1
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+1 -1
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+1 -1
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+1 -1
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+2 -2
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -36,7 +36,7 @@ module ContextMenusHelper
def bulk_update_custom_field_context_menu_link(field, text, value)
context_menu_link h(text),
bulk_update_issues_path(:ids => @issue_ids, :issue => {'custom_field_values' => {field.id => value}}, :back_url => @back),
{:controller => 'issues', :action => 'bulk_update', :ids => @issue_ids, :issue => {'custom_field_values' => {field.id => value}}, :back_url => @back},
:method => :post,
:selected => (@issue && @issue.custom_field_value(field) == value)
end
+18 -42
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -19,29 +19,17 @@
module CustomFieldsHelper
CUSTOM_FIELDS_TABS = [
{:name => 'IssueCustomField', :partial => 'custom_fields/index',
:label => :label_issue_plural},
{:name => 'TimeEntryCustomField', :partial => 'custom_fields/index',
:label => :label_spent_time},
{:name => 'ProjectCustomField', :partial => 'custom_fields/index',
:label => :label_project_plural},
{:name => 'VersionCustomField', :partial => 'custom_fields/index',
:label => :label_version_plural},
{:name => 'UserCustomField', :partial => 'custom_fields/index',
:label => :label_user_plural},
{:name => 'GroupCustomField', :partial => 'custom_fields/index',
:label => :label_group_plural},
{:name => 'TimeEntryActivityCustomField', :partial => 'custom_fields/index',
:label => TimeEntryActivity::OptionName},
{:name => 'IssuePriorityCustomField', :partial => 'custom_fields/index',
:label => IssuePriority::OptionName},
{:name => 'DocumentCategoryCustomField', :partial => 'custom_fields/index',
:label => DocumentCategory::OptionName}
]
def custom_fields_tabs
CUSTOM_FIELDS_TABS
tabs = [{:name => 'IssueCustomField', :partial => 'custom_fields/index', :label => :label_issue_plural},
{:name => 'TimeEntryCustomField', :partial => 'custom_fields/index', :label => :label_spent_time},
{:name => 'ProjectCustomField', :partial => 'custom_fields/index', :label => :label_project_plural},
{:name => 'VersionCustomField', :partial => 'custom_fields/index', :label => :label_version_plural},
{:name => 'UserCustomField', :partial => 'custom_fields/index', :label => :label_user_plural},
{:name => 'GroupCustomField', :partial => 'custom_fields/index', :label => :label_group_plural},
{:name => 'TimeEntryActivityCustomField', :partial => 'custom_fields/index', :label => TimeEntryActivity::OptionName},
{:name => 'IssuePriorityCustomField', :partial => 'custom_fields/index', :label => IssuePriority::OptionName},
{:name => 'DocumentCategoryCustomField', :partial => 'custom_fields/index', :label => DocumentCategory::OptionName}
]
end
# Return custom field html tag corresponding to its format
@@ -98,44 +86,32 @@ module CustomFieldsHelper
custom_field_label_tag(name, custom_value, options) + custom_field_tag(name, custom_value)
end
def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil, value='')
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"}
unset_tag = ''
unless custom_field.is_required?
unset_tag = content_tag('label',
check_box_tag(field_name, '__none__', (value == '__none__'), :id => nil, :data => {:disables => "##{field_id}"}) + l(:button_clear),
:class => 'inline'
)
end
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
case field_format.try(:edit_as)
when "date"
text_field_tag(field_name, value, tag_options.merge(:size => 10)) +
calendar_for(field_id) +
unset_tag
text_field_tag(field_name, '', tag_options.merge(:size => 10)) +
calendar_for(field_id)
when "text"
text_area_tag(field_name, value, tag_options.merge(:rows => 3)) +
'<br />'.html_safe +
unset_tag
text_area_tag(field_name, '', tag_options.merge(:rows => 3))
when "bool"
select_tag(field_name, options_for_select([[l(:label_no_change_option), ''],
[l(:general_text_yes), '1'],
[l(:general_text_no), '0']], value), tag_options)
[l(:general_text_no), '0']]), tag_options)
when "list"
options = []
options << [l(:label_no_change_option), ''] unless custom_field.multiple?
options << [l(:label_none), '__none__'] unless custom_field.is_required?
options += custom_field.possible_values_options(projects)
select_tag(field_name, options_for_select(options, value), tag_options.merge(:multiple => custom_field.multiple?))
select_tag(field_name, options_for_select(options), tag_options.merge(:multiple => custom_field.multiple?))
else
text_field_tag(field_name, value, tag_options) +
unset_tag
text_field_tag(field_name, '', tag_options)
end
end
+1 -1
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+1 -1
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+5 -5
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -24,19 +24,19 @@ module GanttHelper
when :in
if gantt.zoom < 4
link_to_content_update l(:text_zoom_in),
params.merge(gantt.params.merge(:zoom => (gantt.zoom + 1))),
params.merge(gantt.params.merge(:zoom => (gantt.zoom+1))),
:class => 'icon icon-zoom-in'
else
content_tag(:span, l(:text_zoom_in), :class => 'icon icon-zoom-in').html_safe
content_tag('span', l(:text_zoom_in), :class => 'icon icon-zoom-in').html_safe
end
when :out
if gantt.zoom > 1
link_to_content_update l(:text_zoom_out),
params.merge(gantt.params.merge(:zoom => (gantt.zoom - 1))),
params.merge(gantt.params.merge(:zoom => (gantt.zoom-1))),
:class => 'icon icon-zoom-out'
else
content_tag(:span, l(:text_zoom_out), :class => 'icon icon-zoom-out').html_safe
content_tag('span', l(:text_zoom_out), :class => 'icon icon-zoom-out').html_safe
end
end
end
+10 -16
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -18,25 +18,19 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module GroupsHelper
# Options for the new membership projects combo-box
def options_for_membership_project_select(user, projects)
options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---")
options << project_tree_options_for_select(projects) do |p|
{:disabled => (user.projects.include?(p))}
end
options
end
def group_settings_tabs
tabs = [{:name => 'general', :partial => 'groups/general', :label => :label_general},
{:name => 'users', :partial => 'groups/users', :label => :label_user_plural},
{:name => 'memberships', :partial => 'groups/memberships', :label => :label_project_plural}
]
end
def render_principals_for_new_group_users(group)
scope = User.active.sorted.not_in_group(group).like(params[:q])
principal_count = scope.count
principal_pages = Redmine::Pagination::Paginator.new principal_count, 100, params['page']
principals = scope.offset(principal_pages.offset).limit(principal_pages.per_page).all
s = content_tag('div', principals_check_box_tags('user_ids[]', principals), :id => 'principals')
links = pagination_links_full(principal_pages, principal_count, :per_page_links => false) {|text, parameters, options|
link_to text, autocomplete_for_user_group_path(group, parameters.merge(:q => params[:q], :format => 'js')), :remote => true
}
s + content_tag('p', links, :class => 'pagination')
end
end
+1 -1
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+1 -1
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+1 -1
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+70 -102
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -65,7 +65,7 @@ module IssuesHelper
s = ''
ancestors = issue.root? ? [] : issue.ancestors.visible.all
ancestors.each do |ancestor|
s << '<div>' + content_tag('p', link_to_issue(ancestor, :project => (issue.project_id != ancestor.project_id)))
s << '<div>' + content_tag('p', link_to_issue(ancestor))
end
s << '<div>'
subject = h(issue.subject)
@@ -80,43 +80,18 @@ module IssuesHelper
def render_descendants_tree(issue)
s = '<form><table class="list issues">'
issue_list(issue.descendants.visible.sort_by(&:lft)) do |child, level|
css = "issue issue-#{child.id} hascontextmenu"
css << " idnt idnt-#{level}" if level > 0
s << content_tag('tr',
content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
content_tag('td', link_to_issue(child, :truncate => 60, :project => (issue.project_id != child.project_id)), :class => 'subject') +
content_tag('td', link_to_issue(child, :truncate => 60), :class => 'subject') +
content_tag('td', h(child.status)) +
content_tag('td', link_to_user(child.assigned_to)) +
content_tag('td', progress_bar(child.done_ratio, :width => '80px')),
:class => css)
:class => "issue issue-#{child.id} hascontextmenu #{level > 0 ? "idnt idnt-#{level}" : nil}")
end
s << '</table></form>'
s.html_safe
end
# Returns an array of error messages for bulk edited issues
def bulk_edit_error_messages(issues)
messages = {}
issues.each do |issue|
issue.errors.full_messages.each do |message|
messages[message] ||= []
messages[message] << issue
end
end
messages.map { |message, issues|
"#{message}: " + issues.map {|i| "##{i.id}"}.join(', ')
}
end
# Returns a link for adding a new subtask to the given issue
def link_to_new_subtask(issue)
attrs = {
:tracker_id => issue.tracker,
:parent_issue_id => issue
}
link_to(l(:button_add), new_project_issue_path(issue.project, :issue => attrs))
end
class IssueFieldsRows
include ActionView::Helpers::TagHelper
@@ -160,13 +135,12 @@ module IssuesHelper
end
def render_custom_fields_rows(issue)
values = issue.visible_custom_field_values
return if values.empty?
return if issue.custom_field_values.empty?
ordered_values = []
half = (values.size / 2.0).ceil
half = (issue.custom_field_values.size / 2.0).ceil
half.times do |i|
ordered_values << values[i]
ordered_values << values[i + half]
ordered_values << issue.custom_field_values[i]
ordered_values << issue.custom_field_values[i + half]
end
s = "<tr>\n"
n = 0
@@ -199,60 +173,36 @@ module IssuesHelper
def sidebar_queries
unless @sidebar_queries
@sidebar_queries = IssueQuery.visible.
order("#{Query.table_name}.name ASC").
@sidebar_queries = Query.visible.all(
:order => "#{Query.table_name}.name ASC",
# Project specific queries and global queries
where(@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id]).
all
:conditions => (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
)
end
@sidebar_queries
end
def query_links(title, queries)
return '' if queries.empty?
# links to #index on issues/show
url_params = controller_name == 'issues' ? {:controller => 'issues', :action => 'index', :project_id => @project} : params
content_tag('h3', title) + "\n" +
content_tag('ul',
queries.collect {|query|
css = 'query'
css << ' selected' if query == @query
content_tag('li', link_to(query.name, url_params.merge(:query_id => query), :class => css))
}.join("\n").html_safe,
:class => 'queries'
) + "\n"
content_tag('h3', h(title)) +
queries.collect {|query|
css = 'query'
css << ' selected' if query == @query
link_to(h(query.name), url_params.merge(:query_id => query), :class => css)
}.join('<br />').html_safe
end
def render_sidebar_queries
out = ''.html_safe
out << query_links(l(:label_my_queries), sidebar_queries.select(&:is_private?))
out << query_links(l(:label_query_plural), sidebar_queries.reject(&:is_private?))
queries = sidebar_queries.select {|q| !q.is_public?}
out << query_links(l(:label_my_queries), queries) if queries.any?
queries = sidebar_queries.select {|q| q.is_public?}
out << query_links(l(:label_query_plural), queries) if queries.any?
out
end
def email_issue_attributes(issue, user)
items = []
%w(author status priority assigned_to category fixed_version).each do |attribute|
unless issue.disabled_core_fields.include?(attribute+"_id")
items << "#{l("field_#{attribute}")}: #{issue.send attribute}"
end
end
issue.visible_custom_field_values(user).each do |value|
items << "#{value.custom_field.name}: #{show_value(value)}"
end
items
end
def render_email_issue_attributes(issue, user, html=false)
items = email_issue_attributes(issue, user)
if html
content_tag('ul', items.map{|s| content_tag('li', s)}.join("\n").html_safe)
else
items.map{|s| "* #{s}"}.join("\n")
end
end
# Returns the textual representation of a journal details
# as an array of strings
def details_to_strings(details, no_html=false, options={})
@@ -261,23 +211,23 @@ module IssuesHelper
values_by_field = {}
details.each do |detail|
if detail.property == 'cf'
field = detail.custom_field
field_id = detail.prop_key
field = CustomField.find_by_id(field_id)
if field && field.multiple?
values_by_field[field] ||= {:added => [], :deleted => []}
values_by_field[field_id] ||= {:added => [], :deleted => []}
if detail.old_value
values_by_field[field][:deleted] << detail.old_value
values_by_field[field_id][:deleted] << detail.old_value
end
if detail.value
values_by_field[field][:added] << detail.value
values_by_field[field_id][:added] << detail.value
end
next
end
end
strings << show_detail(detail, no_html, options)
end
values_by_field.each do |field, changes|
detail = JournalDetail.new(:property => 'cf', :prop_key => field.id.to_s)
detail.instance_variable_set "@custom_field", field
values_by_field.each do |field_id, changes|
detail = JournalDetail.new(:property => 'cf', :prop_key => field_id)
if changes[:added].any?
detail.value = changes[:added]
strings << show_detail(detail, no_html, options)
@@ -320,7 +270,7 @@ module IssuesHelper
old_value = l(detail.old_value == "0" ? :general_text_No : :general_text_Yes) unless detail.old_value.blank?
end
when 'cf'
custom_field = detail.custom_field
custom_field = CustomField.find_by_id(detail.prop_key)
if custom_field
multiple = custom_field.multiple?
label = custom_field.name
@@ -329,17 +279,6 @@ module IssuesHelper
end
when 'attachment'
label = l(:label_attachment)
when 'relation'
if detail.value && !detail.old_value
rel_issue = Issue.visible.find_by_id(detail.value)
value = rel_issue.nil? ? "#{l(:label_issue)} ##{detail.value}" :
(no_html ? rel_issue : link_to_issue(rel_issue))
elsif detail.old_value && !detail.value
rel_issue = Issue.visible.find_by_id(detail.old_value)
old_value = rel_issue.nil? ? "#{l(:label_issue)} ##{detail.old_value}" :
(no_html ? rel_issue : link_to_issue(rel_issue))
end
label = l(detail.prop_key.to_sym)
end
call_hook(:helper_issues_show_detail_after_setting,
{:detail => detail, :label => label, :value => value, :old_value => old_value })
@@ -351,9 +290,7 @@ module IssuesHelper
unless no_html
label = content_tag('strong', label)
old_value = content_tag("i", h(old_value)) if detail.old_value
if detail.old_value && detail.value.blank? && detail.property != 'relation'
old_value = content_tag("del", old_value)
end
old_value = content_tag("del", 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])
@@ -389,7 +326,7 @@ module IssuesHelper
else
l(:text_journal_set_to, :label => label, :value => value).html_safe
end
when 'attachment', 'relation'
when 'attachment'
l(:text_journal_added, :label => label, :value => value).html_safe
end
else
@@ -399,16 +336,10 @@ module IssuesHelper
# Find the name of an associated record stored in the field attribute
def find_name_by_reflection(field, id)
unless id.present?
return nil
end
association = Issue.reflect_on_association(field.to_sym)
if association
record = association.class_name.constantize.find_by_id(id)
if record
record.name.force_encoding('UTF-8') if record.name.respond_to?(:force_encoding)
return record.name
end
return record.name if record
end
end
@@ -425,4 +356,41 @@ module IssuesHelper
end
end
end
def issues_to_csv(issues, project, query, options={})
decimal_separator = l(:general_csv_decimal_separator)
encoding = l(:general_csv_encoding)
columns = (options[:columns] == 'all' ? query.available_columns : query.columns)
export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
# csv header fields
csv << [ "#" ] + columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) } +
(options[:description] ? [Redmine::CodesetUtil.from_utf8(l(:field_description), encoding)] : [])
# csv lines
issues.each do |issue|
col_values = columns.collect do |column|
s = if column.is_a?(QueryCustomFieldColumn)
cv = issue.custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id}
show_value(cv)
else
value = column.value(issue)
if value.is_a?(Date)
format_date(value)
elsif value.is_a?(Time)
format_time(value)
elsif value.is_a?(Float)
("%.2f" % value).gsub('.', decimal_separator)
else
value
end
end
s.to_s
end
csv << [ issue.id.to_s ] + col_values.collect {|c| Redmine::CodesetUtil.from_utf8(c.to_s, encoding) } +
(options[:description] ? [Redmine::CodesetUtil.from_utf8(issue.description, encoding)] : [])
end
end
export
end
end
+1 -1
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+1 -1
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+1 -15
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -18,18 +18,4 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module MembersHelper
def render_principals_for_new_members(project)
scope = Principal.active.sorted.not_member_of(project).like(params[:q])
principal_count = scope.count
principal_pages = Redmine::Pagination::Paginator.new principal_count, 100, params['page']
principals = scope.offset(principal_pages.offset).limit(principal_pages.per_page).all
s = content_tag('div', principals_check_box_tags('membership[user_ids][]', principals), :id => 'principals')
links = pagination_links_full(principal_pages, principal_count, :per_page_links => false) {|text, parameters, options|
link_to text, autocomplete_project_memberships_path(project, parameters.merge(:q => params[:q], :format => 'js')), :remote => true
}
s + content_tag('p', links, :class => 'pagination')
end
end
+1 -1
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+1 -51
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -18,54 +18,4 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module MyHelper
def calendar_items(startdt, enddt)
Issue.visible.
where(:project_id => User.current.projects.map(&:id)).
where("(start_date>=? and start_date<=?) or (due_date>=? and due_date<=?)", startdt, enddt, startdt, enddt).
includes(:project, :tracker, :priority, :assigned_to).
all
end
def documents_items
Document.visible.order("#{Document.table_name}.created_on DESC").limit(10).all
end
def issuesassignedtome_items
Issue.visible.open.
where(:assigned_to_id => ([User.current.id] + User.current.group_ids)).
limit(10).
includes(:status, :project, :tracker, :priority).
order("#{IssuePriority.table_name}.position DESC, #{Issue.table_name}.updated_on DESC").
all
end
def issuesreportedbyme_items
Issue.visible.
where(:author_id => User.current.id).
limit(10).
includes(:status, :project, :tracker).
order("#{Issue.table_name}.updated_on DESC").
all
end
def issueswatched_items
Issue.visible.on_active_project.watched_by(User.current.id).recently_updated.limit(10).all
end
def news_items
News.visible.
where(:project_id => User.current.projects.map(&:id)).
limit(10).
includes(:project, :author).
order("#{News.table_name}.created_on DESC").
all
end
def timelog_items
TimeEntry.
where("#{TimeEntry.table_name}.user_id = ? AND #{TimeEntry.table_name}.spent_on BETWEEN ? AND ?", User.current.id, Date.today - 6, Date.today).
includes(:activity, :project, {:issue => [:tracker, :status]}).
order("#{TimeEntry.table_name}.spent_on DESC, #{Project.table_name}.name ASC, #{Tracker.table_name}.position ASC, #{Issue.table_name}.id ASC").
all
end
end
+1 -1
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+4 -5
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -46,7 +46,7 @@ module ProjectsHelper
end
options = ''
options << "<option value=''>&nbsp;</option>" if project.allowed_parents.include?(nil)
options << "<option value=''></option>" if project.allowed_parents.include?(nil)
options << project_tree_options_for_select(project.allowed_parents.compact, :selected => selected)
content_tag('select', options.html_safe, :name => 'project[parent_id]', :id => 'project_parent_id')
end
@@ -69,11 +69,10 @@ module ProjectsHelper
grouped[version.project.name] << [version.name, version.id]
end
selected = selected.is_a?(Version) ? selected.id : selected
if grouped.keys.size > 1
grouped_options_for_select(grouped, selected)
grouped_options_for_select(grouped, selected && selected.id)
else
options_for_select((grouped.values.first || []), selected)
options_for_select((grouped.values.first || []), selected && selected.id)
end
end
+30 -120
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -19,59 +19,11 @@
module QueriesHelper
def filters_options_for_select(query)
options_for_select(filters_options(query))
end
def filters_options(query)
options = [[]]
options += query.available_filters.map do |field, field_options|
options += query.available_filters.sort {|a,b| a[1][:order] <=> b[1][:order]}.map do |field, field_options|
[field_options[:name], field]
end
end
def query_filters_hidden_tags(query)
tags = ''.html_safe
query.filters.each do |field, options|
tags << hidden_field_tag("f[]", field, :id => nil)
tags << hidden_field_tag("op[#{field}]", options[:operator], :id => nil)
options[:values].each do |value|
tags << hidden_field_tag("v[#{field}][]", value, :id => nil)
end
end
tags
end
def query_columns_hidden_tags(query)
tags = ''.html_safe
query.columns.each do |column|
tags << hidden_field_tag("c[]", column.name, :id => nil)
end
tags
end
def query_hidden_tags(query)
query_filters_hidden_tags(query) + query_columns_hidden_tags(query)
end
def available_block_columns_tags(query)
tags = ''.html_safe
query.available_block_columns.each do |column|
tags << content_tag('label', check_box_tag('c[]', column.name.to_s, query.has_column?(column)) + " #{column.caption}", :class => 'inline')
end
tags
end
def query_available_inline_columns_options(query)
(query.available_inline_columns - query.columns).reject(&:frozen?).collect {|column| [column.caption, column.name]}
end
def query_selected_inline_columns_options(query)
(query.inline_columns & query.available_inline_columns).reject(&:frozen?).collect {|column| [column.caption, column.name]}
end
def render_query_columns_selection(query, options={})
tag_name = (options[:name] || 'c') + '[]'
render :partial => 'queries/columns', :locals => {:query => query, :tag_name => tag_name}
options_for_select(options)
end
def column_header(column)
@@ -83,7 +35,7 @@ module QueriesHelper
def column_content(column, issue)
value = column.value(issue)
if value.is_a?(Array)
value.collect {|v| column_value(column, issue, v)}.compact.join(', ').html_safe
value.collect {|v| column_value(column, issue, v)}.compact.sort.join(', ').html_safe
else
column_value(column, issue, value)
end
@@ -94,8 +46,6 @@ module QueriesHelper
when 'String'
if column.name == :subject
link_to(h(value), :controller => 'issues', :action => 'show', :id => issue)
elsif column.name == :description
issue.description? ? content_tag('div', textilizable(issue, :description), :class => "wiki") : ''
else
h(value)
end
@@ -103,16 +53,14 @@ module QueriesHelper
format_time(value)
when 'Date'
format_date(value)
when 'Fixnum'
if column.name == :id
link_to value, issue_path(issue)
elsif column.name == :done_ratio
when 'Fixnum', 'Float'
if column.name == :done_ratio
progress_bar(value, :width => '80px')
elsif column.name == :spent_hours
sprintf "%.2f", value
else
value.to_s
h(value.to_s)
end
when 'Float'
sprintf "%.2f", value
when 'User'
link_to_user value
when 'Project'
@@ -124,83 +72,32 @@ module QueriesHelper
when 'FalseClass'
l(:general_text_No)
when 'Issue'
value.visible? ? link_to_issue(value) : "##{value.id}"
when 'IssueRelation'
other = value.other_issue(issue)
content_tag('span',
(l(value.label_for(issue)) + " " + link_to_issue(other, :subject => false, :tracker => false)).html_safe,
:class => value.css_classes_for(issue))
link_to_issue(value, :subject => false)
else
h(value)
end
end
def csv_content(column, issue)
value = column.value(issue)
if value.is_a?(Array)
value.collect {|v| csv_value(column, issue, v)}.compact.join(', ')
else
csv_value(column, issue, value)
end
end
def csv_value(column, issue, value)
case value.class.name
when 'Time'
format_time(value)
when 'Date'
format_date(value)
when 'Float'
sprintf("%.2f", value).gsub('.', l(:general_csv_decimal_separator))
when 'IssueRelation'
other = value.other_issue(issue)
l(value.label_for(issue)) + " ##{other.id}"
else
value.to_s
end
end
def query_to_csv(items, query, options={})
encoding = l(:general_csv_encoding)
columns = (options[:columns] == 'all' ? query.available_inline_columns : query.inline_columns)
query.available_block_columns.each do |column|
if options[column.name].present?
columns << column
end
end
export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
# csv header fields
csv << columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) }
# csv lines
items.each do |item|
csv << columns.collect {|c| Redmine::CodesetUtil.from_utf8(csv_content(c, item), encoding) }
end
end
export
end
# Retrieve query from session or build a new query
def retrieve_query
if !params[:query_id].blank?
cond = "project_id IS NULL"
cond << " OR project_id = #{@project.id}" if @project
@query = IssueQuery.where(cond).find(params[:query_id])
@query = Query.find(params[:query_id], :conditions => cond)
raise ::Unauthorized unless @query.visible?
@query.project = @project
session[:query] = {:id => @query.id, :project_id => @query.project_id}
sort_clear
elsif api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
# Give it a name, required to be valid
@query = IssueQuery.new(:name => "_")
@query = Query.new(:name => "_")
@query.project = @project
@query.build_from_params(params)
build_query_from_params
session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
else
# retrieve from session
@query = nil
@query = IssueQuery.find_by_id(session[:query][:id]) if session[:query][:id]
@query ||= IssueQuery.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
@query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
@query ||= Query.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
@query.project = @project
end
end
@@ -208,10 +105,10 @@ module QueriesHelper
def retrieve_query_from_session
if session[:query]
if session[:query][:id]
@query = IssueQuery.find_by_id(session[:query][:id])
@query = Query.find_by_id(session[:query][:id])
return unless @query
else
@query = IssueQuery.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
@query = Query.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
end
if session[:query].has_key?(:project_id)
@query.project_id = session[:query][:project_id]
@@ -221,4 +118,17 @@ module QueriesHelper
@query
end
end
def build_query_from_params
if params[:fields] || params[:f]
@query.filters = {}
@query.add_filters(params[:fields] || params[:f], params[:operators] || params[:op], params[:values] || params[:v])
else
@query.available_filters.keys.each do |field|
@query.add_short_filter(field, params[field]) if params[field]
end
end
@query.group_by = params[:group_by] || (params[:query] && params[:query][:group_by])
@query.column_names = params[:c] || (params[:query] && params[:query][:column_names])
end
end
+2 -7
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -24,7 +24,7 @@ module ReportsHelper
data.each { |row|
match = 1
criteria.each { |k, v|
match = 0 unless (row[k].to_s == v.to_s) || (k == 'closed' && (v == 0 ? ['f', false] : ['t', true]).include?(row[k]))
match = 0 unless (row[k].to_s == v.to_s) || (k == 'closed' && row[k] == (v == 0 ? "f" : "t"))
} unless criteria.nil?
a = a + row["total"].to_i if match == 1
} unless data.nil?
@@ -35,9 +35,4 @@ module ReportsHelper
a = aggregate data, criteria
a > 0 ? link_to(h(a), *args) : '-'
end
def aggregate_path(project, field, row, options={})
parameters = {:set_filter => 1, :subproject_id => '!*', field => row.id}.merge(options)
project_issues_path(row.is_a?(Project) ? row : project, parameters)
end
end
+18 -2
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -17,6 +17,9 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'iconv'
require 'redmine/codeset_util'
module RepositoriesHelper
def format_revision(revision)
if revision.respond_to? :format_identifier
@@ -43,7 +46,7 @@ module RepositoriesHelper
end
def render_changeset_changes
changes = @changeset.filechanges.limit(1000).reorder('path').all.collect do |change|
changes = @changeset.filechanges.find(:all, :limit => 1000, :order => 'path').collect do |change|
case change.action
when 'A'
# Detects moved/copied files
@@ -250,13 +253,16 @@ module RepositoriesHelper
def index_commits(commits, heads)
return nil if commits.nil? or commits.first.parents.nil?
refs_map = {}
heads.each do |head|
refs_map[head.scmid] ||= []
refs_map[head.scmid] << head
end
commits_by_scmid = {}
commits.reverse.each_with_index do |commit, commit_index|
commits_by_scmid[commit.scmid] = {
:parent_scmids => commit.parents.collect { |parent| parent.scmid },
:rdmid => commit_index,
@@ -265,28 +271,38 @@ module RepositoriesHelper
:href => block_given? ? yield(commit.scmid) : commit.scmid
}
end
heads.sort! { |head1, head2| head1.to_s <=> head2.to_s }
space = nil
heads.each do |head|
if commits_by_scmid.include? head.scmid
space = index_head((space || -1) + 1, head, commits_by_scmid)
end
end
# when no head matched anything use first commit
space ||= index_head(0, commits.first, commits_by_scmid)
return commits_by_scmid, space
end
def index_head(space, commit, commits_by_scmid)
stack = [[space, commits_by_scmid[commit.scmid]]]
max_space = space
until stack.empty?
space, commit = stack.pop
commit[:space] = space if commit[:space].nil?
space -= 1
commit[:parent_scmids].each_with_index do |parent_scmid, parent_index|
parent_commit = commits_by_scmid[parent_scmid]
if parent_commit and parent_commit[:space].nil?
stack.unshift [space += 1, parent_commit]
end
end
+1 -1
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+1 -1
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+3 -15
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -53,10 +53,10 @@ module SettingsHelper
check_box_tag(
"settings[#{setting}][]",
value,
setting_values.include?(value),
Setting.send(setting).include?(value),
:id => nil
) + text.to_s,
:class => (options[:inline] ? 'inline' : 'block')
:class => 'block'
)
end.join.html_safe
end
@@ -91,16 +91,4 @@ module SettingsHelper
l_or_humanize(notifiable.name, :prefix => 'label_').html_safe,
:class => notifiable.parent.present? ? "parent" : '').html_safe
end
def cross_project_subtasks_options
options = [
[:label_disabled, ''],
[:label_cross_project_system, 'system'],
[:label_cross_project_tree, 'tree'],
[:label_cross_project_hierarchy, 'hierarchy'],
[:label_cross_project_descendants, 'descendants']
]
options.map {|label, value| [l(label), value.to_s]}
end
end
+2 -11
View File
@@ -80,20 +80,15 @@ module SortHelper
@criteria.collect {|k,o| k + (o ? '' : ':desc')}.join(',')
end
# Returns an array of SQL fragments used to sort the list
def to_sql
sql = @criteria.collect do |k,o|
if s = @available_criteria[k]
(o ? s.to_a : s.to_a.collect {|c| append_desc(c)})
(o ? s.to_a : s.to_a.collect {|c| append_desc(c)}).join(', ')
end
end.flatten.compact
end.compact.join(', ')
sql.blank? ? nil : sql
end
def to_a
@criteria.dup
end
def add!(key, asc)
@criteria.delete_if {|k,o| k == key}
@criteria = [[key, asc]] + @criteria
@@ -187,10 +182,6 @@ module SortHelper
@sort_criteria.to_sql
end
def sort_criteria
@sort_criteria
end
# Returns a link which sorts by the named column.
#
# - column is the name of an attribute in the sorted record collection.
+49 -7
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -56,10 +56,10 @@ module TimelogHelper
end
def select_hours(data, criteria, value)
if value.to_s.empty?
data.select {|row| row[criteria].blank? }
if value.to_s.empty?
data.select {|row| row[criteria].blank? }
else
data.select {|row| row[criteria].to_s == value.to_s}
data.select {|row| row[criteria].to_s == value.to_s}
end
end
@@ -77,7 +77,6 @@ module TimelogHelper
[l(:label_yesterday), 'yesterday'],
[l(:label_this_week), 'current_week'],
[l(:label_last_week), 'last_week'],
[l(:label_last_n_weeks, 2), 'last_2_weeks'],
[l(:label_last_n_days, 7), '7_days'],
[l(:label_this_month), 'current_month'],
[l(:label_last_month), 'last_month'],
@@ -86,6 +85,49 @@ module TimelogHelper
value)
end
def entries_to_csv(entries)
decimal_separator = l(:general_csv_decimal_separator)
custom_fields = TimeEntryCustomField.find(:all)
export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
# csv header fields
headers = [l(:field_spent_on),
l(:field_user),
l(:field_activity),
l(:field_project),
l(:field_issue),
l(:field_tracker),
l(:field_subject),
l(:field_hours),
l(:field_comments)
]
# Export custom fields
headers += custom_fields.collect(&:name)
csv << headers.collect {|c| Redmine::CodesetUtil.from_utf8(
c.to_s,
l(:general_csv_encoding) ) }
# csv lines
entries.each do |entry|
fields = [format_date(entry.spent_on),
entry.user,
entry.activity,
entry.project,
(entry.issue ? entry.issue.id : nil),
(entry.issue ? entry.issue.tracker : nil),
(entry.issue ? entry.issue.subject : nil),
entry.hours.to_s.gsub('.', decimal_separator),
entry.comments
]
fields += custom_fields.collect {|f| show_value(entry.custom_field_values.detect {|v| v.custom_field_id == f.id}) }
csv << fields.collect {|c| Redmine::CodesetUtil.from_utf8(
c.to_s,
l(:general_csv_encoding) ) }
end
end
export
end
def format_criteria_value(criteria_options, value)
if value.blank?
"[#{l(:label_none)}]"
@@ -107,14 +149,14 @@ module TimelogHelper
# Column headers
headers = report.criteria.collect {|criteria| l(report.available_criteria[criteria][:label]) }
headers += report.periods
headers << l(:label_total_time)
headers << l(:label_total)
csv << headers.collect {|c| Redmine::CodesetUtil.from_utf8(
c.to_s,
l(:general_csv_encoding) ) }
# Content
report_criteria_to_csv(csv, report.available_criteria, report.columns, report.criteria, report.periods, report.hours)
# Total row
str_total = Redmine::CodesetUtil.from_utf8(l(:label_total_time), l(:general_csv_encoding))
str_total = Redmine::CodesetUtil.from_utf8(l(:label_total), l(:general_csv_encoding))
row = [ str_total ] + [''] * (report.criteria.size - 1)
total = 0
report.periods.each do |period|
+1 -1
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+10 -1
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -26,6 +26,15 @@ module UsersHelper
["#{l(:status_locked)} (#{user_count_by_status[3].to_i})", '3']], selected.to_s)
end
# Options for the new membership projects combo-box
def options_for_membership_project_select(user, projects)
options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---")
options << project_tree_options_for_select(projects) do |p|
{:disabled => (user.projects.include?(p))}
end
options
end
def user_mail_notification_options(user)
user.valid_notification_options.collect {|o| [l(o.last), o.first]}
end
+7 -5
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -35,14 +35,16 @@ module VersionsHelper
h = Hash.new {|k,v| k[v] = [0, 0]}
begin
# Total issue count
Issue.where(:fixed_version_id => version.id).group(criteria).count.each {|c,s| h[c][0] = s}
Issue.count(:group => criteria,
:conditions => ["#{Issue.table_name}.fixed_version_id = ?", version.id]).each {|c,s| h[c][0] = s}
# Open issues count
Issue.open.where(:fixed_version_id => version.id).group(criteria).count.each {|c,s| h[c][1] = s}
Issue.count(:group => criteria,
:include => :status,
:conditions => ["#{Issue.table_name}.fixed_version_id = ? AND #{IssueStatus.table_name}.is_closed = ?", version.id, false]).each {|c,s| h[c][1] = s}
rescue ActiveRecord::RecordNotFound
# When grouping by an association, Rails throws this exception if there's no result (bug)
end
# Sort with nil keys in last position
counts = h.keys.sort {|a,b| a.nil? ? 1 : (b.nil? ? -1 : a <=> b)}.collect {|k| {:group => k, :total => h[k][0], :open => h[k][1], :closed => (h[k][0] - h[k][1])}}
counts = h.keys.compact.sort.collect {|k| {:group => k, :total => h[k][0], :open => h[k][1], :closed => (h[k][0] - h[k][1])}}
max = counts.collect {|c| c[:total]}.max
render :partial => 'issue_counts', :locals => {:version => version, :criteria => criteria, :counts => counts, :max => max}
+16 -23
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -20,31 +20,24 @@
module WatchersHelper
def watcher_tag(object, user, options={})
ActiveSupport::Deprecation.warn "#watcher_tag is deprecated and will be removed in Redmine 3.0. Use #watcher_link instead."
watcher_link(object, user)
content_tag("span", watcher_link(object, user), :class => watcher_css(object))
end
def watcher_link(objects, user)
return '' unless user && user.logged?
objects = Array.wrap(objects)
def watcher_link(object, user)
return '' unless user && user.logged? && object.respond_to?('watched_by?')
watched = object.watched_by?(user)
url = {:controller => 'watchers',
: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'))
watched = Watcher.any_watched?(objects, user)
css = [watcher_css(objects), watched ? 'icon icon-fav' : 'icon icon-fav-off'].join(' ')
text = watched ? l(:button_unwatch) : l(:button_watch)
url = watch_path(
:object_type => objects.first.class.to_s.underscore,
:object_id => (objects.size == 1 ? objects.first.id : objects.map(&:id).sort)
)
method = watched ? 'delete' : 'post'
link_to text, url, :remote => true, :method => method, :class => css
end
# Returns the css class used to identify watch links for a given +object+
def watcher_css(objects)
objects = Array.wrap(objects)
id = (objects.size == 1 ? objects.first.id : 'bulk')
"#{objects.first.class.to_s.underscore}-#{id}-watcher"
def watcher_css(object)
"#{object.class.to_s.underscore}-#{object.id}-watcher"
end
# Returns a comma separated list of users watching the given object
@@ -63,11 +56,11 @@ module WatchersHelper
:user_id => user}
s << ' '
s << link_to(image_tag('delete.png'), url,
:remote => true, :method => 'delete', :class => "delete")
:remote => true, :method => 'post', :style => "vertical-align: middle", :class => "delete")
end
content << content_tag('li', s, :class => "user-#{user.id}")
content << content_tag('li', s)
end
content.present? ? content_tag('ul', content, :class => 'watchers') : content
content.present? ? content_tag('ul', content) : content
end
def watchers_checkboxes(object, users, checked=nil)
+1 -1
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+2 -2
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -37,7 +37,7 @@ module WikiHelper
def wiki_page_breadcrumb(page)
breadcrumb(page.ancestors.reverse.collect {|parent|
link_to(h(parent.pretty_title), {:controller => 'wiki', :action => 'show', :id => parent.title, :project_id => parent.project, :version => nil})
link_to(h(parent.pretty_title), {:controller => 'wiki', :action => 'show', :id => parent.title, :project_id => parent.project})
})
end
end
+3 -12
View File
@@ -1,7 +1,7 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -22,20 +22,11 @@ module WorkflowsHelper
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, role)
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)
html_options = {}
selected = permissions[status.id][name]
hidden = field.is_a?(CustomField) && !field.visible? && !role.custom_fields.to_a.include?(field)
if hidden
options[0][0] = l(:label_hidden)
selected = ''
html_options[:disabled] = true
end
select_tag("permissions[#{name}][#{status.id}]", options_for_select(options, selected), html_options)
select_tag("permissions[#{name}][#{status.id}]", options_for_select(options, permissions[status.id][name]))
end
end
+13 -52
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require "digest/md5"
require "fileutils"
class Attachment < ActiveRecord::Base
belongs_to :container, :polymorphic => true
@@ -93,6 +92,9 @@ class Attachment < ActiveRecord::Base
def filename=(arg)
write_attribute :filename, sanitize_filename(arg.to_s)
if new_record? && disk_filename.blank?
self.disk_filename = Attachment.disk_filename(filename)
end
filename
end
@@ -100,13 +102,7 @@ class Attachment < ActiveRecord::Base
# and computes its MD5 hash
def files_to_final_location
if @temp_file && (@temp_file.size > 0)
self.disk_directory = target_directory
self.disk_filename = Attachment.disk_filename(filename, disk_directory)
logger.info("Saving attachment '#{self.diskfile}' (#{@temp_file.size} bytes)") if logger
path = File.dirname(diskfile)
unless File.directory?(path)
FileUtils.mkdir_p(path)
end
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)
@@ -138,7 +134,7 @@ class Attachment < ActiveRecord::Base
# Returns file's location on disk
def diskfile
File.join(self.class.storage_path, disk_directory.to_s, disk_filename.to_s)
File.join(self.class.storage_path, disk_filename.to_s)
end
def title
@@ -158,19 +154,11 @@ class Attachment < ActiveRecord::Base
end
def visible?(user=User.current)
if container_id
container && container.attachments_visible?(user)
else
author == user
end
container && container.attachments_visible?(user)
end
def deletable?(user=User.current)
if container_id
container && container.attachments_deletable?(user)
else
author == user
end
container && container.attachments_deletable?(user)
end
def image?
@@ -263,26 +251,6 @@ class Attachment < ActiveRecord::Base
Attachment.where("created_on < ? AND (container_type IS NULL OR container_type = '')", Time.now - age).destroy_all
end
# Moves an existing attachment to its target directory
def move_to_target_directory!
if !new_record? & readable?
src = diskfile
self.disk_directory = target_directory
dest = diskfile
if src != dest && FileUtils.mkdir_p(File.dirname(dest)) && FileUtils.mv(src, dest)
update_column :disk_directory, disk_directory
end
end
end
# Moves existing attachments that are stored at the root of the files
# directory (ie. created before Redmine 2.3) to their target subdirectories
def self.move_from_root_to_target_directory
Attachment.where("disk_directory IS NULL OR disk_directory = ''").find_each do |attachment|
attachment.move_to_target_directory!
end
end
private
# Physically deletes the file from the file system
@@ -294,21 +262,14 @@ class Attachment < ActiveRecord::Base
def sanitize_filename(value)
# get only the filename, not the whole path
just_filename = value.gsub(/\A.*(\\|\/)/m, '')
just_filename = value.gsub(/^.*(\\|\/)/, '')
# Finally, replace invalid characters with underscore
@filename = just_filename.gsub(/[\/\?\%\*\:\|\"\'<>\n\r]+/, '_')
@filename = just_filename.gsub(/[\/\?\%\*\:\|\"\'<>]+/, '_')
end
# Returns the subdirectory in which the attachment will be saved
def target_directory
time = created_on || DateTime.now
time.strftime("%Y/%m")
end
# Returns an ASCII or hashed filename that do not
# exists yet in the given subdirectory
def self.disk_filename(filename, directory=nil)
# Returns an ASCII or hashed filename
def self.disk_filename(filename)
timestamp = DateTime.now.strftime("%y%m%d%H%M%S")
ascii = ''
if filename =~ %r{^[a-zA-Z0-9_\.\-]*$}
@@ -318,7 +279,7 @@ class Attachment < ActiveRecord::Base
# keep the extension if any
ascii << $1 if filename =~ %r{(\.[a-zA-Z0-9]+)$}
end
while File.exist?(File.join(storage_path, directory.to_s, "#{timestamp}_#{ascii}"))
while File.exist?(File.join(@@storage_path, "#{timestamp}_#{ascii}"))
timestamp.succ!
end
"#{timestamp}_#{ascii}"
+1 -19
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -48,24 +48,6 @@ class AuthSource < ActiveRecord::Base
write_ciphered_attribute(:account_password, arg)
end
def searchable?
false
end
def self.search(q)
results = []
AuthSource.all.each do |source|
begin
if source.searchable?
results += source.search(q)
end
rescue AuthSourceException => e
logger.error "Error while searching users in #{source.name}: #{e.message}"
end
end
results
end
def allow_password_changes?
self.class.allow_password_changes?
end
+9 -36
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -15,6 +15,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'iconv'
require 'net/ldap'
require 'net/ldap/dn'
require 'timeout'
@@ -63,32 +64,6 @@ class AuthSourceLdap < AuthSource
"LDAP"
end
# Returns true if this source can be searched for users
def searchable?
!account.to_s.include?("$login") && %w(login firstname lastname mail).all? {|a| send("attr_#{a}?")}
end
# Searches the source for users and returns an array of results
def search(q)
q = q.to_s.strip
return [] unless searchable? && q.present?
results = []
search_filter = base_filter & Net::LDAP::Filter.begins(self.attr_login, q)
ldap_con = initialize_ldap_con(self.account, self.account_password)
ldap_con.search(:base => self.base_dn,
:filter => search_filter,
:attributes => ['dn', self.attr_login, self.attr_firstname, self.attr_lastname, self.attr_mail],
:size => 10) do |entry|
attrs = get_user_attributes_from_ldap_entry(entry)
attrs[:login] = AuthSourceLdap.get_attr(entry, self.attr_login)
results << attrs
end
results
rescue Net::LDAP::LdapError => e
raise AuthSourceException.new(e.message)
end
private
def with_timeout(&block)
@@ -109,14 +84,6 @@ class AuthSourceLdap < AuthSource
nil
end
def base_filter
filter = Net::LDAP::Filter.eq("objectClass", "*")
if f = ldap_filter
filter = filter & f
end
filter
end
def validate_filter
if filter.present? && ldap_filter.nil?
errors.add(:filter, :invalid)
@@ -173,8 +140,14 @@ class AuthSourceLdap < AuthSource
else
ldap_con = initialize_ldap_con(self.account, self.account_password)
end
login_filter = Net::LDAP::Filter.eq( self.attr_login, login )
object_filter = Net::LDAP::Filter.eq( "objectClass", "*" )
attrs = {}
search_filter = base_filter & Net::LDAP::Filter.eq(self.attr_login, login)
search_filter = object_filter & login_filter
if f = ldap_filter
search_filter = search_filter & f
end
ldap_con.search( :base => self.base_dn,
:filter => search_filter,
+3 -4
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -30,9 +30,8 @@ class Board < ActiveRecord::Base
validates_length_of :description, :maximum => 255
validate :validate_board
scope :visible, lambda {|*args|
includes(:project).where(Project.allowed_to_condition(args.shift || User.current, :view_messages, *args))
}
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'
+1 -1
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+22 -24
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -15,6 +15,8 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'iconv'
class Changeset < ActiveRecord::Base
belongs_to :repository
belongs_to :user
@@ -47,9 +49,9 @@ 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|
includes(:repository => :project).where(Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args))
}
scope :visible,
lambda {|*args| { :include => {:repository => :project},
:conditions => Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args) } }
after_create :scan_for_issues
before_create :before_create_cs
@@ -118,25 +120,22 @@ class Changeset < ActiveRecord::Base
ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip)
ref_keywords_any = ref_keywords.delete('*')
# keywords used to fix issues
fix_keywords = Setting.commit_update_keywords_array.map {|r| r['keywords']}.flatten.compact
fix_keywords = Setting.commit_fix_keywords.downcase.split(",").collect(&:strip)
kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|")
referenced_issues = []
comments.scan(/([\s\(\[,-]|^)((#{kw_regexp})[\s:]+)?(#\d+(\s+@#{TIMELOG_RE})?([\s,;&]+#\d+(\s+@#{TIMELOG_RE})?)*)(?=[[:punct:]]|\s|<|$)/i) do |match|
action, refs = match[2].to_s.downcase, match[3]
action, refs = match[2], match[3]
next unless action.present? || ref_keywords_any
refs.scan(/#(\d+)(\s+@#{TIMELOG_RE})?/).each do |m|
issue, hours = find_referenced_issue_by_id(m[0].to_i), m[2]
if issue
referenced_issues << issue
# Don't update issues or log time when importing old commits
unless repository.created_on && committed_on && committed_on < repository.created_on
fix_issue(issue, action) if fix_keywords.include?(action)
log_time(issue, hours) if hours && Setting.commit_logtime_enabled?
end
fix_issue(issue) if fix_keywords.include?(action.to_s.downcase)
log_time(issue, hours) if hours && Setting.commit_logtime_enabled?
end
end
end
@@ -213,26 +212,25 @@ class Changeset < ActiveRecord::Base
private
# Updates the +issue+ according to +action+
def fix_issue(issue, action)
def fix_issue(issue)
status = IssueStatus.find_by_id(Setting.commit_fix_status_id.to_i)
if status.nil?
logger.warn("No status matches commit_fix_status_id setting (#{Setting.commit_fix_status_id})") if logger
return issue
end
# the issue may have been updated by the closure of another one (eg. duplicate)
issue.reload
# don't change the status is the issue is closed
return if issue.status && issue.status.is_closed?
journal = issue.init_journal(user || User.anonymous,
ll(Setting.default_language,
:text_status_changed_by_changeset,
text_tag(issue.project)))
rule = Setting.commit_update_keywords_array.detect do |rule|
rule['keywords'].include?(action) &&
(rule['if_tracker_id'].blank? || rule['if_tracker_id'] == issue.tracker_id.to_s)
end
if rule
issue.assign_attributes rule.slice(*Issue.attribute_names)
journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, text_tag(issue.project)))
issue.status = status
unless Setting.commit_fix_done_ratio.blank?
issue.done_ratio = Setting.commit_fix_done_ratio.to_i
end
Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update,
{ :changeset => self, :issue => issue, :action => action })
{ :changeset => self, :issue => issue })
unless issue.save
logger.warn("Issue ##{issue.id} could not be saved by changeset #{id}: #{issue.errors.full_messages}") if logger
end
+1 -12
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -22,16 +22,5 @@ class Comment < ActiveRecord::Base
validates_presence_of :commented, :author, :comments
after_create :send_notification
safe_attributes 'comments'
private
def send_notification
mailer_method = "#{commented.class.name.underscore}_comment_added"
if Setting.notified_events.include?(mailer_method)
Mailer.send(mailer_method, self).deliver
end
end
end
@@ -1,7 +1,5 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -17,17 +15,10 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module ActivitiesHelper
def sort_activity_events(events)
events_by_group = events.group_by(&:event_group)
sorted_events = []
events.sort {|x, y| y.event_datetime <=> x.event_datetime}.each do |event|
if group_events = events_by_group.delete(event.event_group)
group_events.sort {|x, y| y.event_datetime <=> x.event_datetime}.each_with_index do |e, i|
sorted_events << [e, i > 0]
end
end
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
end
sorted_events
end
end
+19 -115
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
@@ -19,7 +19,6 @@ class CustomField < ActiveRecord::Base
include Redmine::SubclassFactory
has_many :custom_values, :dependent => :delete_all
has_and_belongs_to_many :roles, :join_table => "#{table_name_prefix}custom_fields_roles#{table_name_suffix}", :foreign_key => "custom_field_id"
acts_as_list :scope => 'type = \'#{self.class}\''
serialize :possible_values
@@ -27,40 +26,9 @@ class CustomField < ActiveRecord::Base
validates_uniqueness_of :name, :scope => :type
validates_length_of :name, :maximum => 30
validates_inclusion_of :field_format, :in => Redmine::CustomFieldFormat.available_formats
validate :validate_custom_field
before_validation :set_searchable
after_save :handle_multiplicity_change
after_save do |field|
if field.visible_changed? && field.visible
field.roles.clear
end
end
scope :sorted, lambda { order("#{table_name}.position ASC") }
scope :visible, lambda {|*args|
user = args.shift || User.current
if user.admin?
# nop
elsif user.memberships.any?
where("#{table_name}.visible = ? OR #{table_name}.id IN (SELECT DISTINCT cfr.custom_field_id FROM #{Member.table_name} m" +
" INNER JOIN #{MemberRole.table_name} mr ON mr.member_id = m.id" +
" INNER JOIN #{table_name_prefix}custom_fields_roles#{table_name_suffix} cfr ON cfr.role_id = mr.role_id" +
" WHERE m.user_id = ?)",
true, user.id)
else
where(:visible => true)
end
}
def visible_by?(project, user=User.current)
visible? || user.admin?
end
def field_format=(arg)
# cannot change format of a saved custom field
super if new_record?
end
def set_searchable
# make sure these fields are not searchable
@@ -123,10 +91,8 @@ class CustomField < ActiveRecord::Base
values.each do |value|
value.force_encoding('UTF-8') if value.respond_to?(:force_encoding)
end
values
else
[]
end
values || []
end
end
@@ -164,13 +130,7 @@ class CustomField < ActiveRecord::Base
possible_values_options = possible_values_options(customized)
if possible_values_options.present?
keyword = keyword.to_s.downcase
if v = possible_values_options.detect {|text, id| text.downcase == keyword}
if v.is_a?(Array)
v.last
else
v
end
end
possible_values_options.detect {|text, id| text.downcase == keyword}.try(:last)
else
keyword
end
@@ -184,12 +144,18 @@ class CustomField < ActiveRecord::Base
case field_format
when 'string', 'text', 'list', 'date', 'bool'
# COALESCE is here to make sure that blank and NULL values are sorted equally
"COALESCE(#{join_alias}.value, '')"
"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), '')"
when 'int', 'float'
# Make the database cast values into numeric
# Postgresql will raise an error if a value can not be casted!
# CustomValue validations should ensure that it doesn't occur
"CAST(CASE #{join_alias}.value WHEN '' THEN '0' ELSE #{join_alias}.value END AS decimal(30,3))"
"(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}'" +
" 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
@@ -205,7 +171,10 @@ class CustomField < ActiveRecord::Base
when 'list', 'date', 'bool', 'int'
order_statement
when 'user', 'version'
"COALESCE(#{join_alias}.value, '')"
"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
@@ -218,35 +187,13 @@ class CustomField < ActiveRecord::Base
" 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 (#{visibility_by_project_condition})" +
" 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(CASE #{join_alias}.value WHEN '' THEN '0' ELSE #{join_alias}.value END AS decimal(30,0)) = #{value_join_alias}.id"
when 'int', 'float'
"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 (#{visibility_by_project_condition})" +
" 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)"
when 'string', 'text', 'list', 'date', 'bool'
"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 (#{visibility_by_project_condition})" +
" 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)"
" ON CAST(#{join_alias}.value as decimal(60,0)) = #{value_join_alias}.id"
else
nil
end
@@ -260,33 +207,6 @@ class CustomField < ActiveRecord::Base
join_alias + "_" + field_format
end
def visibility_by_project_condition(project_key=nil, user=User.current)
if visible? || user.admin?
"1=1"
elsif user.anonymous?
"1=0"
else
project_key ||= "#{self.class.customized_class.table_name}.project_id"
"#{project_key} IN (SELECT DISTINCT m.project_id FROM #{Member.table_name} m" +
" INNER JOIN #{MemberRole.table_name} mr ON mr.member_id = m.id" +
" INNER JOIN #{table_name_prefix}custom_fields_roles#{table_name_suffix} cfr ON cfr.role_id = mr.role_id" +
" WHERE m.user_id = #{user.id} AND cfr.custom_field_id = #{id})"
end
end
def self.visibility_condition
if user.admin?
"1=1"
elsif user.anonymous?
"#{table_name}.visible"
else
"#{project_key} IN (SELECT DISTINCT m.project_id FROM #{Member.table_name} m" +
" INNER JOIN #{MemberRole.table_name} mr ON mr.member_id = m.id" +
" INNER JOIN #{table_name_prefix}custom_fields_roles#{table_name_suffix} cfr ON cfr.role_id = mr.role_id" +
" WHERE m.user_id = #{user.id} AND cfr.custom_field_id = #{id})"
end
end
def <=>(field)
position <=> field.position
end
@@ -303,12 +223,12 @@ class CustomField < ActiveRecord::Base
def self.customized_class
self.name =~ /^(.+)CustomField$/
$1.constantize rescue nil
begin; $1.constantize; rescue nil; end
end
# to move in project_custom_field
def self.for_all
where(:is_for_all => true).order('position').all
find(:all, :conditions => ["is_for_all=?", true], :order => 'position')
end
def type_name
@@ -369,20 +289,4 @@ class CustomField < ActiveRecord::Base
end
errs
end
# Removes multiple values for the custom field after setting the multiple attribute to false
# We kepp the value with the highest id for each customized object
def handle_multiplicity_change
if !new_record? && multiple_was && !multiple
ids = custom_values.
where("EXISTS(SELECT 1 FROM #{CustomValue.table_name} cve WHERE cve.custom_field_id = #{CustomValue.table_name}.custom_field_id" +
" AND cve.customized_type = #{CustomValue.table_name}.customized_type AND cve.customized_id = #{CustomValue.table_name}.customized_id" +
" AND cve.id > #{CustomValue.table_name}.id)").
pluck(:id)
if ids.any?
custom_values.where(:id => ids).delete_all
end
end
end
end
+1 -1
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
+1 -1
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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
Loaded 100 of 1047 files, more files were not shown because too many files have changed in this diff. Show more