Compare commits

..
67 Commits
Author SHA1 Message Date
Jean-Philippe Lang c4164a0a7f Updates for 2.2.4 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11659 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-03-19 19:34:01 +00:00
Toshi MARUYAMA 504a767028 Merged r11648, r11649, r11650 from trunk to 2.2-stable.
upgrade Rails 3.2.13.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11653 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-03-19 08:40:52 +00:00
Toshi MARUYAMA 0eaba11e20 Merged r11637 from trunk to 2.2-stable (#13354)
PDF: fix incompatible character encodings: UTF-8 and ASCII-8BIT.

Contributed by Jun NAITOH.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11638 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-03-16 08:02:20 +00:00
Jean-Philippe Lang c2eb28b901 Merged r11446 from trunk (#13232).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11596 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-03-11 18:30:20 +00:00
Jean-Philippe Lang f09ea7b65a Merged r11351 from trunk (#13127).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11595 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-03-11 18:28:49 +00:00
Jean-Philippe Lang 878e85848f Backported r11424 from trunk (#12243).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11594 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-03-11 18:27:46 +00:00
Jean-Philippe Lang 4b010fb653 Merged r11371 from trunk (#13165).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11593 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-03-11 18:22:03 +00:00
Toshi MARUYAMA 5f96e64cd7 Merged r11553 from trunk to 2.2-stable (#13398, #13391)
Czech translation for 2.2-stable updated by Karel Pičman.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11557 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-03-08 01:02:47 +00:00
Toshi MARUYAMA ec1cb4632c Merged r11529 from trunk to 2.2-stable (#13343, #13339)
r11529 has some 2.2-stable translation.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11532 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-03-03 12:00:40 +00:00
Toshi MARUYAMA e4a1b1324a Merged r11528 from trunk to 2.2-stable (#13343, #13339)
Vietnamese translation for 2.2-stable updated by Minh Thien Nguyen.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11531 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-03-03 12:00:29 +00:00
Toshi MARUYAMA 6991874911 Merged r11498 from trunk to 2.2-stable (#13325, #13324)
pt-BR translation for 2.2-stable updated.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11500 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-03-01 01:52:02 +00:00
Toshi MARUYAMA 17bd53ae43 Merged r11491 from trunk to 2.2-stable (#13310)
pt-BR "label_last_n_weeks" translation updated.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11492 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-02-28 08:33:38 +00:00
Toshi MARUYAMA 559dc4f523 Merged r11432 from trunk to 2.2-stable (#13207, #13206)
Portuguese translation for 2.2-stable updated by Rui Rebelo.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11436 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-02-18 14:19:38 +00:00
Toshi MARUYAMA 6dcd50047d Merged r11389 from trunk to 2.2-stable.
add "tmp:clear" to "ci:setup" rake task for translation.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11435 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-02-18 13:22:25 +00:00
Toshi MARUYAMA 252df8dfc0 Merged r11399 from trunk to 2.2-stable (#13138)
not use htmlentities gem in rfpdf plugin.

Contributed by Jun NAITOH.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11400 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-02-15 14:32:45 +00:00
Toshi MARUYAMA cbd3b057e0 Merged r11390 from trunk to 2.2-stable.
Bulgarian translation of jstoolbar-bg.js updated by Ivan Cenov (#13181)

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11391 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-02-15 06:23:34 +00:00
Jean-Philippe Lang d094dea0ec Merged r11366 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11367 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-02-12 18:08:38 +00:00
Jean-Philippe Lang 3a98a14250 Merged r11350 from trunk (#13126).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11365 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-02-12 18:04:49 +00:00
Jean-Philippe Lang ecef96a6b9 Merged r11332 from trunk (#13097).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11364 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-02-12 18:03:16 +00:00
Jean-Philippe Lang 0951522247 Merged r11331 and r11333 from trunk (#13075).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11363 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-02-12 18:01:15 +00:00
Jean-Philippe Lang 42cc2d322a Merged r11281 from trunk (#12979).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11362 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-02-12 17:57:46 +00:00
Jean-Philippe Lang 0367c323de Merged r11258 from trunk (#12930).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11361 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-02-12 17:56:39 +00:00
Toshi MARUYAMA 80be82ae50 Merged r11353 from trunk to 2.2-stable
upgrade Rails to 3.2.12.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11358 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-02-12 06:28:11 +00:00
Toshi MARUYAMA ebed927de5 2.2-stable: svn propset svn:eol-style native SVG source files (#12971)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11357 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-02-12 03:52:41 +00:00
Toshi MARUYAMA cbd069118a 2.2-stable: backout r11355 (back to Rails 3.2.11)
jruby-1.6.7 tests fail on CI server.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11356 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-02-12 02:49:25 +00:00
Toshi MARUYAMA a211d1be7d Merged r11353 from trunk to 2.2-stable
upgrade Rails to 3.2.12.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11355 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-02-12 01:46:21 +00:00
Toshi MARUYAMA 0a8e0a3bed Merged r11306 from trunk to 2.2-stable (#11987)
pdf: fix broken new line in table.

Contributed by Jun NAITOH.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11308 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-02-03 06:53:18 +00:00
Toshi MARUYAMA 58cf34dea5 Merged r11263 from trunk to 2.2-stable (#12987)
Russian translation for 2.2-stable updated.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11266 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-26 00:40:21 +00:00
Toshi MARUYAMA 594a7bde95 Merged r11246 from trunk to 2.2-stable (#12926, #12928)
Bulgarian translation for 2.2-stable updated by Ivan Cenov.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11251 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-21 13:54:49 +00:00
Toshi MARUYAMA c5e257d82b Merged r11243 from trunk to 2.2-stable (#12922)
Spanish translation for 2.2-stable updated by Jorge López.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11245 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-21 11:50:27 +00:00
Jean-Philippe Lang 56b12b289f Merged r11239 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11240 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-20 18:24:43 +00:00
Jean-Philippe Lang c99eef1aff Merged r11228 from trunk (#12851).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11236 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-20 17:09:36 +00:00
Jean-Philippe Lang ed891e2733 Merged r11227 from trunk (#9842).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11235 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-20 17:06:46 +00:00
Jean-Philippe Lang 4620b8b8db Backported r11233 from trunk (#12833).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11234 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-20 17:03:40 +00:00
Jean-Philippe Lang 98eb2edd25 Merged r11225 from trunk (#12838).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11232 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-20 17:01:03 +00:00
Jean-Philippe Lang ed7318fb8d Backported r11157 and r11158 from trunk (#12801).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11231 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-20 16:59:42 +00:00
Jean-Philippe Lang 5a1a2f5855 Merged r11159 from trunk (#12799).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11230 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-20 16:56:50 +00:00
Jean-Philippe Lang f1314278d5 Backported r11196 from trunk (#7510).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11229 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-20 16:55:37 +00:00
Toshi MARUYAMA 3a8b872a09 Merged r11170 from trunk to 2.2-stable (#12818)
Swedish translation updated by Nicklas Holm.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11172 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-12 12:43:51 +00:00
Toshi MARUYAMA 56290979fc Merged r11163 from trunk to 2.2-stable (#12809)
Swedish translation updated by Nicklas Holm.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11165 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-11 13:19:18 +00:00
Toshi MARUYAMA 6e0fb415a7 Merged r11160 from trunk to 2.2-stable (#12800)
Serbian translation updated by Miodrag Milic.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11162 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-11 08:31:14 +00:00
Jean-Philippe Lang f26654b29f Merged r11146 from trunk: Changes for 2.2.1 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11147 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-09 13:33:24 +00:00
Jean-Philippe Lang 5c62c1cf95 Merged r11126 from trunk (#12744).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11145 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-09 13:31:05 +00:00
Jean-Philippe Lang 2d6adbd7ff Merged r11088 from trunk (#12691).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11144 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-09 13:29:59 +00:00
Jean-Philippe Lang b0ccaffe1b Merged r11064 from trunk (#12652).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11143 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-09 13:29:08 +00:00
Toshi MARUYAMA 2c37617973 Merged r11140 from trunk to 2.2-stable.
Upgrade to Rails 3.2.11.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11142 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-08 23:57:13 +00:00
Toshi MARUYAMA 567eb70fdb Merged r11128 from trunk to 2.2-stable.
Test fails when run at the beginning of the day.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11141 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-08 23:14:03 +00:00
Toshi MARUYAMA de63102925 Merged r11109 from trunk to 2.2-stable
Upgrade to Rails 3.2.10.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11111 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-04 02:05:44 +00:00
Toshi MARUYAMA a8fcf9389e Merged r11091 from trunk (#12711, #11290)
fix incorrect min_x_value of lib/SVG/Graph/TimeSeries.rb by r10439.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11093 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-03 00:15:32 +00:00
Toshi MARUYAMA 3e4b36de83 Merged r11051 from trunk to 2.2-stable (#12630)
Russian "x_hours" translation updated by Mikhail Velkin.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11054 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-19 00:00:04 +00:00
Jean-Philippe Lang edd584d59e Merged r11041 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11042 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-18 12:24:17 +00:00
Toshi MARUYAMA e2b27ab696 Merged r11036 from trunk to 2.2-stable (#12615, #12619)
Russian translation changed by Kirill Bezrukov.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11040 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-18 00:04:49 +00:00
Toshi MARUYAMA 1968d039ee Merged r11035 from trunk to 2.2-stable (#12615)
Russian translation updated by Kirill Bezrukov.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11037 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-17 23:24:31 +00:00
Toshi MARUYAMA fddc33cd45 Merged r11022 from trunk to 2.2-stable (#12614)
Dutch translation updated.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11023 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-17 12:43:15 +00:00
Toshi MARUYAMA 29518ba0cf Merged r11015 from trunk to 2.2-stable (#12605, #12608)
Norwegian translation changed by Ketil Mehl.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11018 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-16 07:34:34 +00:00
Toshi MARUYAMA acd27b9eb1 Merged r11014 from trunk to 2.2-stable (#12605)
Norwegian translation for 1.4-stable updated by Ketil Mehl.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11017 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-16 07:34:09 +00:00
Toshi MARUYAMA 6445b5997c Merged r11012 from trunk to 2.2-stable (#12607)
Japanese translation for 2.2-stable updated.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11016 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-16 07:33:39 +00:00
Jean-Philippe Lang 416d33973f Merged r10991 from trunk (#12568).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11009 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-15 07:55:50 +00:00
Jean-Philippe Lang 82c7dc11d2 Merged r10992 from trunk (#12400).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11006 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-15 07:52:39 +00:00
Jean-Philippe Lang d22f782d3f Merged r10956 from trunk (#12513).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11004 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-15 07:51:12 +00:00
Jean-Philippe Lang 3717fdfa79 Merged r10975 from trunk (#12566).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11002 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-15 07:49:31 +00:00
Toshi MARUYAMA 9d3d932703 Merged r10999 from trunk (#12602)
Korean translation updated by Jongwook Choi.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@11000 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-15 01:32:43 +00:00
Jean-Philippe Lang 3a379ce4b9 Merged r10948 into 2.2-stable (#3447).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@10952 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-08 08:41:59 +00:00
Toshi MARUYAMA daa77f0109 Merged r10946 from trunk to 2.2-stable (#12514)
Lithuanian translation updated by Egidijus Zideckas.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@10947 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-06 06:30:05 +00:00
Toshi MARUYAMA c0d66d626b Merged r10940 from trunk to 2.2-stable (#12494)
Bulgarian "button_submit" translation changed by Ivan Cenov.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@10942 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-04 05:43:41 +00:00
Jean-Philippe Lang bf02b76ca3 Sets stable branch.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@10908 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-02 18:01:48 +00:00
Jean-Philippe Lang 75e02ca486 branch 2.2-stable added
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.2-stable@10907 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-02 17:58:22 +00:00
994 changed files with 12410 additions and 36511 deletions

No files matched your search

-8
View File
@@ -1,7 +1,5 @@
/.project
/.loadpath
/.powrc
/.rvmrc
/config/additional_environment.rb
/config/configuration.yml
/config/database.yml
@@ -17,14 +15,8 @@
/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/*
-2
View File
@@ -2,8 +2,6 @@ syntax: glob
.project
.loadpath
.powrc
.rvmrc
config/additional_environment.rb
config/configuration.yml
config/database.yml
-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 -48
View File
@@ -1,9 +1,9 @@
source 'https://rubygems.org'
source 'http://rubygems.org'
gem "rails", "3.2.13"
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")
platforms :mri_18, :mingw_18 do
group :mysql do
gem "mysql", "~> 2.8.1"
end
end
platforms :mri_19, :mingw_19 do
group :mysql do
gem "mysql2", "~> 0.3.11"
end
else
warn("No adapter found in config/database.yml, please configure it first")
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,13 @@ 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 JRuby 1.7.
# It seems to need test-unit explicitely.
platforms = [:mri_19]
platforms << :jruby if defined?(JRUBY_VERSION) && JRUBY_VERSION >= "1.7"
gem "test-unit", :platforms => platforms
gem "mocha", "~> 0.13.3"
end
local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
@@ -95,6 +91,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
+29 -76
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
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
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
+23 -48
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,10 +109,6 @@ 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)
@@ -134,14 +124,10 @@ class ApplicationController < ActionController::Base
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 +150,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 +163,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 +188,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"' }
@@ -321,7 +291,7 @@ class ApplicationController < ActionController::Base
# Find issues with a single :id param or :ids array param
# Raises a Unauthorized exception if one of the issues is not visible
def find_issues
@issues = Issue.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?)
@projects = @issues.collect(&:project).compact.uniq
@@ -330,16 +300,6 @@ class ApplicationController < ActionController::Base
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
@@ -578,6 +538,21 @@ class ApplicationController < ActionController::Base
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
+9 -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
@@ -85,17 +85,15 @@ 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
if @attachment.save
respond_to do |format|
format.js
format.api {
if saved
render :action => 'upload', :status => :created
else
render_validation_errors(@attachment)
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
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
+9 -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 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
@@ -42,12 +42,12 @@ class BoardsController < ApplicationController
'updated_on' => "COALESCE(last_replies_messages.created_on, #{Message.table_name}.created_on)"
@topic_count = @board.topics.count
@topic_pages = Paginator.new @topic_count, per_page_option, params['page']
@topic_pages = Paginator.new self, @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).
limit(@topic_pages.items_per_page).
offset(@topic_pages.current.offset).
order(sort_clause).
preload(:author, {:last_reply => :author}).
all
@@ -55,11 +55,9 @@ class BoardsController < ApplicationController
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 +98,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
+8 -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
@@ -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 }
@custom_fields_by_type = CustomField.find(:all).group_by {|f| f.class.name }
@tab = params[:tab] || 'IssueCustomField'
}
format.api {
@custom_fields = CustomField.all
}
end
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
redirect_to :action => 'index', :tab => @custom_field.class.name
rescue
flash[:error] = l(:error_can_not_delete_custom_field)
end
redirect_to custom_fields_path(:tab => @custom_field.class.name)
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
+9 -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
@@ -46,7 +46,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'
else
render :action => 'new'
end
@@ -58,7 +58,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'
else
render :action => 'edit'
end
@@ -68,14 +68,16 @@ class EnumerationsController < ApplicationController
if !@enumeration.in_use?
# No associated objects
@enumeration.destroy
redirect_to enumerations_path
redirect_to :action => 'index'
return
elsif params[:reassign_to_id].present? && (reassign_to = @enumeration.class.find_by_id(params[:reassign_to_id].to_i))
elsif params[:reassign_to_id]
if reassign_to = @enumeration.class.find_by_id(params[:reassign_to_id])
@enumeration.destroy(reassign_to)
redirect_to enumerations_path
redirect_to :action => 'index'
return
end
@enumerations = @enumeration.class.system.all - [@enumeration]
end
@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
+30 -76
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
@@ -71,8 +71,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,
@@ -85,8 +85,8 @@ class IssuesController < ApplicationController
Issue.load_visible_relations(@issues) if include_in_api_response?('relations')
}
format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
format.csv { send_data(query_to_csv(@issues, @query, params), :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|
@@ -103,9 +103,6 @@ class IssuesController < ApplicationController
@journals = @issue.journals.includes(:user, :details).reorder("#{Journal.table_name}.id ASC").all
@journals.each_with_index {|j,i| j.indice = i+1}
@journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
Journal.preload_journals_details_custom_fields(@journals)
# TODO: use #select! when ruby1.8 support is dropped
@journals.reject! {|journal| !journal.notes? && journal.visible_details.empty?}
@journals.reverse! if User.current.wants_comments_in_reverse_order?
@changesets = @issue.changesets.visible.all
@@ -116,8 +113,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
@@ -137,6 +132,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 +145,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,7 +173,7 @@ 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]
@@ -195,7 +187,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 +198,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 +220,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 +231,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 +239,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 +248,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)
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
redirect_back_or_default _project_issues_path(@project)
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,7 +308,7 @@ 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
@@ -452,26 +428,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
+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
@@ -80,7 +80,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
+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
+8 -10
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'
+14 -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
@@ -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
@@ -160,7 +157,7 @@ class MyController < ApplicationController
@user.pref[:my_page_layout] = layout
@user.pref.save
end
redirect_to my_page_layout_path
redirect_to :action => 'page_layout'
end
# Remove a block to user's page
@@ -173,7 +170,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 -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
@@ -16,11 +16,12 @@
# 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
@@ -36,6 +37,7 @@ class PreviewsController < ApplicationController
def news
if params[:id].present? && news = News.visible.find_by_id(params[:id])
@previewed = news
@attachments = news.attachments
end
@text = (params[:news] ? params[:news][:description] : nil)
render :partial => 'common/preview'
@@ -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
+28 -26
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,11 +104,15 @@ 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)
@@ -122,13 +124,13 @@ 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
@@ -139,14 +141,14 @@ class ProjectsController < ApplicationController
# Show @project
def show
if params[:jump]
# try to redirect to the requested menu item
if params[:jump] && redirect_to_project_menu_item(@project, params[:jump])
return
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 +157,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 +169,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 +186,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 +204,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 +213,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 +237,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,
+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
@@ -26,7 +26,7 @@ class RolesController < ApplicationController
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 {
@@ -58,7 +58,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 +71,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 +79,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 +94,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
+6 -14
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,10 +31,12 @@ 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]}
@@ -46,24 +46,16 @@ class SettingsController < ApplicationController
@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],
+92 -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,51 @@ 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 'last_2_weeks'
@from = Date.today - 14 - (Date.today.cwday - 1)%7
@to = @from + 13
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
scope
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
@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
+15 -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
@@ -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
+20 -34
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|
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
+29 -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
@@ -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
#
@@ -35,7 +37,6 @@ class WikiController < ApplicationController
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]
helper :attachments
include AttachmentsHelper
@@ -62,12 +63,7 @@ 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 @page.new_record?
if User.current.allowed_to?(:edit_wiki_pages, @project) && editable? && !api_request?
edit
render :action => 'edit'
@@ -76,6 +72,11 @@ class WikiController < ApplicationController
end
return
end
if params[:version] && !User.current.allowed_to?(:view_wiki_edits, @project)
deny_access
return
end
@content = @page.content_for_version(params[:version])
if User.current.allowed_to?(:export_wiki_pages, @project)
if params[:format] == 'pdf'
send_data(wiki_page_to_pdf(@page, @project), :type => 'application/pdf', :filename => "#{@page.title}.pdf")
@@ -104,19 +105,19 @@ class WikiController < ApplicationController
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?
@@ -130,9 +131,10 @@ class WikiController < ApplicationController
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 = @page.content
content_params = params[:content]
if content_params.nil? && params[:wiki_page].is_a?(Hash)
content_params = params[:wiki_page].slice(:text, :comments, :version)
@@ -144,26 +146,23 @@ class WikiController < ApplicationController
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.text = @text
end
@content.author = User.current
if @page.save_with_content(@content)
if @page.save_with_content
attachments = Attachment.attach_files(@page, params[:attachments])
render_attachment_warning_if_needed(@page)
call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
respond_to do |format|
format.html {
anchor = @section ? "section-#{@section}" : nil
redirect_to project_wiki_page_path(@project, @page.title, :anchor => anchor)
}
format.html { redirect_to :action => 'show', :project_id => @project, :id => @page.title }
format.api {
if was_new_page
render :action => 'show', :status => :created, :location => project_wiki_page_path(@project, @page.title)
render :action => 'show', :status => :created, :location => url_for(:controller => 'wiki', :action => 'show', :project_id => @project, :id => @page.title)
else
render_api_ok
end
@@ -200,26 +199,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['page']
# 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
@@ -262,7 +260,7 @@ class WikiController < ApplicationController
end
@page.destroy
respond_to do |format|
format.html { redirect_to project_wiki_index_path(@project) }
format.html { redirect_to :action => 'index', :project_id => @project }
format.api { render_api_ok }
end
end
@@ -272,7 +270,7 @@ class WikiController < ApplicationController
@content = @page.content_for_version(params[:version])
@content.destroy
redirect_to_referer_or history_project_wiki_page_path(@project, @page.title)
redirect_to_referer_or :action => 'history', :id => @page.title, :project_id => @project
end
# Export wiki to a single pdf or html file
@@ -294,7 +292,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 +349,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.order("#{WikiPage.table_name}.title").includes(:wiki => :project).includes(:parent).all
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
+100 -87
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
@@ -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,29 +134,16 @@ 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
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
link_to(h(project), url, html_options)
end
end
@@ -160,8 +152,8 @@ module ApplicationHelper
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
@@ -316,8 +308,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 +322,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?
@@ -343,15 +335,11 @@ module ApplicationHelper
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)}
{:disabled => principal.projects.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 +372,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 +391,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 +490,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 +510,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.
@@ -587,7 +604,8 @@ module ApplicationHelper
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 +634,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
@@ -680,9 +698,6 @@ 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
@@ -719,7 +734,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
@@ -786,14 +801,14 @@ module ApplicationHelper
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),
@@ -807,10 +822,11 @@ module ApplicationHelper
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')
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 +847,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
@@ -1063,8 +1078,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 +1111,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 +1118,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 +1227,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 +1236,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
+9 -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,8 @@
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
CustomField::CUSTOM_FIELDS_TABS
end
# Return custom field html tag corresponding to its format
@@ -98,44 +77,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
+1 -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
@@ -24,19 +24,4 @@ module GroupsHelper
{: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
+66 -81
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
@@ -94,20 +94,6 @@ module IssuesHelper
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 = {
@@ -160,13 +146,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 +184,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',
content_tag('h3', h(title)) +
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"
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 +222,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 +281,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 +290,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 +301,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 +337,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,9 +347,6 @@ 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)
@@ -425,4 +370,44 @@ 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_inline_columns : query.inline_columns)
if options[:description]
if description = query.available_columns.detect {|q| q.name == :description}
columns << description
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
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) }
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
+45 -96
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,35 +24,32 @@ module QueriesHelper
def filters_options(query)
options = [[]]
options += query.available_filters.map do |field, field_options|
sorted_options = query.available_filters.sort do |a, b|
ord = 0
if !(a[1][:order] == 20 && b[1][:order] == 20)
ord = a[1][:order] <=> b[1][:order]
else
cn = (CustomField::CUSTOM_FIELDS_NAMES.index(a[1][:field].class.name) <=>
CustomField::CUSTOM_FIELDS_NAMES.index(b[1][:field].class.name))
if cn != 0
ord = cn
else
f = (a[1][:field] <=> b[1][:field])
if f != 0
ord = f
else
# assigned_to or author
ord = (a[0] <=> b[0])
end
end
end
ord
end
options += sorted_options.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|
@@ -61,19 +58,6 @@ module QueriesHelper
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}
end
def column_header(column)
column.sortable ? sort_header_tag(column.name.to_s, :caption => column.caption,
:default_order => column.default_order) :
@@ -104,9 +88,7 @@ module QueriesHelper
when 'Date'
format_date(value)
when 'Fixnum'
if column.name == :id
link_to value, issue_path(issue)
elsif column.name == :done_ratio
if column.name == :done_ratio
progress_bar(value, :width => '80px')
else
value.to_s
@@ -124,7 +106,7 @@ module QueriesHelper
when 'FalseClass'
l(:general_text_No)
when 'Issue'
value.visible? ? link_to_issue(value) : "##{value.id}"
link_to_issue(value, :subject => false)
when 'IssueRelation'
other = value.other_issue(issue)
content_tag('span',
@@ -135,72 +117,26 @@ module QueriesHelper
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 +144,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 +157,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
+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
@@ -43,7 +43,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
+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
@@ -53,7 +53,7 @@ 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')
+2 -3
View File
@@ -80,13 +80,12 @@ 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
+46 -3
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
@@ -86,6 +86,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 +150,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
+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
+6 -3
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,9 +35,12 @@ 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
+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
+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 -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
+11 -50
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
end
def deletable?(user=User.current)
if container_id
container && container.attachments_deletable?(user)
else
author == user
end
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
+21 -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
@@ -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,28 +120,25 @@ 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)
fix_issue(issue) if fix_keywords.include?(action.to_s.downcase)
log_time(issue, hours) if hours && Setting.commit_logtime_enabled?
end
end
end
end
referenced_issues.uniq!
self.issues = referenced_issues unless referenced_issues.empty?
@@ -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]
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
end
end
sorted_events
end
end
+39 -101
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,35 +26,32 @@ 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
}
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 visible_by?(project, user=User.current)
visible? || user.admin?
end
CUSTOM_FIELDS_NAMES = CUSTOM_FIELDS_TABS.collect{|v| v[:name]}
def field_format=(arg)
# cannot change format of a saved custom field
@@ -123,10 +119,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
@@ -184,12 +178,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 +205,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 +221,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 +241,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 +257,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 +323,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 994 files, more files were not shown because too many files have changed in this diff. Show more