Compare commits

..
82 Commits
Author SHA1 Message Date
Jean-Philippe Lang 8cdd427a0e Changes for 2.1.6 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@11152 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-09 14:03:28 +00:00
Jean-Philippe Lang de2b7c2879 Upgrade to Rails 3.2.11.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@11151 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-09 14:01:43 +00:00
Jean-Philippe Lang f0aad8c2a8 Backported r10842 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@11150 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-09 14:01:27 +00:00
Jean-Philippe Lang 68b923640e Set version to 2.1.5
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@11046 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-18 12:56:45 +00:00
Jean-Philippe Lang 4e2fbf440b Changelog for 2.1.5 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@11043 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-18 12:24:45 +00:00
Toshi MARUYAMA 5572b6c715 Merged r11035 from trunk to 2.1-stable (#12615)
Russian translation updated by Kirill Bezrukov.

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

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@11024 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-17 12:44:13 +00:00
Toshi MARUYAMA dff772b066 Merged r11014 from trunk to 2.1-stable (#12605)
Norwegian translation for 1.4-stable updated by Ketil Mehl.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@11019 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-16 07:39:07 +00:00
Jean-Philippe Lang fb2cbdc6cc Merged r10991 from trunk (#12568).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@11008 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-15 07:55:41 +00:00
Jean-Philippe Lang 6062a0a89d Merged r10885 from trunk (#12451).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@11007 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-15 07:54:21 +00:00
Jean-Philippe Lang 0ebf95d819 Merged r10992 from trunk (#12400).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@11005 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-15 07:52:31 +00:00
Jean-Philippe Lang 14879e24c4 Merged r10956 from trunk (#12513).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@11003 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-15 07:50:45 +00:00
Jean-Philippe Lang ca0b289479 Merged r10975 from trunk (#12566).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@11001 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-12-15 07:48:42 +00:00
Jean-Philippe Lang e36a611f81 Reverts r10895 (#12472).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10897 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-30 10:25:22 +00:00
Jean-Philippe Lang 6c2eec59d8 Merged r10893 into 2.1-stable (#12472).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10895 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-30 08:58:26 +00:00
Jean-Philippe Lang 905ed3aa75 Updates for 2.1.4 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10875 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-24 09:35:08 +00:00
Jean-Philippe Lang bb4c530ba2 Merged r10865 and r10866 from trunk (#12431).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10873 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-23 22:31:02 +00:00
Jean-Philippe Lang e3958ef577 Merged r10838 from trunk (#12274).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10872 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-23 22:29:45 +00:00
Jean-Philippe Lang c5f2dadb2c Merged r10836 from trunk (#12298).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10871 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-23 22:28:37 +00:00
Jean-Philippe Lang af8dcccb7b Merged r10837 from trunk (#12332).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10870 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-23 22:25:40 +00:00
Toshi MARUYAMA c0742ed9e6 Merged r10862 from trunk to 2.1-stable (#12409)
scm: git: change extra_report_last_commit type at unit test to string.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10863 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-22 01:10:50 +00:00
Jean-Philippe Lang 8c7ae20402 Merged r10850 from trunk (#12396).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10861 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-21 20:44:19 +00:00
Toshi MARUYAMA 06b68f1948 Merged r10856 and r10857 from trunk to 2.1-stable (#12409)
scm: git: fix changesets aren't read after clear_changesets call.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10858 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-21 09:55:48 +00:00
Toshi MARUYAMA b51203bc83 Merged r10853 from trunk (#12399)
svn propset svn:eol-style native to fixtures.


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10855 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-11-21 07:19:57 +00:00
Toshi MARUYAMA f578a5c4f6 Merged r10852 from trunk (#12399)
fix non ASCII attachment filename encoding broken (MOJIBAKE) in receiving mail on Ruby 1.8.

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Array indexOf() is not defined on IE8.

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

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

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

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

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

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

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

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

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10380 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-15 12:02:36 +00:00
Jean-Philippe Lang 9939fbeef6 Merged r10375 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10376 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-12 19:16:31 +00:00
Jean-Philippe Lang b369f82d15 Merged r10368 to r10373 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10374 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 19:40:59 +00:00
Jean-Philippe Lang 0ad5dfaba0 Set branch to stable.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10367 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 16:33:49 +00:00
Jean-Philippe Lang d7d18689b7 Added 2.1-stable branch.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10366 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 16:30:57 +00:00
293 changed files with 5333 additions and 9930 deletions
-1
View File
@@ -23,7 +23,6 @@
/tmp/sessions/* /tmp/sessions/*
/tmp/sockets/* /tmp/sockets/*
/tmp/test/* /tmp/test/*
/tmp/thumbnails/*
/vendor/cache /vendor/cache
/vendor/rails /vendor/rails
*.rbc *.rbc
-1
View File
@@ -25,7 +25,6 @@ tmp/pdf/*
tmp/sessions/* tmp/sessions/*
tmp/sockets/* tmp/sockets/*
tmp/test/* tmp/test/*
tmp/thumbnails/*
vendor/cache vendor/cache
vendor/rails vendor/rails
*.rbc *.rbc
+4 -7
View File
@@ -1,6 +1,6 @@
source 'http://rubygems.org' source 'http://rubygems.org'
gem "rails", "3.2.13" gem 'rails', '3.2.11'
gem "jquery-rails", "~> 2.0.2" gem "jquery-rails", "~> 2.0.2"
gem "i18n", "~> 0.6.0" gem "i18n", "~> 0.6.0"
gem "coderay", "~> 1.0.6" gem "coderay", "~> 1.0.6"
@@ -74,12 +74,9 @@ end
group :test do group :test do
gem "shoulda", "~> 2.11" gem "shoulda", "~> 2.11"
# Shoulda does not work nice on Ruby 1.9.3 and JRuby 1.7. # Shoulda does not work nice on Ruby 1.9.3 and seems to need test-unit explicitely.
# It seems to need test-unit explicitely. gem "test-unit", :platforms => [:mri_19]
platforms = [:mri_19] gem "mocha", "0.12.3"
platforms << :jruby if defined?(JRUBY_VERSION) && JRUBY_VERSION >= "1.7"
gem "test-unit", :platforms => platforms
gem "mocha", "~> 0.13.3"
end end
local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local") local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
+8 -38
View File
@@ -110,16 +110,6 @@ class ApplicationController < ActionController::Base
user = User.try_to_login(username, password) || User.find_by_api_key(username) user = User.try_to_login(username, password) || User.find_by_api_key(username)
end end
end end
# Switch user if requested by an admin user
if user && user.admin? && (username = api_switch_user_from_request)
su = User.find_by_login(username)
if su && su.active?
logger.info(" User switched by: #{user.login} (id=#{user.id})") if logger
user = su
else
render_error :message => 'Invalid X-Redmine-Switch-User header', :status => 412
end
end
end end
user user
end end
@@ -276,24 +266,14 @@ class ApplicationController < ActionController::Base
self.model_object = model self.model_object = model
end end
# Find the issue whose id is the :id parameter # Filter for bulk issue operations
# Raises a Unauthorized exception if the issue is not visible
def find_issue
# Issue.visible.find(...) can not be used to redirect user to the login form
# if the issue actually exists but requires authentication
@issue = Issue.find(params[:id])
raise Unauthorized unless @issue.visible?
@project = @issue.project
rescue ActiveRecord::RecordNotFound
render_404
end
# Find issues with a single :id param or :ids array param
# Raises a Unauthorized exception if one of the issues is not visible
def find_issues def find_issues
@issues = Issue.find_all_by_id(params[:id] || params[:ids]) @issues = Issue.find_all_by_id(params[:id] || params[:ids])
raise ActiveRecord::RecordNotFound if @issues.empty? raise ActiveRecord::RecordNotFound if @issues.empty?
raise Unauthorized unless @issues.all?(&:visible?) if @issues.detect {|issue| !issue.visible?}
deny_access
return
end
@projects = @issues.collect(&:project).compact.uniq @projects = @issues.collect(&:project).compact.uniq
@project = @projects.first if @projects.size == 1 @project = @projects.first if @projects.size == 1
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
@@ -422,7 +402,7 @@ class ApplicationController < ActionController::Base
@items.sort! {|x,y| y.event_datetime <=> x.event_datetime } @items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
@items = @items.slice(0, Setting.feeds_limit.to_i) @items = @items.slice(0, Setting.feeds_limit.to_i)
@title = options[:title] || Setting.app_title @title = options[:title] || Setting.app_title
render :template => "common/feed", :formats => [:atom], :layout => false, render :template => "common/feed.atom", :layout => false,
:content_type => 'application/atom+xml' :content_type => 'application/atom+xml'
end end
@@ -528,11 +508,6 @@ class ApplicationController < ActionController::Base
end end
end end
# Returns the API 'switch user' value if present
def api_switch_user_from_request
request.headers["X-Redmine-Switch-User"].to_s.presence
end
# Renders a warning flash if obj has unsaved attachments # Renders a warning flash if obj has unsaved attachments
def render_attachment_warning_if_needed(obj) def render_attachment_warning_if_needed(obj)
flash[:warning] = l(:warning_attachments_not_saved, obj.unsaved_attachments.size) if obj.unsaved_attachments.present? flash[:warning] = l(:warning_attachments_not_saved, obj.unsaved_attachments.size) if obj.unsaved_attachments.present?
@@ -563,13 +538,8 @@ class ApplicationController < ActionController::Base
# Renders a 200 response for successfull updates or deletions via the API # Renders a 200 response for successfull updates or deletions via the API
def render_api_ok def render_api_ok
render_api_head :ok # head :ok would return a response body with one space
end render :text => '', :status => :ok, :layout => nil
# Renders a head API response
def render_api_head(status)
# #head would return a response body with one space
render :text => '', :status => status, :layout => nil
end end
# Renders API response on validation failure # Renders API response on validation failure
+1 -1
View File
@@ -84,7 +84,7 @@ class AttachmentsController < ApplicationController
@attachment = Attachment.new(:file => request.raw_post) @attachment = Attachment.new(:file => request.raw_post)
@attachment.author = User.current @attachment.author = User.current
@attachment.filename = params[:filename].presence || Redmine::Utils.random_hex(16) @attachment.filename = Redmine::Utils.random_hex(16)
if @attachment.save if @attachment.save
respond_to do |format| respond_to do |format|
+5 -9
View File
@@ -39,18 +39,14 @@ class BoardsController < ApplicationController
sort_init 'updated_on', 'desc' sort_init 'updated_on', 'desc'
sort_update 'created_on' => "#{Message.table_name}.created_on", sort_update 'created_on' => "#{Message.table_name}.created_on",
'replies' => "#{Message.table_name}.replies_count", 'replies' => "#{Message.table_name}.replies_count",
'updated_on' => "COALESCE(last_replies_messages.created_on, #{Message.table_name}.created_on)" 'updated_on' => "#{Message.table_name}.updated_on"
@topic_count = @board.topics.count @topic_count = @board.topics.count
@topic_pages = Paginator.new self, @topic_count, per_page_option, params['page'] @topic_pages = Paginator.new self, @topic_count, per_page_option, params['page']
@topics = @board.topics. @topics = @board.topics.reorder("#{Message.table_name}.sticky DESC").order(sort_clause).all(
reorder("#{Message.table_name}.sticky DESC"). :include => [:author, {:last_reply => :author}],
includes(:last_reply). :limit => @topic_pages.items_per_page,
limit(@topic_pages.items_per_page). :offset => @topic_pages.current.offset)
offset(@topic_pages.current.offset).
order(sort_clause).
preload(:author, {:last_reply => :author}).
all
@message = Message.new(:board => @board) @message = Message.new(:board => @board)
render :action => 'show', :layout => !request.xhr? render :action => 'show', :layout => !request.xhr?
} }
+3 -16
View File
@@ -18,26 +18,13 @@
class EnumerationsController < ApplicationController class EnumerationsController < ApplicationController
layout 'admin' layout 'admin'
before_filter :require_admin, :except => :index before_filter :require_admin
before_filter :require_admin_or_api_request, :only => :index
before_filter :build_new_enumeration, :only => [:new, :create] before_filter :build_new_enumeration, :only => [:new, :create]
before_filter :find_enumeration, :only => [:edit, :update, :destroy] before_filter :find_enumeration, :only => [:edit, :update, :destroy]
accept_api_auth :index
helper :custom_fields helper :custom_fields
def index def index
respond_to do |format|
format.html
format.api {
@klass = Enumeration.get_subclass(params[:type])
if @klass
@enumerations = @klass.shared.sorted.all
else
render_404
end
}
end
end end
def new def new
@@ -46,7 +33,7 @@ class EnumerationsController < ApplicationController
def create def create
if request.post? && @enumeration.save if request.post? && @enumeration.save
flash[:notice] = l(:notice_successful_create) flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'index' redirect_to :action => 'index', :type => @enumeration.type
else else
render :action => 'new' render :action => 'new'
end end
@@ -58,7 +45,7 @@ class EnumerationsController < ApplicationController
def update def update
if request.put? && @enumeration.update_attributes(params[:enumeration]) if request.put? && @enumeration.update_attributes(params[:enumeration])
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'index' redirect_to :action => 'index', :type => @enumeration.type
else else
render :action => 'edit' render :action => 'edit'
end end
+19 -12
View File
@@ -56,7 +56,6 @@ class IssuesController < ApplicationController
retrieve_query retrieve_query
sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria) sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
sort_update(@query.sortable_columns) sort_update(@query.sortable_columns)
@query.sort_criteria = sort_criteria.to_a
if @query.valid? if @query.valid?
case params[:format] case params[:format]
@@ -82,7 +81,7 @@ class IssuesController < ApplicationController
respond_to do |format| respond_to do |format|
format.html { render :template => 'issues/index', :layout => !request.xhr? } format.html { render :template => 'issues/index', :layout => !request.xhr? }
format.api { format.api {
Issue.load_visible_relations(@issues) if include_in_api_response?('relations') Issue.load_relations(@issues) if include_in_api_response?('relations')
} }
format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") } format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
format.csv { send_data(issues_to_csv(@issues, @project, @query, params), :type => 'text/csv; header=present', :filename => 'export.csv') } format.csv { send_data(issues_to_csv(@issues, @project, @query, params), :type => 'text/csv; header=present', :filename => 'export.csv') }
@@ -100,9 +99,8 @@ class IssuesController < ApplicationController
end end
def show def show
@journals = @issue.journals.includes(:user, :details).reorder("#{Journal.table_name}.id ASC").all @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
@journals.each_with_index {|j,i| j.indice = i+1} @journals.each_with_index {|j,i| j.indice = i+1}
@journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
@journals.reverse! if User.current.wants_comments_in_reverse_order? @journals.reverse! if User.current.wants_comments_in_reverse_order?
@changesets = @issue.changesets.visible.all @changesets = @issue.changesets.visible.all
@@ -120,10 +118,7 @@ class IssuesController < ApplicationController
} }
format.api format.api
format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' } format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
format.pdf { format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
pdf = issue_to_pdf(@issue, :journals => @journals)
send_data(pdf, :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf")
}
end end
end end
@@ -178,7 +173,6 @@ class IssuesController < ApplicationController
@conflict = true @conflict = true
if params[:last_journal_id] if params[:last_journal_id]
@conflict_journals = @issue.journals_after(params[:last_journal_id]).all @conflict_journals = @issue.journals_after(params[:last_journal_id]).all
@conflict_journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
end end
end end
@@ -313,7 +307,19 @@ class IssuesController < ApplicationController
end end
end end
private private
def find_issue
# Issue.visible.find(...) can not be used to redirect user to the login form
# if the issue actually exists but requires authentication
@issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
unless @issue.visible?
deny_access
return
end
@project = @issue.project
rescue ActiveRecord::RecordNotFound
render_404
end
def find_project def find_project
project_id = params[:project_id] || (params[:issue] && params[:issue][:project_id]) project_id = params[:project_id] || (params[:issue] && params[:issue][:project_id])
@@ -348,7 +354,8 @@ class IssuesController < ApplicationController
@time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project) @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
@time_entry.attributes = params[:time_entry] @time_entry.attributes = params[:time_entry]
@issue.init_journal(User.current) @notes = params[:notes] || (params[:issue].present? ? params[:issue][:notes] : nil)
@issue.init_journal(User.current, @notes)
issue_attributes = params[:issue] issue_attributes = params[:issue]
if issue_attributes && params[:conflict_resolution] if issue_attributes && params[:conflict_resolution]
@@ -357,7 +364,7 @@ class IssuesController < ApplicationController
issue_attributes = issue_attributes.dup issue_attributes = issue_attributes.dup
issue_attributes.delete(:lock_version) issue_attributes.delete(:lock_version)
when 'add_notes' when 'add_notes'
issue_attributes = issue_attributes.slice(:notes) issue_attributes = {}
when 'cancel' when 'cancel'
redirect_to issue_path(@issue) redirect_to issue_path(@issue)
return false return false
+13 -7
View File
@@ -57,10 +57,10 @@ class JournalsController < ApplicationController
end end
def new def new
@journal = Journal.visible.find(params[:journal_id]) if params[:journal_id] journal = Journal.find(params[:journal_id]) if params[:journal_id]
if @journal if journal
user = @journal.user user = journal.user
text = @journal.notes text = journal.notes
else else
user = @issue.author user = @issue.author
text = @issue.description text = @issue.description
@@ -69,8 +69,6 @@ class JournalsController < ApplicationController
text = text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]') text = text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]')
@content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> " @content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> "
@content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n" @content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
rescue ActiveRecord::RecordNotFound
render_404
end end
def edit def edit
@@ -97,9 +95,17 @@ class JournalsController < ApplicationController
private private
def find_journal def find_journal
@journal = Journal.visible.find(params[:id]) @journal = Journal.find(params[:id])
@project = @journal.journalized.project @project = @journal.journalized.project
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render_404 render_404
end end
# TODO: duplicated in IssuesController
def find_issue
@issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
@project = @issue.project
rescue ActiveRecord::RecordNotFound
render_404
end
end end
+1 -2
View File
@@ -123,7 +123,7 @@ class MessagesController < ApplicationController
private private
def find_message def find_message
return unless find_board find_board
@message = @board.messages.find(params[:id], :include => :parent) @message = @board.messages.find(params[:id], :include => :parent)
@topic = @message.root @topic = @message.root
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
@@ -135,6 +135,5 @@ private
@project = @board.project @project = @board.project
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render_404 render_404
nil
end end
end end
+9 -10
View File
@@ -147,16 +147,15 @@ class MyController < ApplicationController
# params[:block] : id of the block to add # params[:block] : id of the block to add
def add_block def add_block
block = params[:block].to_s.underscore block = params[:block].to_s.underscore
if block.present? && BLOCKS.key?(block) (render :nothing => true; return) unless block && (BLOCKS.keys.include? block)
@user = User.current @user = User.current
layout = @user.pref[:my_page_layout] || {} layout = @user.pref[:my_page_layout] || {}
# remove if already present in a group # remove if already present in a group
%w(top left right).each {|f| (layout[f] ||= []).delete block } %w(top left right).each {|f| (layout[f] ||= []).delete block }
# add it on top # add it on top
layout['top'].unshift block layout['top'].unshift block
@user.pref[:my_page_layout] = layout @user.pref[:my_page_layout] = layout
@user.pref.save @user.pref.save
end
redirect_to :action => 'page_layout' redirect_to :action => 'page_layout'
end end
-4
View File
@@ -35,10 +35,6 @@ class PreviewsController < ApplicationController
end end
def news 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) @text = (params[:news] ? params[:news][:description] : nil)
render :partial => 'common/preview' render :partial => 'common/preview'
end end
+6 -3
View File
@@ -116,7 +116,11 @@ class ProjectsController < ApplicationController
@source_project = Project.find(params[:id]) @source_project = Project.find(params[:id])
if request.get? if request.get?
@project = Project.copy_from(@source_project) @project = Project.copy_from(@source_project)
@project.identifier = Project.next_identifier if Setting.sequential_project_identifiers? if @project
@project.identifier = Project.next_identifier if Setting.sequential_project_identifiers?
else
redirect_to :controller => 'admin', :action => 'projects'
end
else else
Mailer.with_deliveries(params[:notifications] == '1') do Mailer.with_deliveries(params[:notifications] == '1') do
@project = Project.new @project = Project.new
@@ -135,8 +139,7 @@ class ProjectsController < ApplicationController
end end
end end
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
# source_project not found redirect_to :controller => 'admin', :action => 'projects'
render_404
end end
# Show @project # Show @project
+4 -10
View File
@@ -18,10 +18,10 @@
class RolesController < ApplicationController class RolesController < ApplicationController
layout 'admin' layout 'admin'
before_filter :require_admin, :except => [:index, :show] before_filter :require_admin, :except => :index
before_filter :require_admin_or_api_request, :only => [:index, :show] before_filter :require_admin_or_api_request, :only => :index
before_filter :find_role, :only => [:show, :edit, :update, :destroy] before_filter :find_role, :only => [:edit, :update, :destroy]
accept_api_auth :index, :show accept_api_auth :index
def index def index
respond_to do |format| respond_to do |format|
@@ -35,12 +35,6 @@ class RolesController < ApplicationController
end end
end end
def show
respond_to do |format|
format.api
end
end
def new def new
# Prefills the form with 'Non member' role permissions by default # Prefills the form with 'Non member' role permissions by default
@role = Role.new(params[:role] || {:permissions => Role.non_member.permissions}) @role = Role.new(params[:role] || {:permissions => Role.non_member.permissions})
+1 -2
View File
@@ -39,8 +39,7 @@ class SettingsController < ApplicationController
redirect_to :action => 'edit', :tab => params[:tab] redirect_to :action => 'edit', :tab => params[:tab]
else else
@options = {} @options = {}
user_format = User::USER_FORMATS.collect{|key, value| [key, value[:setting_order]]}.sort{|a, b| a[1] <=> b[1]} @options[:user_format] = User::USER_FORMATS.keys.collect {|f| [User.current.name(f), f.to_s] }
@options[:user_format] = user_format.collect{|f| [User.current.name(f[0]), f[0].to_s]}
@deliveries = ActionMailer::Base.perform_deliveries @deliveries = ActionMailer::Base.perform_deliveries
@guessed_host_and_path = request.host_with_port.dup @guessed_host_and_path = request.host_with_port.dup
-3
View File
@@ -308,9 +308,6 @@ private
when 'last_week' when 'last_week'
@from = Date.today - 7 - (Date.today.cwday - 1)%7 @from = Date.today - 7 - (Date.today.cwday - 1)%7
@to = @from + 6 @to = @from + 6
when 'last_2_weeks'
@from = Date.today - 14 - (Date.today.cwday - 1)%7
@to = @from + 13
when '7_days' when '7_days'
@from = Date.today - 7 @from = Date.today - 7
@to = Date.today @to = Date.today
+8 -2
View File
@@ -20,7 +20,7 @@ class VersionsController < ApplicationController
model_object Version model_object Version
before_filter :find_model_object, :except => [:index, :new, :create, :close_completed] before_filter :find_model_object, :except => [:index, :new, :create, :close_completed]
before_filter :find_project_from_association, :except => [:index, :new, :create, :close_completed] before_filter :find_project_from_association, :except => [:index, :new, :create, :close_completed]
before_filter :find_project_by_project_id, :only => [:index, :new, :create, :close_completed] before_filter :find_project, :only => [:index, :new, :create, :close_completed]
before_filter :authorize before_filter :authorize
accept_api_auth :index, :show, :create, :update, :destroy accept_api_auth :index, :show, :create, :update, :destroy
@@ -169,7 +169,12 @@ class VersionsController < ApplicationController
end end
end end
private private
def find_project
@project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound
render_404
end
def retrieve_selected_tracker_ids(selectable_trackers, default_trackers=nil) def retrieve_selected_tracker_ids(selectable_trackers, default_trackers=nil)
if ids = params[:tracker_ids] if ids = params[:tracker_ids]
@@ -178,4 +183,5 @@ class VersionsController < ApplicationController
@selected_tracker_ids = (default_trackers || selectable_trackers).collect {|t| t.id.to_s } @selected_tracker_ids = (default_trackers || selectable_trackers).collect {|t| t.id.to_s }
end end
end end
end end
+23 -68
View File
@@ -35,8 +35,7 @@ class WikiController < ApplicationController
default_search_scope :wiki_pages default_search_scope :wiki_pages
before_filter :find_wiki, :authorize before_filter :find_wiki, :authorize
before_filter :find_existing_or_new_page, :only => [:show, :edit, :update] before_filter :find_existing_or_new_page, :only => [:show, :edit, :update]
before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy, :destroy_version] before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy]
accept_api_auth :index, :show, :update, :destroy
helper :attachments helper :attachments
include AttachmentsHelper include AttachmentsHelper
@@ -46,13 +45,7 @@ class WikiController < ApplicationController
# List of pages, sorted alphabetically and by parent (hierarchy) # List of pages, sorted alphabetically and by parent (hierarchy)
def index def index
load_pages_for_index load_pages_for_index
@pages_by_parent_id = @pages.group_by(&:parent_id)
respond_to do |format|
format.html {
@pages_by_parent_id = @pages.group_by(&:parent_id)
}
format.api
end
end end
# List of page, by last update # List of page, by last update
@@ -64,7 +57,7 @@ class WikiController < ApplicationController
# display a page (in editing mode if it doesn't exist) # display a page (in editing mode if it doesn't exist)
def show def show
if @page.new_record? if @page.new_record?
if User.current.allowed_to?(:edit_wiki_pages, @project) && editable? && !api_request? if User.current.allowed_to?(:edit_wiki_pages, @project) && editable?
edit edit
render :action => 'edit' render :action => 'edit'
else else
@@ -73,7 +66,8 @@ class WikiController < ApplicationController
return return
end end
if params[:version] && !User.current.allowed_to?(:view_wiki_edits, @project) if params[:version] && !User.current.allowed_to?(:view_wiki_edits, @project)
deny_access # Redirects user to the current version if he's not allowed to view previous versions
redirect_to :version => nil
return return
end end
@content = @page.content_for_version(params[:version]) @content = @page.content_for_version(params[:version])
@@ -95,10 +89,7 @@ class WikiController < ApplicationController
@content.current_version? && @content.current_version? &&
Redmine::WikiFormatting.supports_section_edit? Redmine::WikiFormatting.supports_section_edit?
respond_to do |format| render :action => 'show'
format.html
format.api
end
end end
# edit an existing page or a new one # edit an existing page or a new one
@@ -130,25 +121,22 @@ class WikiController < ApplicationController
# Creates a new page or updates an existing one # Creates a new page or updates an existing one
def update def update
return render_403 unless editable? return render_403 unless editable?
was_new_page = @page.new_record?
@page.content = WikiContent.new(:page => @page) if @page.new_record? @page.content = WikiContent.new(:page => @page) if @page.new_record?
@page.safe_attributes = params[:wiki_page] @page.safe_attributes = params[:wiki_page]
@content = @page.content @content = @page.content_for_version(params[:version])
content_params = params[:content] @content.text = initial_page_content(@page) if @content.text.blank?
if content_params.nil? && params[:wiki_page].is_a?(Hash) # don't keep previous comment
content_params = params[:wiki_page].slice(:text, :comments, :version) @content.comments = nil
end
content_params ||= {}
@content.comments = content_params[:comments] @content.comments = params[:content][:comments]
@text = content_params[:text] @text = params[:content][:text]
if params[:section].present? && Redmine::WikiFormatting.supports_section_edit? if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
@section = params[:section].to_i @section = params[:section].to_i
@section_hash = params[:section_hash] @section_hash = params[:section_hash]
@content.text = Redmine::WikiFormatting.formatter.new(@content.text).update_section(params[:section].to_i, @text, @section_hash) @content.text = Redmine::WikiFormatting.formatter.new(@content.text).update_section(params[:section].to_i, @text, @section_hash)
else else
@content.version = content_params[:version] if content_params[:version] @content.version = params[:content][:version]
@content.text = @text @content.text = @text
end end
@content.author = User.current @content.author = User.current
@@ -157,38 +145,17 @@ class WikiController < ApplicationController
attachments = Attachment.attach_files(@page, params[:attachments]) attachments = Attachment.attach_files(@page, params[:attachments])
render_attachment_warning_if_needed(@page) render_attachment_warning_if_needed(@page)
call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page}) call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
redirect_to :action => 'show', :project_id => @project, :id => @page.title
respond_to do |format|
format.html { redirect_to :action => 'show', :project_id => @project, :id => @page.title }
format.api {
if was_new_page
render :action => 'show', :status => :created, :location => url_for(:controller => 'wiki', :action => 'show', :project_id => @project, :id => @page.title)
else
render_api_ok
end
}
end
else else
respond_to do |format| render :action => 'edit'
format.html { render :action => 'edit' }
format.api { render_validation_errors(@content) }
end
end end
rescue ActiveRecord::StaleObjectError, Redmine::WikiFormatting::StaleSectionError rescue ActiveRecord::StaleObjectError, Redmine::WikiFormatting::StaleSectionError
# Optimistic locking exception # Optimistic locking exception
respond_to do |format| flash.now[:error] = l(:notice_locking_conflict)
format.html { render :action => 'edit'
flash.now[:error] = l(:notice_locking_conflict)
render :action => 'edit'
}
format.api { render_api_head :conflict }
end
rescue ActiveRecord::RecordNotSaved rescue ActiveRecord::RecordNotSaved
respond_to do |format| render :action => 'edit'
format.html { render :action => 'edit' }
format.api { render_validation_errors(@content) }
end
end end
# rename a page # rename a page
@@ -211,7 +178,7 @@ class WikiController < ApplicationController
# show page history # show page history
def history def history
@version_count = @page.content.versions.count @version_count = @page.content.versions.count
@version_pages = Paginator.new self, @version_count, per_page_option, params['page'] @version_pages = Paginator.new self, @version_count, per_page_option, params['p']
# don't load text # don't load text
@versions = @page.content.versions.find :all, @versions = @page.content.versions.find :all,
:select => "id, author_id, comments, updated_on, version", :select => "id, author_id, comments, updated_on, version",
@@ -254,28 +221,16 @@ class WikiController < ApplicationController
end end
else else
@reassignable_to = @wiki.pages - @page.self_and_descendants @reassignable_to = @wiki.pages - @page.self_and_descendants
# display the destroy form if it's a user request return
return unless api_request?
end end
end end
@page.destroy @page.destroy
respond_to do |format| redirect_to :action => 'index', :project_id => @project
format.html { redirect_to :action => 'index', :project_id => @project }
format.api { render_api_ok }
end
end
def destroy_version
return render_403 unless editable?
@content = @page.content_for_version(params[:version])
@content.destroy
redirect_to_referer_or :action => 'history', :id => @page.title, :project_id => @project
end end
# Export wiki to a single pdf or html file # Export wiki to a single pdf or html file
def export def export
@pages = @wiki.pages.all(:order => 'title', :include => [:content, {:attachments => :author}]) @pages = @wiki.pages.all(:order => 'title', :include => [:content, :attachments])
respond_to do |format| respond_to do |format|
format.html { format.html {
export = render_to_string :action => 'export_multiple', :layout => false export = render_to_string :action => 'export_multiple', :layout => false
@@ -349,6 +304,6 @@ private
end end
def load_pages_for_index def load_pages_for_index
@pages = @wiki.pages.with_updated_on.order("#{WikiPage.table_name}.title").includes(:wiki => :project).includes(:parent).all @pages = @wiki.pages.with_updated_on.all(:order => 'title', :include => {:wiki => :project})
end end
end end
+15 -29
View File
@@ -47,8 +47,8 @@ module ApplicationHelper
def link_to_user(user, options={}) def link_to_user(user, options={})
if user.is_a?(User) if user.is_a?(User)
name = h(user.name(options[:format])) name = h(user.name(options[:format]))
if user.active? || (User.current.admin? && user.logged?) if user.active?
link_to name, user_path(user), :class => user.css_classes link_to name, :controller => 'users', :action => 'show', :id => user
else else
name name
end end
@@ -64,12 +64,10 @@ module ApplicationHelper
# link_to_issue(issue, :truncate => 6) # => Defect #6: This i... # link_to_issue(issue, :truncate => 6) # => Defect #6: This i...
# link_to_issue(issue, :subject => false) # => Defect #6 # link_to_issue(issue, :subject => false) # => Defect #6
# link_to_issue(issue, :project => true) # => Foo - Defect #6 # link_to_issue(issue, :project => true) # => Foo - Defect #6
# link_to_issue(issue, :subject => false, :tracker => false) # => #6
# #
def link_to_issue(issue, options={}) def link_to_issue(issue, options={})
title = nil title = nil
subject = nil subject = nil
text = options[:tracker] == false ? "##{issue.id}" : "#{issue.tracker} ##{issue.id}"
if options[:subject] == false if options[:subject] == false
title = truncate(issue.subject, :length => 60) title = truncate(issue.subject, :length => 60)
else else
@@ -78,7 +76,9 @@ module ApplicationHelper
subject = truncate(subject, :length => options[:truncate]) subject = truncate(subject, :length => options[:truncate])
end end
end end
s = link_to text, issue_path(issue), :class => issue.css_classes, :title => title s = link_to "#{h(issue.tracker)} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue},
:class => issue.css_classes,
:title => title
s << h(": #{subject}") if subject s << h(": #{subject}") if subject
s = h("#{issue.project} - ") + s if options[:project] s = h("#{issue.project} - ") + s if options[:project]
s s
@@ -147,10 +147,6 @@ module ApplicationHelper
end end
end end
def wiki_page_path(page, options={})
url_for({:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title}.merge(options))
end
def thumbnail_tag(attachment) def thumbnail_tag(attachment)
link_to image_tag(url_for(:controller => 'attachments', :action => 'thumbnail', :id => attachment)), link_to image_tag(url_for(:controller => 'attachments', :action => 'thumbnail', :id => attachment)),
{:controller => 'attachments', :action => 'show', :id => attachment, :filename => attachment.filename}, {:controller => 'attachments', :action => 'show', :id => attachment, :filename => attachment.filename},
@@ -238,7 +234,7 @@ module ApplicationHelper
content << "<ul class=\"pages-hierarchy\">\n" content << "<ul class=\"pages-hierarchy\">\n"
pages[node].each do |page| pages[node].each do |page|
content << "<li>" content << "<li>"
content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title, :version => nil}, content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title},
:title => (options[:timestamp] && page.updated_on ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil)) :title => (options[:timestamp] && page.updated_on ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
content << "\n" + render_page_hierarchy(pages, page.id, options) if pages[page.id] content << "\n" + render_page_hierarchy(pages, page.id, options) if pages[page.id]
content << "</li>\n" content << "</li>\n"
@@ -331,15 +327,6 @@ module ApplicationHelper
s.html_safe s.html_safe
end end
# Options for the new membership projects combo-box
def options_for_membership_project_select(principal, projects)
options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---")
options << project_tree_options_for_select(projects) do |p|
{:disabled => principal.projects.include?(p)}
end
options
end
# Truncates and returns the string as a single line # Truncates and returns the string as a single line
def truncate_single_line(string, *args) def truncate_single_line(string, *args)
truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ') truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
@@ -597,9 +584,8 @@ module ApplicationHelper
def parse_inline_attachments(text, project, obj, attr, only_path, options) def parse_inline_attachments(text, project, obj, attr, only_path, options)
# when using an image link, try to use an attachment, if possible # when using an image link, try to use an attachment, if possible
attachments = options[:attachments] || [] if options[:attachments] || (obj && obj.respond_to?(:attachments))
attachments += obj.attachments if obj.respond_to?(:attachments) attachments = options[:attachments] || obj.attachments
if attachments.present?
text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpe|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m| text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpe|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
filename, ext, alt, alttext = $1.downcase, $2, $3, $4 filename, ext, alt, alttext = $1.downcase, $2, $3, $4
# search for the picture in attachments # search for the picture in attachments
@@ -658,7 +644,7 @@ module ApplicationHelper
wiki_page_id = page.present? ? Wiki.titleize(page) : nil wiki_page_id = page.present? ? Wiki.titleize(page) : nil
parent = wiki_page.nil? && obj.is_a?(WikiContent) && obj.page && project == link_project ? obj.page.title : nil parent = wiki_page.nil? && obj.is_a?(WikiContent) && obj.page && project == link_project ? obj.page.title : nil
url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project, url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project,
:id => wiki_page_id, :version => nil, :anchor => anchor, :parent => parent) :id => wiki_page_id, :anchor => anchor, :parent => parent)
end end
end end
link_to(title.present? ? title.html_safe : h(page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new'))) link_to(title.present? ? title.html_safe : h(page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
@@ -704,11 +690,10 @@ module ApplicationHelper
# identifier:document:"Some document" # identifier:document:"Some document"
# identifier:version:1.0.0 # identifier:version:1.0.0
# identifier:source:some/file # identifier:source:some/file
def parse_redmine_links(text, default_project, obj, attr, only_path, options) def parse_redmine_links(text, project, obj, attr, only_path, options)
text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-_]+):)?(attachment|document|version|forum|news|message|project|commit|source|export)?(((#)|((([a-z0-9\-_]+)\|)?(r)))((\d+)((#note)?-(\d+))?)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]][^A-Za-z0-9_/])|,|\s|\]|<|$)}) do |m| text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-_]+):)?(attachment|document|version|forum|news|message|project|commit|source|export)?(((#)|((([a-z0-9\-]+)\|)?(r)))((\d+)((#note)?-(\d+))?)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]][^A-Za-z0-9_/])|,|\s|\]|<|$)}) do |m|
leading, esc, project_prefix, project_identifier, prefix, repo_prefix, repo_identifier, sep, identifier, comment_suffix, comment_id = $1, $2, $3, $4, $5, $10, $11, $8 || $12 || $18, $14 || $19, $15, $17 leading, esc, project_prefix, project_identifier, prefix, repo_prefix, repo_identifier, sep, identifier, comment_suffix, comment_id = $1, $2, $3, $4, $5, $10, $11, $8 || $12 || $18, $14 || $19, $15, $17
link = nil link = nil
project = default_project
if project_identifier if project_identifier
project = Project.visible.find_by_identifier(project_identifier) project = Project.visible.find_by_identifier(project_identifier)
end end
@@ -794,7 +779,7 @@ module ApplicationHelper
when 'commit', 'source', 'export' when 'commit', 'source', 'export'
if project if project
repository = nil repository = nil
if name =~ %r{^(([a-z0-9\-_]+)\|)(.+)$} if name =~ %r{^(([a-z0-9\-]+)\|)(.+)$}
repo_prefix, repo_identifier, name = $1, $2, $3 repo_prefix, repo_identifier, name = $1, $2, $3
repository = project.repositories.detect {|repo| repo.identifier == repo_identifier} repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
else else
@@ -821,7 +806,7 @@ module ApplicationHelper
end end
when 'attachment' when 'attachment'
attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil) attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
if attachments && attachment = Attachment.latest_attach(attachments, name) if attachments && attachment = attachments.detect {|a| a.filename == name }
link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment}, link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
:class => 'attachment' :class => 'attachment'
end end
@@ -973,7 +958,8 @@ module ApplicationHelper
end end
def lang_options_for_select(blank=true) def lang_options_for_select(blank=true)
(blank ? [["(auto)", ""]] : []) + languages_options (blank ? [["(auto)", ""]] : []) +
valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
end end
def label_tag_for(name, option_tags = nil, options = {}) def label_tag_for(name, option_tags = nil, options = {})
+10 -1
View File
@@ -20,7 +20,16 @@
module CustomFieldsHelper module CustomFieldsHelper
def custom_fields_tabs def custom_fields_tabs
CustomField::CUSTOM_FIELDS_TABS tabs = [{:name => 'IssueCustomField', :partial => 'custom_fields/index', :label => :label_issue_plural},
{:name => 'TimeEntryCustomField', :partial => 'custom_fields/index', :label => :label_spent_time},
{:name => 'ProjectCustomField', :partial => 'custom_fields/index', :label => :label_project_plural},
{:name => 'VersionCustomField', :partial => 'custom_fields/index', :label => :label_version_plural},
{:name => 'UserCustomField', :partial => 'custom_fields/index', :label => :label_user_plural},
{:name => 'GroupCustomField', :partial => 'custom_fields/index', :label => :label_group_plural},
{:name => 'TimeEntryActivityCustomField', :partial => 'custom_fields/index', :label => TimeEntryActivity::OptionName},
{:name => 'IssuePriorityCustomField', :partial => 'custom_fields/index', :label => IssuePriority::OptionName},
{:name => 'DocumentCategoryCustomField', :partial => 'custom_fields/index', :label => DocumentCategory::OptionName}
]
end end
# Return custom field html tag corresponding to its format # Return custom field html tag corresponding to its format
+9
View File
@@ -18,6 +18,15 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module GroupsHelper module GroupsHelper
# Options for the new membership projects combo-box
def options_for_membership_project_select(user, projects)
options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---")
options << project_tree_options_for_select(projects) do |p|
{:disabled => (user.projects.include?(p))}
end
options
end
def group_settings_tabs def group_settings_tabs
tabs = [{:name => 'general', :partial => 'groups/general', :label => :label_general}, tabs = [{:name => 'general', :partial => 'groups/general', :label => :label_general},
{:name => 'users', :partial => 'groups/users', :label => :label_user_plural}, {:name => 'users', :partial => 'groups/users', :label => :label_user_plural},
+9 -26
View File
@@ -65,7 +65,7 @@ module IssuesHelper
s = '' s = ''
ancestors = issue.root? ? [] : issue.ancestors.visible.all ancestors = issue.root? ? [] : issue.ancestors.visible.all
ancestors.each do |ancestor| ancestors.each do |ancestor|
s << '<div>' + content_tag('p', link_to_issue(ancestor, :project => (issue.project_id != ancestor.project_id))) s << '<div>' + content_tag('p', link_to_issue(ancestor))
end end
s << '<div>' s << '<div>'
subject = h(issue.subject) subject = h(issue.subject)
@@ -80,29 +80,18 @@ module IssuesHelper
def render_descendants_tree(issue) def render_descendants_tree(issue)
s = '<form><table class="list issues">' s = '<form><table class="list issues">'
issue_list(issue.descendants.visible.sort_by(&:lft)) do |child, level| issue_list(issue.descendants.visible.sort_by(&:lft)) do |child, level|
css = "issue issue-#{child.id} hascontextmenu"
css << " idnt idnt-#{level}" if level > 0
s << content_tag('tr', s << content_tag('tr',
content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') + content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
content_tag('td', link_to_issue(child, :truncate => 60, :project => (issue.project_id != child.project_id)), :class => 'subject') + content_tag('td', link_to_issue(child, :truncate => 60), :class => 'subject') +
content_tag('td', h(child.status)) + content_tag('td', h(child.status)) +
content_tag('td', link_to_user(child.assigned_to)) + content_tag('td', link_to_user(child.assigned_to)) +
content_tag('td', progress_bar(child.done_ratio, :width => '80px')), content_tag('td', progress_bar(child.done_ratio, :width => '80px')),
:class => css) :class => "issue issue-#{child.id} hascontextmenu #{level > 0 ? "idnt idnt-#{level}" : nil}")
end end
s << '</table></form>' s << '</table></form>'
s.html_safe s.html_safe
end end
# Returns a link for adding a new subtask to the given issue
def link_to_new_subtask(issue)
attrs = {
:tracker_id => issue.tracker,
:parent_issue_id => issue
}
link_to(l(:button_add), new_project_issue_path(issue.project, :issue => attrs))
end
class IssueFieldsRows class IssueFieldsRows
include ActionView::Helpers::TagHelper include ActionView::Helpers::TagHelper
@@ -350,10 +339,7 @@ module IssuesHelper
association = Issue.reflect_on_association(field.to_sym) association = Issue.reflect_on_association(field.to_sym)
if association if association
record = association.class_name.constantize.find_by_id(id) record = association.class_name.constantize.find_by_id(id)
if record return record.name if record
record.name.force_encoding('UTF-8') if record.name.respond_to?(:force_encoding)
return record.name
end
end end
end end
@@ -374,16 +360,12 @@ module IssuesHelper
def issues_to_csv(issues, project, query, options={}) def issues_to_csv(issues, project, query, options={})
decimal_separator = l(:general_csv_decimal_separator) decimal_separator = l(:general_csv_decimal_separator)
encoding = l(:general_csv_encoding) encoding = l(:general_csv_encoding)
columns = (options[:columns] == 'all' ? query.available_inline_columns : query.inline_columns) columns = (options[:columns] == 'all' ? query.available_columns : query.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| export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
# csv header fields # csv header fields
csv << [ "#" ] + columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) } csv << [ "#" ] + columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) } +
(options[:description] ? [Redmine::CodesetUtil.from_utf8(l(:field_description), encoding)] : [])
# csv lines # csv lines
issues.each do |issue| issues.each do |issue|
@@ -405,7 +387,8 @@ module IssuesHelper
end end
s.to_s s.to_s
end end
csv << [ issue.id.to_s ] + col_values.collect {|c| Redmine::CodesetUtil.from_utf8(c.to_s, encoding) } csv << [ issue.id.to_s ] + col_values.collect {|c| Redmine::CodesetUtil.from_utf8(c.to_s, encoding) } +
(options[:description] ? [Redmine::CodesetUtil.from_utf8(issue.description, encoding)] : [])
end end
end end
export export
+7 -46
View File
@@ -19,43 +19,11 @@
module QueriesHelper module QueriesHelper
def filters_options_for_select(query) def filters_options_for_select(query)
options_for_select(filters_options(query))
end
def filters_options(query)
options = [[]] options = [[]]
sorted_options = query.available_filters.sort do |a, b| options += query.available_filters.sort {|a,b| a[1][:order] <=> b[1][:order]}.map do |field, field_options|
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] [field_options[:name], field]
end end
end options_for_select(options)
def available_block_columns_tags(query)
tags = ''.html_safe
query.available_block_columns.each do |column|
tags << content_tag('label', check_box_tag('c[]', column.name.to_s, query.has_column?(column)) + " #{column.caption}", :class => 'inline')
end
tags
end end
def column_header(column) def column_header(column)
@@ -67,7 +35,7 @@ module QueriesHelper
def column_content(column, issue) def column_content(column, issue)
value = column.value(issue) value = column.value(issue)
if value.is_a?(Array) if value.is_a?(Array)
value.collect {|v| column_value(column, issue, v)}.compact.join(', ').html_safe value.collect {|v| column_value(column, issue, v)}.compact.sort.join(', ').html_safe
else else
column_value(column, issue, value) column_value(column, issue, value)
end end
@@ -78,8 +46,6 @@ module QueriesHelper
when 'String' when 'String'
if column.name == :subject if column.name == :subject
link_to(h(value), :controller => 'issues', :action => 'show', :id => issue) link_to(h(value), :controller => 'issues', :action => 'show', :id => issue)
elsif column.name == :description
issue.description? ? content_tag('div', textilizable(issue, :description), :class => "wiki") : ''
else else
h(value) h(value)
end end
@@ -87,14 +53,14 @@ module QueriesHelper
format_time(value) format_time(value)
when 'Date' when 'Date'
format_date(value) format_date(value)
when 'Fixnum' when 'Fixnum', 'Float'
if column.name == :done_ratio if column.name == :done_ratio
progress_bar(value, :width => '80px') progress_bar(value, :width => '80px')
elsif column.name == :spent_hours
sprintf "%.2f", value
else else
value.to_s h(value.to_s)
end end
when 'Float'
sprintf "%.2f", value
when 'User' when 'User'
link_to_user value link_to_user value
when 'Project' when 'Project'
@@ -107,11 +73,6 @@ module QueriesHelper
l(:general_text_No) l(:general_text_No)
when 'Issue' when 'Issue'
link_to_issue(value, :subject => false) link_to_issue(value, :subject => false)
when 'IssueRelation'
other = value.other_issue(issue)
content_tag('span',
(l(value.label_for(issue)) + " " + link_to_issue(other, :subject => false, :tracker => false)).html_safe,
:class => value.css_classes_for(issue))
else else
h(value) h(value)
end end
+16
View File
@@ -17,6 +17,9 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'iconv'
require 'redmine/codeset_util'
module RepositoriesHelper module RepositoriesHelper
def format_revision(revision) def format_revision(revision)
if revision.respond_to? :format_identifier if revision.respond_to? :format_identifier
@@ -250,13 +253,16 @@ module RepositoriesHelper
def index_commits(commits, heads) def index_commits(commits, heads)
return nil if commits.nil? or commits.first.parents.nil? return nil if commits.nil? or commits.first.parents.nil?
refs_map = {} refs_map = {}
heads.each do |head| heads.each do |head|
refs_map[head.scmid] ||= [] refs_map[head.scmid] ||= []
refs_map[head.scmid] << head refs_map[head.scmid] << head
end end
commits_by_scmid = {} commits_by_scmid = {}
commits.reverse.each_with_index do |commit, commit_index| commits.reverse.each_with_index do |commit, commit_index|
commits_by_scmid[commit.scmid] = { commits_by_scmid[commit.scmid] = {
:parent_scmids => commit.parents.collect { |parent| parent.scmid }, :parent_scmids => commit.parents.collect { |parent| parent.scmid },
:rdmid => commit_index, :rdmid => commit_index,
@@ -265,28 +271,38 @@ module RepositoriesHelper
:href => block_given? ? yield(commit.scmid) : commit.scmid :href => block_given? ? yield(commit.scmid) : commit.scmid
} }
end end
heads.sort! { |head1, head2| head1.to_s <=> head2.to_s } heads.sort! { |head1, head2| head1.to_s <=> head2.to_s }
space = nil space = nil
heads.each do |head| heads.each do |head|
if commits_by_scmid.include? head.scmid if commits_by_scmid.include? head.scmid
space = index_head((space || -1) + 1, head, commits_by_scmid) space = index_head((space || -1) + 1, head, commits_by_scmid)
end end
end end
# when no head matched anything use first commit # when no head matched anything use first commit
space ||= index_head(0, commits.first, commits_by_scmid) space ||= index_head(0, commits.first, commits_by_scmid)
return commits_by_scmid, space return commits_by_scmid, space
end end
def index_head(space, commit, commits_by_scmid) def index_head(space, commit, commits_by_scmid)
stack = [[space, commits_by_scmid[commit.scmid]]] stack = [[space, commits_by_scmid[commit.scmid]]]
max_space = space max_space = space
until stack.empty? until stack.empty?
space, commit = stack.pop space, commit = stack.pop
commit[:space] = space if commit[:space].nil? commit[:space] = space if commit[:space].nil?
space -= 1 space -= 1
commit[:parent_scmids].each_with_index do |parent_scmid, parent_index| commit[:parent_scmids].each_with_index do |parent_scmid, parent_index|
parent_commit = commits_by_scmid[parent_scmid] parent_commit = commits_by_scmid[parent_scmid]
if parent_commit and parent_commit[:space].nil? if parent_commit and parent_commit[:space].nil?
stack.unshift [space += 1, parent_commit] stack.unshift [space += 1, parent_commit]
end end
end end
+1 -13
View File
@@ -56,7 +56,7 @@ module SettingsHelper
Setting.send(setting).include?(value), Setting.send(setting).include?(value),
:id => nil :id => nil
) + text.to_s, ) + text.to_s,
:class => (options[:inline] ? 'inline' : 'block') :class => 'block'
) )
end.join.html_safe end.join.html_safe
end end
@@ -91,16 +91,4 @@ module SettingsHelper
l_or_humanize(notifiable.name, :prefix => 'label_').html_safe, l_or_humanize(notifiable.name, :prefix => 'label_').html_safe,
:class => notifiable.parent.present? ? "parent" : '').html_safe :class => notifiable.parent.present? ? "parent" : '').html_safe
end end
def cross_project_subtasks_options
options = [
[:label_disabled, ''],
[:label_cross_project_system, 'system'],
[:label_cross_project_tree, 'tree'],
[:label_cross_project_hierarchy, 'hierarchy'],
[:label_cross_project_descendants, 'descendants']
]
options.map {|label, value| [l(label), value.to_s]}
end
end end
-8
View File
@@ -89,10 +89,6 @@ module SortHelper
sql.blank? ? nil : sql sql.blank? ? nil : sql
end end
def to_a
@criteria.dup
end
def add!(key, asc) def add!(key, asc)
@criteria.delete_if {|k,o| k == key} @criteria.delete_if {|k,o| k == key}
@criteria = [[key, asc]] + @criteria @criteria = [[key, asc]] + @criteria
@@ -186,10 +182,6 @@ module SortHelper
@sort_criteria.to_sql @sort_criteria.to_sql
end end
def sort_criteria
@sort_criteria
end
# Returns a link which sorts by the named column. # Returns a link which sorts by the named column.
# #
# - column is the name of an attribute in the sorted record collection. # - column is the name of an attribute in the sorted record collection.
-1
View File
@@ -77,7 +77,6 @@ module TimelogHelper
[l(:label_yesterday), 'yesterday'], [l(:label_yesterday), 'yesterday'],
[l(:label_this_week), 'current_week'], [l(:label_this_week), 'current_week'],
[l(:label_last_week), 'last_week'], [l(:label_last_week), 'last_week'],
[l(:label_last_n_weeks, 2), 'last_2_weeks'],
[l(:label_last_n_days, 7), '7_days'], [l(:label_last_n_days, 7), '7_days'],
[l(:label_this_month), 'current_month'], [l(:label_this_month), 'current_month'],
[l(:label_last_month), 'last_month'], [l(:label_last_month), 'last_month'],
+9
View File
@@ -26,6 +26,15 @@ module UsersHelper
["#{l(:status_locked)} (#{user_count_by_status[3].to_i})", '3']], selected.to_s) ["#{l(:status_locked)} (#{user_count_by_status[3].to_i})", '3']], selected.to_s)
end end
# Options for the new membership projects combo-box
def options_for_membership_project_select(user, projects)
options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---")
options << project_tree_options_for_select(projects) do |p|
{:disabled => (user.projects.include?(p))}
end
options
end
def user_mail_notification_options(user) def user_mail_notification_options(user)
user.valid_notification_options.collect {|o| [l(o.last), o.first]} user.valid_notification_options.collect {|o| [l(o.last), o.first]}
end end
+1 -2
View File
@@ -44,8 +44,7 @@ module VersionsHelper
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
# When grouping by an association, Rails throws this exception if there's no result (bug) # When grouping by an association, Rails throws this exception if there's no result (bug)
end end
# Sort with nil keys in last position counts = h.keys.compact.sort.collect {|k| {:group => k, :total => h[k][0], :open => h[k][1], :closed => (h[k][0] - h[k][1])}}
counts = h.keys.sort {|a,b| a.nil? ? 1 : (b.nil? ? -1 : a <=> b)}.collect {|k| {:group => k, :total => h[k][0], :open => h[k][1], :closed => (h[k][0] - h[k][1])}}
max = counts.collect {|c| c[:total]}.max max = counts.collect {|c| c[:total]}.max
render :partial => 'issue_counts', :locals => {:version => version, :criteria => criteria, :counts => counts, :max => max} render :partial => 'issue_counts', :locals => {:version => version, :criteria => criteria, :counts => counts, :max => max}
+1 -1
View File
@@ -37,7 +37,7 @@ module WikiHelper
def wiki_page_breadcrumb(page) def wiki_page_breadcrumb(page)
breadcrumb(page.ancestors.reverse.collect {|parent| breadcrumb(page.ancestors.reverse.collect {|parent|
link_to(h(parent.pretty_title), {:controller => 'wiki', :action => 'show', :id => parent.title, :project_id => parent.project, :version => nil}) link_to(h(parent.pretty_title), {:controller => 'wiki', :action => 'show', :id => parent.title, :project_id => parent.project})
}) })
end end
end end
-28
View File
@@ -30,34 +30,6 @@ class CustomField < ActiveRecord::Base
validate :validate_custom_field validate :validate_custom_field
before_validation :set_searchable before_validation :set_searchable
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}
]
CUSTOM_FIELDS_NAMES = CUSTOM_FIELDS_TABS.collect{|v| v[:name]}
def field_format=(arg)
# cannot change format of a saved custom field
super if new_record?
end
def set_searchable def set_searchable
# make sure these fields are not searchable # make sure these fields are not searchable
self.searchable = false if %w(int float date bool).include?(field_format) self.searchable = false if %w(int float date bool).include?(field_format)
-1
View File
@@ -36,7 +36,6 @@ class Enumeration < ActiveRecord::Base
validates_length_of :name, :maximum => 30 validates_length_of :name, :maximum => 30
scope :shared, where(:project_id => nil) scope :shared, where(:project_id => nil)
scope :sorted, order("#{table_name}.position ASC")
scope :active, where(:active => true) scope :active, where(:active => true)
scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)} scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)}
+31 -143
View File
@@ -17,7 +17,6 @@
class Issue < ActiveRecord::Base class Issue < ActiveRecord::Base
include Redmine::SafeAttributes include Redmine::SafeAttributes
include Redmine::Utils::DateCalculation
belongs_to :project belongs_to :project
belongs_to :tracker belongs_to :tracker
@@ -29,14 +28,6 @@ class Issue < ActiveRecord::Base
belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id' belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id'
has_many :journals, :as => :journalized, :dependent => :destroy has_many :journals, :as => :journalized, :dependent => :destroy
has_many :visible_journals,
:class_name => 'Journal',
:as => :journalized,
:conditions => Proc.new {
["(#{Journal.table_name}.private_notes = ? OR (#{Project.allowed_to_condition(User.current, :view_private_notes)}))", false]
},
:readonly => true
has_many :time_entries, :dependent => :delete_all has_many :time_entries, :dependent => :delete_all
has_and_belongs_to_many :changesets, :order => "#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC" has_and_belongs_to_many :changesets, :order => "#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC"
@@ -48,7 +39,7 @@ class Issue < ActiveRecord::Base
acts_as_customizable acts_as_customizable
acts_as_watchable acts_as_watchable
acts_as_searchable :columns => ['subject', "#{table_name}.description", "#{Journal.table_name}.notes"], acts_as_searchable :columns => ['subject', "#{table_name}.description", "#{Journal.table_name}.notes"],
:include => [:project, :visible_journals], :include => [:project, :journals],
# sort by id so that limited eager loading doesn't break with postgresql # sort by id so that limited eager loading doesn't break with postgresql
:order_column => "#{table_name}.id" :order_column => "#{table_name}.id"
acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id} (#{o.status}): #{o.subject}"}, acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id} (#{o.status}): #{o.subject}"},
@@ -61,7 +52,6 @@ class Issue < ActiveRecord::Base
DONE_RATIO_OPTIONS = %w(issue_field issue_status) DONE_RATIO_OPTIONS = %w(issue_field issue_status)
attr_reader :current_journal attr_reader :current_journal
delegate :notes, :notes=, :private_notes, :private_notes=, :to => :current_journal, :allow_nil => true
validates_presence_of :subject, :priority, :project, :tracker, :author, :status validates_presence_of :subject, :priority, :project, :tracker, :author, :status
@@ -286,8 +276,7 @@ class Issue < ActiveRecord::Base
if fixed_version && fixed_version.project != project && !project.shared_versions.include?(fixed_version) if fixed_version && fixed_version.project != project && !project.shared_versions.include?(fixed_version)
self.fixed_version = nil self.fixed_version = nil
end end
# Clear the parent task if it's no longer valid if parent && parent.project_id != project_id
unless valid_parent_project?
self.parent_issue_id = nil self.parent_issue_id = nil
end end
@custom_field_values = nil @custom_field_values = nil
@@ -346,7 +335,6 @@ class Issue < ActiveRecord::Base
'custom_field_values', 'custom_field_values',
'custom_fields', 'custom_fields',
'lock_version', 'lock_version',
'notes',
:if => lambda {|issue, user| issue.new_record? || user.allowed_to?(:edit_issues, issue.project) } :if => lambda {|issue, user| issue.new_record? || user.allowed_to?(:edit_issues, issue.project) }
safe_attributes 'status_id', safe_attributes 'status_id',
@@ -354,15 +342,8 @@ class Issue < ActiveRecord::Base
'fixed_version_id', 'fixed_version_id',
'done_ratio', 'done_ratio',
'lock_version', 'lock_version',
'notes',
:if => lambda {|issue, user| issue.new_statuses_allowed_to(user).any? } :if => lambda {|issue, user| issue.new_statuses_allowed_to(user).any? }
safe_attributes 'notes',
:if => lambda {|issue, user| user.allowed_to?(:add_issue_notes, issue.project)}
safe_attributes 'private_notes',
:if => lambda {|issue, user| !issue.new_record? && user.allowed_to?(:set_notes_private, issue.project)}
safe_attributes 'watcher_user_ids', safe_attributes 'watcher_user_ids',
:if => lambda {|issue, user| issue.new_record? && user.allowed_to?(:add_issue_watchers, issue.project)} :if => lambda {|issue, user| issue.new_record? && user.allowed_to?(:add_issue_watchers, issue.project)}
@@ -417,10 +398,7 @@ class Issue < ActiveRecord::Base
end end
if attrs['parent_issue_id'].present? if attrs['parent_issue_id'].present?
s = attrs['parent_issue_id'].to_s attrs.delete('parent_issue_id') unless Issue.visible(user).exists?(attrs['parent_issue_id'].to_i)
unless (m = s.match(%r{\A#?(\d+)\z})) && (m[1] == parent_id.to_s || Issue.visible(user).exists?(m[1]))
@invalid_parent_issue_id = attrs.delete('parent_issue_id')
end
end end
if attrs['custom_field_values'].present? if attrs['custom_field_values'].present?
@@ -526,15 +504,11 @@ class Issue < ActiveRecord::Base
end end
def validate_issue def validate_issue
if due_date.nil? && @attributes['due_date'].present? if self.due_date.nil? && @attributes['due_date'] && !@attributes['due_date'].empty?
errors.add :due_date, :not_a_date errors.add :due_date, :not_a_date
end end
if start_date.nil? && @attributes['start_date'].present? if self.due_date and self.start_date and self.due_date < self.start_date
errors.add :start_date, :not_a_date
end
if due_date && start_date && due_date < start_date
errors.add :due_date, :greater_than_start_date errors.add :due_date, :greater_than_start_date
end end
@@ -558,11 +532,9 @@ class Issue < ActiveRecord::Base
end end
# Checks parent issue assignment # Checks parent issue assignment
if @invalid_parent_issue_id.present? if @parent_issue
errors.add :parent_issue_id, :invalid if @parent_issue.project_id != project_id
elsif @parent_issue errors.add :parent_issue_id, :not_same_project
if !valid_parent_project?(@parent_issue)
errors.add :parent_issue_id, :invalid
elsif !new_record? elsif !new_record?
# moving an existing issue # moving an existing issue
if @parent_issue.root_id != root_id if @parent_issue.root_id != root_id
@@ -570,7 +542,7 @@ class Issue < ActiveRecord::Base
elsif move_possible?(@parent_issue) elsif move_possible?(@parent_issue)
# move accepted inside tree # move accepted inside tree
else else
errors.add :parent_issue_id, :invalid errors.add :parent_issue_id, :not_a_valid_parent
end end
end end
end end
@@ -743,8 +715,8 @@ class Issue < ActiveRecord::Base
end end
end end
# Returns the users that should be notified # Returns the mail adresses of users that should be notified
def notified_users def recipients
notified = [] notified = []
# Author and assignee are always notified unless they have been # Author and assignee are always notified unless they have been
# locked or don't want to be notified # locked or don't want to be notified
@@ -761,12 +733,7 @@ class Issue < ActiveRecord::Base
notified.uniq! notified.uniq!
# Remove users that can not view the issue # Remove users that can not view the issue
notified.reject! {|user| !visible?(user)} notified.reject! {|user| !visible?(user)}
notified notified.collect(&:mail)
end
# Returns the email addresses that should be notified
def recipients
notified_users.collect(&:mail)
end end
# Returns the number of hours spent on this issue # Returns the number of hours spent on this issue
@@ -785,7 +752,7 @@ class Issue < ActiveRecord::Base
end end
def relations def relations
@relations ||= IssueRelations.new(self, (relations_from + relations_to).sort) @relations ||= (relations_from + relations_to).sort
end end
# Preloads relations for a collection of issues # Preloads relations for a collection of issues
@@ -808,25 +775,6 @@ class Issue < ActiveRecord::Base
end end
end end
# Preloads visible relations for a collection of issues
def self.load_visible_relations(issues, user=User.current)
if issues.any?
issue_ids = issues.map(&:id)
# Relations with issue_from in given issues and visible issue_to
relations_from = IssueRelation.includes(:issue_to => [:status, :project]).where(visible_condition(user)).where(:issue_from_id => issue_ids).all
# Relations with issue_to in given issues and visible issue_from
relations_to = IssueRelation.includes(:issue_from => [:status, :project]).where(visible_condition(user)).where(:issue_to_id => issue_ids).all
issues.each do |issue|
relations =
relations_from.select {|relation| relation.issue_from_id == issue.id} +
relations_to.select {|relation| relation.issue_to_id == issue.id}
issue.instance_variable_set "@relations", IssueRelations.new(issue, relations.sort)
end
end
end
# Finds an issue relation given its id. # Finds an issue relation given its id.
def find_relation(relation_id) def find_relation(relation_id)
IssueRelation.find(relation_id, :conditions => ["issue_to_id = ? OR issue_from_id = ?", id, id]) IssueRelation.find(relation_id, :conditions => ["issue_to_id = ? OR issue_from_id = ?", id, id])
@@ -864,58 +812,29 @@ class Issue < ActiveRecord::Base
(start_date && due_date) ? due_date - start_date : 0 (start_date && due_date) ? due_date - start_date : 0
end end
# Returns the duration in working days def soonest_start
def working_duration
(start_date && due_date) ? working_days(start_date, due_date) : 0
end
def soonest_start(reload=false)
@soonest_start = nil if reload
@soonest_start ||= ( @soonest_start ||= (
relations_to(reload).collect{|relation| relation.successor_soonest_start} + relations_to.collect{|relation| relation.successor_soonest_start} +
ancestors.collect(&:soonest_start) ancestors.collect(&:soonest_start)
).compact.max ).compact.max
end end
# Sets start_date on the given date or the next working day def reschedule_after(date)
# and changes due_date to keep the same working duration.
def reschedule_on(date)
wd = working_duration
date = next_working_date(date)
self.start_date = date
self.due_date = add_working_days(date, wd)
end
# Reschedules the issue on the given date or the next working day and saves the record.
# If the issue is a parent task, this is done by rescheduling its subtasks.
def reschedule_on!(date)
return if date.nil? return if date.nil?
if leaf? if leaf?
if start_date.nil? || start_date != date if start_date.nil? || start_date < date
if start_date && start_date > date self.start_date, self.due_date = date, date + duration
# Issue can not be moved earlier than its soonest start date
date = [soonest_start(true), date].compact.max
end
reschedule_on(date)
begin begin
save save
rescue ActiveRecord::StaleObjectError rescue ActiveRecord::StaleObjectError
reload reload
reschedule_on(date) self.start_date, self.due_date = date, date + duration
save save
end end
end end
else else
leaves.each do |leaf| leaves.each do |leaf|
if leaf.start_date leaf.reschedule_after(date)
# Only move subtask if it starts at the same date as the parent
# or if it starts before the given date
if start_date == leaf.start_date || date > leaf.start_date
leaf.reschedule_on!(date)
end
else
leaf.reschedule_on!(date)
end
end end
end end
end end
@@ -936,7 +855,7 @@ class Issue < ActiveRecord::Base
# Returns a string of css classes that apply to the issue # Returns a string of css classes that apply to the issue
def css_classes def css_classes
s = "issue status-#{status_id} #{priority.try(:css_classes)}" s = "issue status-#{status_id} priority-#{priority_id}"
s << ' closed' if closed? s << ' closed' if closed?
s << ' overdue' if overdue? s << ' overdue' if overdue?
s << ' child' if child? s << ' child' if child?
@@ -986,44 +905,23 @@ class Issue < ActiveRecord::Base
end end
def parent_issue_id=(arg) def parent_issue_id=(arg)
s = arg.to_s.strip.presence parent_issue_id = arg.blank? ? nil : arg.to_i
if s && (m = s.match(%r{\A#?(\d+)\z})) && (@parent_issue = Issue.find_by_id(m[1])) if parent_issue_id && @parent_issue = Issue.find_by_id(parent_issue_id)
@parent_issue.id @parent_issue.id
else else
@parent_issue = nil @parent_issue = nil
@invalid_parent_issue_id = arg nil
end end
end end
def parent_issue_id def parent_issue_id
if @invalid_parent_issue_id if instance_variable_defined? :@parent_issue
@invalid_parent_issue_id
elsif instance_variable_defined? :@parent_issue
@parent_issue.nil? ? nil : @parent_issue.id @parent_issue.nil? ? nil : @parent_issue.id
else else
parent_id parent_id
end end
end end
# Returns true if issue's project is a valid
# parent issue project
def valid_parent_project?(issue=parent)
return true if issue.nil? || issue.project_id == project_id
case Setting.cross_project_subtasks
when 'system'
true
when 'tree'
issue.project.root == project.root
when 'hierarchy'
issue.project.is_or_is_ancestor_of?(project) || issue.project.is_descendant_of?(project)
when 'descendants'
issue.project.is_or_is_ancestor_of?(project)
else
false
end
end
# Extracted from the ReportsController. # Extracted from the ReportsController.
def self.by_tracker(project) def self.by_tracker(project)
count_and_group_by(:project => project, count_and_group_by(:project => project,
@@ -1103,9 +1001,8 @@ class Issue < ActiveRecord::Base
relations_to.clear relations_to.clear
end end
# Move subtasks that were in the same project # Move subtasks
children.each do |child| children.each do |child|
next unless child.project_id == project_id_was
# Change project and keep project # Change project and keep project
child.send :project=, project, true child.send :project=, project, true
unless child.save unless child.save
@@ -1114,20 +1011,11 @@ class Issue < ActiveRecord::Base
end end
end end
# Callback for after the creation of an issue by copy # Copies subtasks from the copied issue
# * adds a "copied to" relation with the copied issue
# * copies subtasks from the copied issue
def after_create_from_copy def after_create_from_copy
return unless copy? && !@after_create_from_copy_handled return unless copy?
if (@copied_from.project_id == project_id || Setting.cross_project_issue_relations?) && @copy_options[:link] != false unless @copied_from.leaf? || @copy_options[:subtasks] == false || @subtasks_copied
relation = IssueRelation.new(:issue_from => @copied_from, :issue_to => self, :relation_type => IssueRelation::TYPE_COPIED_TO)
unless relation.save
logger.error "Could not create relation while copying ##{@copied_from.id} to ##{id} due to validation errors: #{relation.errors.full_messages.join(', ')}" if logger
end
end
unless @copied_from.leaf? || @copy_options[:subtasks] == false
@copied_from.children.each do |child| @copied_from.children.each do |child|
unless child.visible? unless child.visible?
# Do not copy subtasks that are not visible to avoid potential disclosure of private data # Do not copy subtasks that are not visible to avoid potential disclosure of private data
@@ -1143,8 +1031,8 @@ class Issue < ActiveRecord::Base
logger.error "Could not copy subtask ##{child.id} while copying ##{@copied_from.id} to ##{id} due to validation errors: #{copy.errors.full_messages.join(', ')}" if logger logger.error "Could not copy subtask ##{child.id} while copying ##{@copied_from.id} to ##{id} due to validation errors: #{copy.errors.full_messages.join(', ')}" if logger
end end
end end
@subtasks_copied = true
end end
@after_create_from_copy_handled = true
end end
def update_nested_set_attributes def update_nested_set_attributes
@@ -1255,7 +1143,7 @@ class Issue < ActiveRecord::Base
end end
end end
# Callback on file attachment # Callback on attachment deletion
def attachment_added(obj) def attachment_added(obj)
if @current_journal && !obj.new_record? if @current_journal && !obj.new_record?
@current_journal.details << JournalDetail.new(:property => 'attachment', :prop_key => obj.id, :value => obj.filename) @current_journal.details << JournalDetail.new(:property => 'attachment', :prop_key => obj.id, :value => obj.filename)
+1 -1
View File
@@ -27,7 +27,7 @@ class IssueCategory < ActiveRecord::Base
safe_attributes 'name', 'assigned_to_id' safe_attributes 'name', 'assigned_to_id'
scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)} scope :named, lambda {|arg| { :conditions => ["LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip]}}
alias :destroy_without_reassign :destroy alias :destroy_without_reassign :destroy
-34
View File
@@ -18,9 +18,6 @@
class IssuePriority < Enumeration class IssuePriority < Enumeration
has_many :issues, :foreign_key => 'priority_id' has_many :issues, :foreign_key => 'priority_id'
after_destroy {|priority| priority.class.compute_position_names}
after_save {|priority| priority.class.compute_position_names if priority.position_changed? && priority.position}
OptionName = :enumeration_issue_priorities OptionName = :enumeration_issue_priorities
def option_name def option_name
@@ -34,35 +31,4 @@ class IssuePriority < Enumeration
def transfer_relations(to) def transfer_relations(to)
issues.update_all("priority_id = #{to.id}") issues.update_all("priority_id = #{to.id}")
end end
def css_classes
"priority-#{id} priority-#{position_name}"
end
# Clears position_name for all priorities
# Called from migration 20121026003537_populate_enumerations_position_name
def self.clear_position_names
update_all :position_name => nil
end
# Updates position_name for active priorities
# Called from migration 20121026003537_populate_enumerations_position_name
def self.compute_position_names
priorities = where(:active => true).all.sort_by(&:position)
if priorities.any?
default = priorities.detect(&:is_default?) || priorities[(priorities.size - 1) / 2]
priorities.each_with_index do |priority, index|
name = case
when priority.position == default.position
"default"
when priority.position < default.position
index == 0 ? "lowest" : "low#{index+1}"
else
index == (priorities.size - 1) ? "highest" : "high#{priorities.size - index}"
end
update_all({:position_name => name}, :id => priority.id)
end
end
end
end end
+17 -56
View File
@@ -15,20 +15,6 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# Class used to represent the relations of an issue
class IssueRelations < Array
include Redmine::I18n
def initialize(issue, *args)
@issue = issue
super(*args)
end
def to_s(*args)
map {|relation| "#{l(relation.label_for(@issue))} ##{relation.other_issue(@issue).id}"}.join(', ')
end
end
class IssueRelation < ActiveRecord::Base class IssueRelation < ActiveRecord::Base
belongs_to :issue_from, :class_name => 'Issue', :foreign_key => 'issue_from_id' belongs_to :issue_from, :class_name => 'Issue', :foreign_key => 'issue_from_id'
belongs_to :issue_to, :class_name => 'Issue', :foreign_key => 'issue_to_id' belongs_to :issue_to, :class_name => 'Issue', :foreign_key => 'issue_to_id'
@@ -40,37 +26,25 @@ class IssueRelation < ActiveRecord::Base
TYPE_BLOCKED = "blocked" TYPE_BLOCKED = "blocked"
TYPE_PRECEDES = "precedes" TYPE_PRECEDES = "precedes"
TYPE_FOLLOWS = "follows" TYPE_FOLLOWS = "follows"
TYPE_COPIED_TO = "copied_to"
TYPE_COPIED_FROM = "copied_from"
TYPES = { TYPES = { TYPE_RELATES => { :name => :label_relates_to, :sym_name => :label_relates_to, :order => 1, :sym => TYPE_RELATES },
TYPE_RELATES => { :name => :label_relates_to, :sym_name => :label_relates_to, TYPE_DUPLICATES => { :name => :label_duplicates, :sym_name => :label_duplicated_by, :order => 2, :sym => TYPE_DUPLICATED },
:order => 1, :sym => TYPE_RELATES }, TYPE_DUPLICATED => { :name => :label_duplicated_by, :sym_name => :label_duplicates, :order => 3, :sym => TYPE_DUPLICATES, :reverse => TYPE_DUPLICATES },
TYPE_DUPLICATES => { :name => :label_duplicates, :sym_name => :label_duplicated_by, TYPE_BLOCKS => { :name => :label_blocks, :sym_name => :label_blocked_by, :order => 4, :sym => TYPE_BLOCKED },
:order => 2, :sym => TYPE_DUPLICATED }, TYPE_BLOCKED => { :name => :label_blocked_by, :sym_name => :label_blocks, :order => 5, :sym => TYPE_BLOCKS, :reverse => TYPE_BLOCKS },
TYPE_DUPLICATED => { :name => :label_duplicated_by, :sym_name => :label_duplicates, TYPE_PRECEDES => { :name => :label_precedes, :sym_name => :label_follows, :order => 6, :sym => TYPE_FOLLOWS },
:order => 3, :sym => TYPE_DUPLICATES, :reverse => TYPE_DUPLICATES }, TYPE_FOLLOWS => { :name => :label_follows, :sym_name => :label_precedes, :order => 7, :sym => TYPE_PRECEDES, :reverse => TYPE_PRECEDES }
TYPE_BLOCKS => { :name => :label_blocks, :sym_name => :label_blocked_by, }.freeze
:order => 4, :sym => TYPE_BLOCKED },
TYPE_BLOCKED => { :name => :label_blocked_by, :sym_name => :label_blocks,
:order => 5, :sym => TYPE_BLOCKS, :reverse => TYPE_BLOCKS },
TYPE_PRECEDES => { :name => :label_precedes, :sym_name => :label_follows,
:order => 6, :sym => TYPE_FOLLOWS },
TYPE_FOLLOWS => { :name => :label_follows, :sym_name => :label_precedes,
:order => 7, :sym => TYPE_PRECEDES, :reverse => TYPE_PRECEDES },
TYPE_COPIED_TO => { :name => :label_copied_to, :sym_name => :label_copied_from,
:order => 8, :sym => TYPE_COPIED_FROM },
TYPE_COPIED_FROM => { :name => :label_copied_from, :sym_name => :label_copied_to,
:order => 9, :sym => TYPE_COPIED_TO, :reverse => TYPE_COPIED_TO }
}.freeze
validates_presence_of :issue_from, :issue_to, :relation_type validates_presence_of :issue_from, :issue_to, :relation_type
validates_inclusion_of :relation_type, :in => TYPES.keys validates_inclusion_of :relation_type, :in => TYPES.keys
validates_numericality_of :delay, :allow_nil => true validates_numericality_of :delay, :allow_nil => true
validates_uniqueness_of :issue_to_id, :scope => :issue_from_id validates_uniqueness_of :issue_to_id, :scope => :issue_from_id
validate :validate_issue_relation validate :validate_issue_relation
attr_protected :issue_from_id, :issue_to_id attr_protected :issue_from_id, :issue_to_id
before_save :handle_issue_order before_save :handle_issue_order
def visible?(user=User.current) def visible?(user=User.current)
@@ -95,19 +69,14 @@ class IssueRelation < ActiveRecord::Base
def validate_issue_relation def validate_issue_relation
if issue_from && issue_to if issue_from && issue_to
errors.add :issue_to_id, :invalid if issue_from_id == issue_to_id errors.add :issue_to_id, :invalid if issue_from_id == issue_to_id
unless issue_from.project_id == issue_to.project_id || errors.add :issue_to_id, :not_same_project unless issue_from.project_id == issue_to.project_id || Setting.cross_project_issue_relations?
Setting.cross_project_issue_relations? #detect circular dependencies depending wether the relation should be reversed
errors.add :issue_to_id, :not_same_project
end
# detect circular dependencies depending wether the relation should be reversed
if TYPES.has_key?(relation_type) && TYPES[relation_type][:reverse] if TYPES.has_key?(relation_type) && TYPES[relation_type][:reverse]
errors.add :base, :circular_dependency if issue_from.all_dependent_issues.include? issue_to errors.add :base, :circular_dependency if issue_from.all_dependent_issues.include? issue_to
else else
errors.add :base, :circular_dependency if issue_to.all_dependent_issues.include? issue_from errors.add :base, :circular_dependency if issue_to.all_dependent_issues.include? issue_from
end end
if issue_from.is_descendant_of?(issue_to) || issue_from.is_ancestor_of?(issue_to) errors.add :base, :cant_link_an_issue_with_a_descendant if issue_from.is_descendant_of?(issue_to) || issue_from.is_ancestor_of?(issue_to)
errors.add :base, :cant_link_an_issue_with_a_descendant
end
end end
end end
@@ -127,13 +96,7 @@ class IssueRelation < ActiveRecord::Base
end end
def label_for(issue) def label_for(issue)
TYPES[relation_type] ? TYPES[relation_type] ? TYPES[relation_type][(self.issue_from_id == issue.id) ? :name : :sym_name] : :unknow
TYPES[relation_type][(self.issue_from_id == issue.id) ? :name : :sym_name] :
:unknow
end
def css_classes_for(issue)
"rel-#{relation_type_for(issue)}"
end end
def handle_issue_order def handle_issue_order
@@ -150,20 +113,18 @@ class IssueRelation < ActiveRecord::Base
def set_issue_to_dates def set_issue_to_dates
soonest_start = self.successor_soonest_start soonest_start = self.successor_soonest_start
if soonest_start && issue_to if soonest_start && issue_to
issue_to.reschedule_on!(soonest_start) issue_to.reschedule_after(soonest_start)
end end
end end
def successor_soonest_start def successor_soonest_start
if (TYPE_PRECEDES == self.relation_type) && delay && issue_from && if (TYPE_PRECEDES == self.relation_type) && delay && issue_from && (issue_from.start_date || issue_from.due_date)
(issue_from.start_date || issue_from.due_date)
(issue_from.due_date || issue_from.start_date) + 1 + delay (issue_from.due_date || issue_from.start_date) + 1 + delay
end end
end end
def <=>(relation) def <=>(relation)
r = TYPES[self.relation_type][:order] <=> TYPES[relation.relation_type][:order] TYPES[self.relation_type][:order] <=> TYPES[relation.relation_type][:order]
r == 0 ? id <=> relation.id : r
end end
private private
+1 -1
View File
@@ -29,7 +29,7 @@ class IssueStatus < ActiveRecord::Base
validates_inclusion_of :default_done_ratio, :in => 0..100, :allow_nil => true validates_inclusion_of :default_done_ratio, :in => 0..100, :allow_nil => true
scope :sorted, order("#{table_name}.position ASC") scope :sorted, order("#{table_name}.position ASC")
scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)} scope :named, lambda {|arg| where(["LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip])}
def update_default def update_default
IssueStatus.update_all({:is_default => false}, ['id <> ?', id]) if self.is_default? IssueStatus.update_all({:is_default => false}, ['id <> ?', id]) if self.is_default?
+4 -47
View File
@@ -37,15 +37,10 @@ class Journal < ActiveRecord::Base
:conditions => "#{Journal.table_name}.journalized_type = 'Issue' AND" + :conditions => "#{Journal.table_name}.journalized_type = 'Issue' AND" +
" (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')"} " (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')"}
before_create :split_private_notes scope :visible, lambda {|*args| {
:include => {:issue => :project},
scope :visible, lambda {|*args| :conditions => Issue.visible_condition(args.shift || User.current, *args)
user = args.shift || User.current }}
includes(:issue => :project).
where(Issue.visible_condition(user, *args)).
where("(#{Journal.table_name}.private_notes = ? OR (#{Project.allowed_to_condition(user, :view_private_notes, *args)}))", false)
}
def save(*args) def save(*args)
# Do not save an empty journal # Do not save an empty journal
@@ -80,7 +75,6 @@ class Journal < ActiveRecord::Base
s = 'journal' s = 'journal'
s << ' has-notes' unless notes.blank? s << ' has-notes' unless notes.blank?
s << ' has-details' unless details.blank? s << ' has-details' unless details.blank?
s << ' private-notes' if private_notes?
s s
end end
@@ -91,41 +85,4 @@ class Journal < ActiveRecord::Base
def notify=(arg) def notify=(arg)
@notify = arg @notify = arg
end end
def recipients
notified = journalized.notified_users
if private_notes?
notified = notified.select {|user| user.allowed_to?(:view_private_notes, journalized.project)}
end
notified.map(&:mail)
end
def watcher_recipients
notified = journalized.notified_watchers
if private_notes?
notified = notified.select {|user| user.allowed_to?(:view_private_notes, journalized.project)}
end
notified.map(&:mail)
end
private
def split_private_notes
if private_notes?
if notes.present?
if details.any?
# Split the journal (notes/changes) so we don't have half-private journals
journal = Journal.new(:journalized => journalized, :user => user, :notes => nil, :private_notes => false)
journal.details = details
journal.save
self.details = []
self.created_on = journal.created_on
end
else
# Blank notes should not be private
self.private_notes = false
end
end
true
end
end end
+33 -7
View File
@@ -182,7 +182,7 @@ class MailHandler < ActionMailer::Base
end end
# Adds a note to an existing issue # Adds a note to an existing issue
def receive_issue_reply(issue_id, from_journal=nil) def receive_issue_reply(issue_id)
issue = Issue.find_by_id(issue_id) issue = Issue.find_by_id(issue_id)
return unless issue return unless issue
# check permission # check permission
@@ -197,10 +197,6 @@ class MailHandler < ActionMailer::Base
@@handler_options[:issue].clear @@handler_options[:issue].clear
journal = issue.init_journal(user) journal = issue.init_journal(user)
if from_journal && from_journal.private_notes?
# If the received email was a reply to a private note, make the added note private
issue.private_notes = true
end
issue.safe_attributes = issue_attributes_from_keywords(issue) issue.safe_attributes = issue_attributes_from_keywords(issue)
issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)} issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
journal.notes = cleaned_up_text_body journal.notes = cleaned_up_text_body
@@ -216,7 +212,7 @@ class MailHandler < ActionMailer::Base
def receive_journal_reply(journal_id) def receive_journal_reply(journal_id)
journal = Journal.find_by_id(journal_id) journal = Journal.find_by_id(journal_id)
if journal && journal.journalized_type == 'Issue' if journal && journal.journalized_type == 'Issue'
receive_issue_reply(journal.journalized_id, journal) receive_issue_reply(journal.journalized_id)
end end
end end
@@ -249,9 +245,26 @@ class MailHandler < ActionMailer::Base
def add_attachments(obj) def add_attachments(obj)
if email.attachments && email.attachments.any? if email.attachments && email.attachments.any?
email.attachments.each do |attachment| email.attachments.each do |attachment|
filename = attachment.filename
unless filename.respond_to?(:encoding)
# try to reencode to utf8 manually with ruby1.8
h = attachment.header['Content-Disposition']
unless h.nil?
begin
if m = h.value.match(/filename\*[0-9\*]*=([^=']+)'/)
filename = Redmine::CodesetUtil.to_utf8(filename, m[1])
elsif m = h.value.match(/filename=.*=\?([^\?]+)\?[BbQq]\?/)
# http://tools.ietf.org/html/rfc2047#section-4
filename = Redmine::CodesetUtil.to_utf8(filename, m[1])
end
rescue
# nop
end
end
end
obj.attachments << Attachment.create(:container => obj, obj.attachments << Attachment.create(:container => obj,
:file => attachment.decoded, :file => attachment.decoded,
:filename => attachment.filename, :filename => filename,
:author => user, :author => user,
:content_type => attachment.mime_type) :content_type => attachment.mime_type)
end end
@@ -374,6 +387,19 @@ class MailHandler < ActionMailer::Base
def cleaned_up_subject def cleaned_up_subject
subject = email.subject.to_s subject = email.subject.to_s
unless subject.respond_to?(:encoding)
# try to reencode to utf8 manually with ruby1.8
begin
if h = email.header[:subject]
# http://tools.ietf.org/html/rfc2047#section-4
if m = h.value.match(/=\?([^\?]+)\?[BbQq]\?/)
subject = Redmine::CodesetUtil.to_utf8(subject, m[1])
end
end
rescue
# nop
end
end
subject.strip[0,255] subject.strip[0,255]
end end
+2 -2
View File
@@ -62,9 +62,9 @@ class Mailer < ActionMailer::Base
message_id journal message_id journal
references issue references issue
@author = journal.user @author = journal.user
recipients = journal.recipients recipients = issue.recipients
# Watchers in cc # Watchers in cc
cc = journal.watcher_recipients - recipients cc = issue.watcher_recipients - recipients
s = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] " s = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] "
s << "(#{issue.status.name}) " if journal.new_value_for('status_id') s << "(#{issue.status.name}) " if journal.new_value_for('status_id')
s << issue.subject s << issue.subject
+9 -10
View File
@@ -27,20 +27,19 @@ class Principal < ActiveRecord::Base
scope :active, :conditions => "#{Principal.table_name}.status = 1" scope :active, :conditions => "#{Principal.table_name}.status = 1"
scope :like, lambda {|q| scope :like, lambda {|q|
q = q.to_s
if q.blank? if q.blank?
where({}) {}
else else
q = q.to_s
pattern = "%#{q}%" pattern = "%#{q}%"
sql = %w(login firstname lastname mail).map {|column| "LOWER(#{table_name}.#{column}) LIKE LOWER(:p)"}.join(" OR ") sql = "LOWER(login) LIKE LOWER(:p) OR LOWER(firstname) LIKE LOWER(:p) OR LOWER(lastname) LIKE LOWER(:p) OR LOWER(mail) LIKE LOWER(:p)"
params = {:p => pattern} params = {:p => pattern}
if q =~ /^(.+)\s+(.+)$/ if q =~ /^(.+)\s+(.+)$/
a, b = "#{$1}%", "#{$2}%" a, b = "#{$1}%", "#{$2}%"
sql << " OR (LOWER(#{table_name}.firstname) LIKE LOWER(:a) AND LOWER(#{table_name}.lastname) LIKE LOWER(:b))" sql << " OR (LOWER(firstname) LIKE LOWER(:a) AND LOWER(lastname) LIKE LOWER(:b)) OR (LOWER(firstname) LIKE LOWER(:b) AND LOWER(lastname) LIKE LOWER(:a))"
sql << " OR (LOWER(#{table_name}.firstname) LIKE LOWER(:b) AND LOWER(#{table_name}.lastname) LIKE LOWER(:a))"
params.merge!(:a => a, :b => b) params.merge!(:a => a, :b => b)
end end
where(sql, params) {:conditions => [sql, params]}
end end
} }
@@ -48,20 +47,20 @@ class Principal < ActiveRecord::Base
scope :member_of, lambda {|projects| scope :member_of, lambda {|projects|
projects = [projects] unless projects.is_a?(Array) projects = [projects] unless projects.is_a?(Array)
if projects.empty? if projects.empty?
where("1=0") {:conditions => "1=0"}
else else
ids = projects.map(&:id) ids = projects.map(&:id)
where("#{Principal.table_name}.status = 1 AND #{Principal.table_name}.id IN (SELECT DISTINCT user_id FROM #{Member.table_name} WHERE project_id IN (?))", ids) {:conditions => ["#{Principal.table_name}.status = 1 AND #{Principal.table_name}.id IN (SELECT DISTINCT user_id FROM #{Member.table_name} WHERE project_id IN (?))", ids]}
end end
} }
# Principals that are not members of projects # Principals that are not members of projects
scope :not_member_of, lambda {|projects| scope :not_member_of, lambda {|projects|
projects = [projects] unless projects.is_a?(Array) projects = [projects] unless projects.is_a?(Array)
if projects.empty? if projects.empty?
where("1=0") {:conditions => "1=0"}
else else
ids = projects.map(&:id) ids = projects.map(&:id)
where("#{Principal.table_name}.id NOT IN (SELECT DISTINCT user_id FROM #{Member.table_name} WHERE project_id IN (?))", ids) {:conditions => ["#{Principal.table_name}.id NOT IN (SELECT DISTINCT user_id FROM #{Member.table_name} WHERE project_id IN (?))", ids]}
end end
} }
+4 -16
View File
@@ -28,7 +28,7 @@ class Project < ActiveRecord::Base
# Specific overidden Activities # Specific overidden Activities
has_many :time_entry_activities has_many :time_entry_activities
has_many :members, :include => [:principal, :roles], :conditions => "#{User.table_name}.type='User' AND #{User.table_name}.status=#{User::STATUS_ACTIVE}" has_many :members, :include => [:user, :roles], :conditions => "#{User.table_name}.type='User' AND #{User.table_name}.status=#{User::STATUS_ACTIVE}"
has_many :memberships, :class_name => 'Member' has_many :memberships, :class_name => 'Member'
has_many :member_principals, :class_name => 'Member', has_many :member_principals, :class_name => 'Member',
:include => :principal, :include => :principal,
@@ -393,16 +393,6 @@ class Project < ActiveRecord::Base
end end
end end
# Recalculates all lft and rgt values based on project names
# Unlike Project.rebuild!, these values are recalculated even if the tree "looks" valid
# Used in BuildProjectsTree migration
def self.rebuild_tree!
transaction do
update_all "lft = NULL, rgt = NULL"
rebuild!(false)
end
end
# Returns an array of the trackers used by the project and its active sub projects # Returns an array of the trackers used by the project and its active sub projects
def rolled_up_trackers def rolled_up_trackers
@rolled_up_trackers ||= @rolled_up_trackers ||=
@@ -482,7 +472,7 @@ class Project < ActiveRecord::Base
# Returns the users that should be notified on project events # Returns the users that should be notified on project events
def notified_users def notified_users
# TODO: User part should be extracted to User#notify_about? # TODO: User part should be extracted to User#notify_about?
members.select {|m| m.principal.present? && (m.mail_notification? || m.principal.mail_notification == 'all')}.collect {|m| m.principal} members.select {|m| m.mail_notification? || m.user.mail_notification == 'all'}.collect {|m| m.user}
end end
# Returns an array of all custom fields enabled for project issues # Returns an array of all custom fields enabled for project issues
@@ -731,7 +721,7 @@ class Project < ActiveRecord::Base
def copy_wiki(project) def copy_wiki(project)
# Check that the source project has a wiki first # Check that the source project has a wiki first
unless project.wiki.nil? unless project.wiki.nil?
wiki = self.wiki || Wiki.new self.wiki ||= Wiki.new
wiki.attributes = project.wiki.attributes.dup.except("id", "project_id") wiki.attributes = project.wiki.attributes.dup.except("id", "project_id")
wiki_pages_map = {} wiki_pages_map = {}
project.wiki.pages.each do |page| project.wiki.pages.each do |page|
@@ -743,8 +733,6 @@ class Project < ActiveRecord::Base
wiki.pages << new_wiki_page wiki.pages << new_wiki_page
wiki_pages_map[page.id] = new_wiki_page wiki_pages_map[page.id] = new_wiki_page
end end
self.wiki = wiki
wiki.save wiki.save
# Reproduce page hierarchy # Reproduce page hierarchy
project.wiki.pages.each do |page| project.wiki.pages.each do |page|
@@ -790,7 +778,7 @@ class Project < ActiveRecord::Base
# get copied before their children # get copied before their children
project.issues.find(:all, :order => 'root_id, lft').each do |issue| project.issues.find(:all, :order => 'root_id, lft').each do |issue|
new_issue = Issue.new new_issue = Issue.new
new_issue.copy_from(issue, :subtasks => false, :link => false) new_issue.copy_from(issue, :subtasks => false)
new_issue.project = self new_issue.project = self
# Reassign fixed_versions by name, since names are unique per project # Reassign fixed_versions by name, since names are unique per project
if issue.fixed_version && issue.fixed_version.project == project if issue.fixed_version && issue.fixed_version.project == project
+51 -196
View File
@@ -27,7 +27,6 @@ class QueryColumn
self.groupable = name.to_s self.groupable = name.to_s
end end
self.default_order = options[:default_order] self.default_order = options[:default_order]
@inline = options.key?(:inline) ? options[:inline] : true
@caption_key = options[:caption] || "field_#{name}" @caption_key = options[:caption] || "field_#{name}"
end end
@@ -44,10 +43,6 @@ class QueryColumn
@sortable.is_a?(Proc) ? @sortable.call : @sortable @sortable.is_a?(Proc) ? @sortable.call : @sortable
end end
def inline?
@inline
end
def value(issue) def value(issue)
issue.send name issue.send name
end end
@@ -63,7 +58,6 @@ class QueryCustomFieldColumn < QueryColumn
self.name = "cf_#{custom_field.id}".to_sym self.name = "cf_#{custom_field.id}".to_sym
self.sortable = custom_field.order_statement || false self.sortable = custom_field.order_statement || false
self.groupable = custom_field.group_statement || false self.groupable = custom_field.group_statement || false
@inline = true
@cf = custom_field @cf = custom_field
end end
@@ -77,7 +71,7 @@ class QueryCustomFieldColumn < QueryColumn
def value(issue) def value(issue)
cv = issue.custom_values.select {|v| v.custom_field_id == @cf.id}.collect {|v| @cf.cast_value(v.value)} cv = issue.custom_values.select {|v| v.custom_field_id == @cf.id}.collect {|v| @cf.cast_value(v.value)}
cv.size > 1 ? cv.sort {|a,b| a.to_s <=> b.to_s} : cv.first cv.size > 1 ? cv : cv.first
end end
def css_classes def css_classes
@@ -106,25 +100,20 @@ class Query < ActiveRecord::Base
"o" => :label_open_issues, "o" => :label_open_issues,
"c" => :label_closed_issues, "c" => :label_closed_issues,
"!*" => :label_none, "!*" => :label_none,
"*" => :label_any, "*" => :label_all,
">=" => :label_greater_or_equal, ">=" => :label_greater_or_equal,
"<=" => :label_less_or_equal, "<=" => :label_less_or_equal,
"><" => :label_between, "><" => :label_between,
"<t+" => :label_in_less_than, "<t+" => :label_in_less_than,
">t+" => :label_in_more_than, ">t+" => :label_in_more_than,
"><t+"=> :label_in_the_next_days,
"t+" => :label_in, "t+" => :label_in,
"t" => :label_today, "t" => :label_today,
"w" => :label_this_week, "w" => :label_this_week,
">t-" => :label_less_than_ago, ">t-" => :label_less_than_ago,
"<t-" => :label_more_than_ago, "<t-" => :label_more_than_ago,
"><t-"=> :label_in_the_past_days,
"t-" => :label_ago, "t-" => :label_ago,
"~" => :label_contains, "~" => :label_contains,
"!~" => :label_not_contains, "!~" => :label_not_contains }
"=p" => :label_any_issues_in_project,
"=!p" => :label_any_issues_not_in_project,
"!p" => :label_no_issues_in_project}
cattr_reader :operators cattr_reader :operators
@@ -132,13 +121,12 @@ class Query < ActiveRecord::Base
:list_status => [ "o", "=", "!", "c", "*" ], :list_status => [ "o", "=", "!", "c", "*" ],
:list_optional => [ "=", "!", "!*", "*" ], :list_optional => [ "=", "!", "!*", "*" ],
:list_subprojects => [ "*", "!*", "=" ], :list_subprojects => [ "*", "!*", "=" ],
:date => [ "=", ">=", "<=", "><", "<t+", ">t+", "><t+", "t+", "t", "w", ">t-", "<t-", "><t-", "t-", "!*", "*" ], :date => [ "=", ">=", "<=", "><", "<t+", ">t+", "t+", "t", "w", ">t-", "<t-", "t-", "!*", "*" ],
:date_past => [ "=", ">=", "<=", "><", ">t-", "<t-", "><t-", "t-", "t", "w", "!*", "*" ], :date_past => [ "=", ">=", "<=", "><", ">t-", "<t-", "t-", "t", "w", "!*", "*" ],
:string => [ "=", "~", "!", "!~", "!*", "*" ], :string => [ "=", "~", "!", "!~", "!*", "*" ],
:text => [ "~", "!~", "!*", "*" ], :text => [ "~", "!~", "!*", "*" ],
:integer => [ "=", ">=", "<=", "><", "!*", "*" ], :integer => [ "=", ">=", "<=", "><", "!*", "*" ],
:float => [ "=", ">=", "<=", "><", "!*", "*" ], :float => [ "=", ">=", "<=", "><", "!*", "*" ] }
:relation => ["=", "=p", "=!p", "!p", "!*", "*"]}
cattr_reader :operators_by_filter_type cattr_reader :operators_by_filter_type
@@ -159,8 +147,6 @@ class Query < ActiveRecord::Base
QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours"), QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours"),
QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio", :groupable => true), QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio", :groupable => true),
QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'), QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'),
QueryColumn.new(:relations, :caption => :label_related_issues),
QueryColumn.new(:description, :inline => false)
] ]
cattr_reader :available_columns cattr_reader :available_columns
@@ -192,7 +178,7 @@ class Query < ActiveRecord::Base
case operator_for(field) case operator_for(field)
when "=", ">=", "<=", "><" when "=", ">=", "<=", "><"
add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && (!v.match(/^\d{4}-\d{2}-\d{2}$/) || (Date.parse(v) rescue nil).nil?) } add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && (!v.match(/^\d{4}-\d{2}-\d{2}$/) || (Date.parse(v) rescue nil).nil?) }
when ">t-", "<t-", "t-", ">t+", "<t+", "t+", "><t+", "><t-" when ">t-", "<t-", "t-"
add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !v.match(/^\d+$/) } add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !v.match(/^\d+$/) }
end end
end end
@@ -235,57 +221,44 @@ class Query < ActiveRecord::Base
def available_filters def available_filters
return @available_filters if @available_filters return @available_filters if @available_filters
@available_filters = {
"status_id" => { @available_filters = { "status_id" => { :type => :list_status, :order => 1, :values => IssueStatus.find(:all, :order => 'position').collect{|s| [s.name, s.id.to_s] } },
:type => :list_status, :order => 0, "tracker_id" => { :type => :list, :order => 2, :values => trackers.collect{|s| [s.name, s.id.to_s] } },
:values => IssueStatus.find(:all, :order => 'position').collect{|s| [s.name, s.id.to_s] } "priority_id" => { :type => :list, :order => 3, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] } },
}, "subject" => { :type => :text, :order => 8 },
"tracker_id" => { "created_on" => { :type => :date_past, :order => 9 },
:type => :list, :order => 2, :values => trackers.collect{|s| [s.name, s.id.to_s] } "updated_on" => { :type => :date_past, :order => 10 },
}, "start_date" => { :type => :date, :order => 11 },
"priority_id" => { "due_date" => { :type => :date, :order => 12 },
:type => :list, :order => 3, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] } "estimated_hours" => { :type => :float, :order => 13 },
}, "done_ratio" => { :type => :integer, :order => 14 }}
"subject" => { :type => :text, :order => 8 },
"created_on" => { :type => :date_past, :order => 9 },
"updated_on" => { :type => :date_past, :order => 10 },
"start_date" => { :type => :date, :order => 11 },
"due_date" => { :type => :date, :order => 12 },
"estimated_hours" => { :type => :float, :order => 13 },
"done_ratio" => { :type => :integer, :order => 14 }
}
IssueRelation::TYPES.each do |relation_type, options|
@available_filters[relation_type] = {
:type => :relation, :order => @available_filters.size + 100,
:label => options[:name]
}
end
principals = [] principals = []
if project if project
principals += project.principals.sort principals += project.principals.sort
unless project.leaf? unless project.leaf?
subprojects = project.descendants.visible.all subprojects = project.descendants.visible.all
if subprojects.any? if subprojects.any?
@available_filters["subproject_id"] = { @available_filters["subproject_id"] = { :type => :list_subprojects, :order => 13, :values => subprojects.collect{|s| [s.name, s.id.to_s] } }
:type => :list_subprojects, :order => 13,
:values => subprojects.collect{|s| [s.name, s.id.to_s] }
}
principals += Principal.member_of(subprojects) principals += Principal.member_of(subprojects)
end end
end end
else else
all_projects = Project.visible.all
if all_projects.any? if all_projects.any?
# members of visible projects # members of visible projects
principals += Principal.member_of(all_projects) principals += Principal.member_of(all_projects)
# project filter # project filter
project_values = [] project_values = []
if User.current.logged? && User.current.memberships.any? if User.current.logged? && User.current.memberships.any?
project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"] project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"]
end end
project_values += all_projects_values Project.project_tree(all_projects) do |p, level|
@available_filters["project_id"] = { prefix = (level > 0 ? ('--' * level + ' ') : '')
:type => :list, :order => 1, :values => project_values project_values << ["#{prefix}#{p.name}", p.id.to_s]
} unless project_values.empty? end
@available_filters["project_id"] = { :type => :list, :order => 1, :values => project_values} unless project_values.empty?
end end
end end
principals.uniq! principals.uniq!
@@ -294,88 +267,63 @@ class Query < ActiveRecord::Base
assigned_to_values = [] assigned_to_values = []
assigned_to_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged? assigned_to_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
assigned_to_values += (Setting.issue_group_assignment? ? assigned_to_values += (Setting.issue_group_assignment? ? principals : users).collect{|s| [s.name, s.id.to_s] }
principals : users).collect{|s| [s.name, s.id.to_s] } @available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => assigned_to_values } unless assigned_to_values.empty?
@available_filters["assigned_to_id"] = {
:type => :list_optional, :order => 4, :values => assigned_to_values
} unless assigned_to_values.empty?
author_values = [] author_values = []
author_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged? author_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
author_values += users.collect{|s| [s.name, s.id.to_s] } author_values += users.collect{|s| [s.name, s.id.to_s] }
@available_filters["author_id"] = { @available_filters["author_id"] = { :type => :list, :order => 5, :values => author_values } unless author_values.empty?
:type => :list, :order => 5, :values => author_values
} unless author_values.empty?
group_values = Group.all.collect {|g| [g.name, g.id.to_s] } group_values = Group.all.collect {|g| [g.name, g.id.to_s] }
@available_filters["member_of_group"] = { @available_filters["member_of_group"] = { :type => :list_optional, :order => 6, :values => group_values } unless group_values.empty?
:type => :list_optional, :order => 6, :values => group_values
} unless group_values.empty?
role_values = Role.givable.collect {|r| [r.name, r.id.to_s] } role_values = Role.givable.collect {|r| [r.name, r.id.to_s] }
@available_filters["assigned_to_role"] = { @available_filters["assigned_to_role"] = { :type => :list_optional, :order => 7, :values => role_values } unless role_values.empty?
:type => :list_optional, :order => 7, :values => role_values
} unless role_values.empty?
if User.current.logged? if User.current.logged?
@available_filters["watcher_id"] = { @available_filters["watcher_id"] = { :type => :list, :order => 15, :values => [["<< #{l(:label_me)} >>", "me"]] }
:type => :list, :order => 15, :values => [["<< #{l(:label_me)} >>", "me"]]
}
end end
if project if project
# project specific filters # project specific filters
categories = project.issue_categories.all categories = project.issue_categories.all
unless categories.empty? unless categories.empty?
@available_filters["category_id"] = { @available_filters["category_id"] = { :type => :list_optional, :order => 6, :values => categories.collect{|s| [s.name, s.id.to_s] } }
:type => :list_optional, :order => 6,
:values => categories.collect{|s| [s.name, s.id.to_s] }
}
end end
versions = project.shared_versions.all versions = project.shared_versions.all
unless versions.empty? unless versions.empty?
@available_filters["fixed_version_id"] = { @available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] } }
:type => :list_optional, :order => 7,
:values => versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] }
}
end end
add_custom_fields_filters(project.all_issue_custom_fields) add_custom_fields_filters(project.all_issue_custom_fields)
else else
# global filters for cross project issue list # global filters for cross project issue list
system_shared_versions = Version.visible.find_all_by_sharing('system') system_shared_versions = Version.visible.find_all_by_sharing('system')
unless system_shared_versions.empty? unless system_shared_versions.empty?
@available_filters["fixed_version_id"] = { @available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => system_shared_versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] } }
:type => :list_optional, :order => 7,
:values => system_shared_versions.sort.collect{|s|
["#{s.project.name} - #{s.name}", s.id.to_s]
}
}
end end
add_custom_fields_filters( add_custom_fields_filters(IssueCustomField.find(:all, :conditions => {:is_filter => true, :is_for_all => true}))
IssueCustomField.find(:all,
:conditions => {
:is_filter => true,
:is_for_all => true
}))
end end
add_associations_custom_fields_filters :project, :author, :assigned_to, :fixed_version add_associations_custom_fields_filters :project, :author, :assigned_to, :fixed_version
if User.current.allowed_to?(:set_issues_private, nil, :global => true) || if User.current.allowed_to?(:set_issues_private, nil, :global => true) ||
User.current.allowed_to?(:set_own_issues_private, nil, :global => true) User.current.allowed_to?(:set_own_issues_private, nil, :global => true)
@available_filters["is_private"] = { @available_filters["is_private"] = { :type => :list, :order => 15, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]] }
:type => :list, :order => 16,
:values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]]
}
end end
Tracker.disabled_core_fields(trackers).each {|field| Tracker.disabled_core_fields(trackers).each {|field|
@available_filters.delete field @available_filters.delete field
} }
@available_filters.each do |field, options| @available_filters.each do |field, options|
options[:name] ||= l(options[:label] || "field_#{field}".gsub(/_id$/, '')) options[:name] ||= l("field_#{field}".gsub(/_id$/, ''))
end end
@available_filters @available_filters
end end
# Returns a representation of the available filters for JSON serialization # Returns a representation of the available filters for JSON serialization
def available_filters_as_json def available_filters_as_json
json = {} json = {}
available_filters.each do |field, options| available_filters.each do |field, options|
@@ -384,21 +332,6 @@ class Query < ActiveRecord::Base
json json
end end
def all_projects
@all_projects ||= Project.visible.all
end
def all_projects_values
return @all_projects_values if @all_projects_values
values = []
Project.project_tree(all_projects) do |p, level|
prefix = (level > 0 ? ('--' * level + ' ') : '')
values << ["#{prefix}#{p.name}", p.id.to_s]
end
@all_projects_values = values
end
def add_filter(field, operator, values) def add_filter(field, operator, values)
# values must be an array # values must be an array
return unless values.nil? || values.is_a?(Array) return unless values.nil? || values.is_a?(Array)
@@ -518,22 +451,6 @@ class Query < ActiveRecord::Base
end.compact end.compact
end end
def inline_columns
columns.select(&:inline?)
end
def block_columns
columns.reject(&:inline?)
end
def available_inline_columns
available_columns.select(&:inline?)
end
def available_block_columns
available_columns.reject(&:inline?)
end
def default_columns_names def default_columns_names
@default_columns_names ||= begin @default_columns_names ||= begin
default_columns = Setting.issue_list_default_columns.map(&:to_sym) default_columns = Setting.issue_list_default_columns.map(&:to_sym)
@@ -567,7 +484,7 @@ class Query < ActiveRecord::Base
if arg.is_a?(Hash) if arg.is_a?(Hash)
arg = arg.keys.sort.collect {|k| arg[k]} arg = arg.keys.sort.collect {|k| arg[k]}
end end
c = arg.select {|k,o| !k.to_s.blank?}.slice(0,3).collect {|k,o| [k.to_s, (o == 'desc' || o == false) ? 'desc' : 'asc']} c = arg.select {|k,o| !k.to_s.blank?}.slice(0,3).collect {|k,o| [k.to_s, o == 'desc' ? o : 'asc']}
write_attribute(:sort_criteria, c) write_attribute(:sort_criteria, c)
end end
@@ -583,17 +500,12 @@ class Query < ActiveRecord::Base
sort_criteria && sort_criteria[arg] && sort_criteria[arg].last sort_criteria && sort_criteria[arg] && sort_criteria[arg].last
end end
def sort_criteria_order_for(key)
sort_criteria.detect {|k, order| key.to_s == k}.try(:last)
end
# Returns the SQL sort order that should be prepended for grouping # Returns the SQL sort order that should be prepended for grouping
def group_by_sort_order def group_by_sort_order
if grouped? && (column = group_by_column) if grouped? && (column = group_by_column)
order = sort_criteria_order_for(column.name) || column.default_order
column.sortable.is_a?(Array) ? column.sortable.is_a?(Array) ?
column.sortable.collect {|s| "#{s} #{order}"}.join(',') : column.sortable.collect {|s| "#{s} #{column.default_order}"}.join(',') :
"#{column.sortable} #{order}" "#{column.sortable} #{column.default_order}"
end end
end end
@@ -723,9 +635,6 @@ class Query < ActiveRecord::Base
if has_column?(:spent_hours) if has_column?(:spent_hours)
Issue.load_visible_spent_hours(issues) Issue.load_visible_spent_hours(issues)
end end
if has_column?(:relations)
Issue.load_visible_relations(issues)
end
issues issues
rescue ::ActiveRecord::StatementInvalid => e rescue ::ActiveRecord::StatementInvalid => e
raise StatementInvalid.new(e.message) raise StatementInvalid.new(e.message)
@@ -820,42 +729,6 @@ class Query < ActiveRecord::Base
"#{Issue.table_name}.is_private #{op} (#{va})" "#{Issue.table_name}.is_private #{op} (#{va})"
end end
def sql_for_relations(field, operator, value, options={})
relation_options = IssueRelation::TYPES[field]
return relation_options unless relation_options
relation_type = field
join_column, target_join_column = "issue_from_id", "issue_to_id"
if relation_options[:reverse] || options[:reverse]
relation_type = relation_options[:reverse] || relation_type
join_column, target_join_column = target_join_column, join_column
end
sql = case operator
when "*", "!*"
op = (operator == "*" ? 'IN' : 'NOT IN')
"#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}')"
when "=", "!"
op = (operator == "=" ? 'IN' : 'NOT IN')
"#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = #{value.first.to_i})"
when "=p", "=!p", "!p"
op = (operator == "!p" ? 'NOT IN' : 'IN')
comp = (operator == "=!p" ? '<>' : '=')
"#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name}, #{Issue.table_name} relissues WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = relissues.id AND relissues.project_id #{comp} #{value.first.to_i})"
end
if relation_options[:sym] == field && !options[:reverse]
sqls = [sql, sql_for_relations(field, operator, value, :reverse => true)]
sqls.join(["!", "!*", "!p"].include?(operator) ? " AND " : " OR ")
else
sql
end
end
IssueRelation::TYPES.keys.each do |relation_type|
alias_method "sql_for_#{relation_type}_field".to_sym, :sql_for_relations
end
private private
def sql_for_custom_field(field, operator, value, custom_field_id) def sql_for_custom_field(field, operator, value, custom_field_id)
@@ -961,35 +834,21 @@ class Query < ActiveRecord::Base
sql = "#{Issue.table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{connection.quoted_false})" if field == "status_id" sql = "#{Issue.table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{connection.quoted_false})" if field == "status_id"
when "c" when "c"
sql = "#{Issue.table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{connection.quoted_true})" if field == "status_id" sql = "#{Issue.table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{connection.quoted_true})" if field == "status_id"
when "><t-"
# between today - n days and today
sql = relative_date_clause(db_table, db_field, - value.first.to_i, 0)
when ">t-" when ">t-"
# >= today - n days sql = relative_date_clause(db_table, db_field, - value.first.to_i, 0)
sql = relative_date_clause(db_table, db_field, - value.first.to_i, nil)
when "<t-" when "<t-"
# <= today - n days
sql = relative_date_clause(db_table, db_field, nil, - value.first.to_i) sql = relative_date_clause(db_table, db_field, nil, - value.first.to_i)
when "t-" when "t-"
# = n days in past
sql = relative_date_clause(db_table, db_field, - value.first.to_i, - value.first.to_i) sql = relative_date_clause(db_table, db_field, - value.first.to_i, - value.first.to_i)
when "><t+"
# between today and today + n days
sql = relative_date_clause(db_table, db_field, 0, value.first.to_i)
when ">t+" when ">t+"
# >= today + n days
sql = relative_date_clause(db_table, db_field, value.first.to_i, nil) sql = relative_date_clause(db_table, db_field, value.first.to_i, nil)
when "<t+" when "<t+"
# <= today + n days sql = relative_date_clause(db_table, db_field, 0, value.first.to_i)
sql = relative_date_clause(db_table, db_field, nil, value.first.to_i)
when "t+" when "t+"
# = today + n days
sql = relative_date_clause(db_table, db_field, value.first.to_i, value.first.to_i) sql = relative_date_clause(db_table, db_field, value.first.to_i, value.first.to_i)
when "t" when "t"
# = today
sql = relative_date_clause(db_table, db_field, 0, 0) sql = relative_date_clause(db_table, db_field, 0, 0)
when "w" when "w"
# = this week
first_day_of_week = l(:general_first_day_of_week).to_i first_day_of_week = l(:general_first_day_of_week).to_i
day_of_week = Date.today.cwday day_of_week = Date.today.cwday
days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week) days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week)
@@ -1039,11 +898,7 @@ class Query < ActiveRecord::Base
filter_id = "#{assoc}.#{filter_id}" filter_id = "#{assoc}.#{filter_id}"
filter_name = l("label_attribute_of_#{assoc}", :name => filter_name) filter_name = l("label_attribute_of_#{assoc}", :name => filter_name)
end end
@available_filters[filter_id] = options.merge({ @available_filters[filter_id] = options.merge({ :name => filter_name, :format => field.field_format })
:name => filter_name,
:format => field.field_format,
:field => field
})
end end
end end
+1 -23
View File
@@ -37,28 +37,7 @@ class Repository::Bazaar < Repository
'Bazaar' 'Bazaar'
end end
def entry(path=nil, identifier=nil)
scm.bzr_path_encodig = log_encoding
scm.entry(path, identifier)
end
def cat(path, identifier=nil)
scm.bzr_path_encodig = log_encoding
scm.cat(path, identifier)
end
def annotate(path, identifier=nil)
scm.bzr_path_encodig = log_encoding
scm.annotate(path, identifier)
end
def diff(path, rev, rev_to)
scm.bzr_path_encodig = log_encoding
scm.diff(path, rev, rev_to)
end
def entries(path=nil, identifier=nil) def entries(path=nil, identifier=nil)
scm.bzr_path_encodig = log_encoding
entries = scm.entries(path, identifier) entries = scm.entries(path, identifier)
if entries if entries
entries.each do |e| entries.each do |e|
@@ -89,7 +68,6 @@ class Repository::Bazaar < Repository
end end
def fetch_changesets def fetch_changesets
scm.bzr_path_encodig = log_encoding
scm_info = scm.info scm_info = scm.info
if scm_info if scm_info
# latest revision found in database # latest revision found in database
@@ -102,7 +80,7 @@ class Repository::Bazaar < Repository
while (identifier_from <= scm_revision) while (identifier_from <= scm_revision)
# loads changesets by batches of 200 # loads changesets by batches of 200
identifier_to = [identifier_from + 199, scm_revision].min identifier_to = [identifier_from + 199, scm_revision].min
revisions = scm.revisions('', identifier_to, identifier_from) revisions = scm.revisions('', identifier_to, identifier_from, :with_paths => true)
transaction do transaction do
revisions.reverse_each do |revision| revisions.reverse_each do |revision|
changeset = Changeset.create(:repository => self, changeset = Changeset.create(:repository => self,
+22 -63
View File
@@ -28,41 +28,11 @@ class User < Principal
# Different ways of displaying/sorting users # Different ways of displaying/sorting users
USER_FORMATS = { USER_FORMATS = {
:firstname_lastname => { :firstname_lastname => {:string => '#{firstname} #{lastname}', :order => %w(firstname lastname id)},
:string => '#{firstname} #{lastname}', :firstname => {:string => '#{firstname}', :order => %w(firstname id)},
:order => %w(firstname lastname id), :lastname_firstname => {:string => '#{lastname} #{firstname}', :order => %w(lastname firstname id)},
:setting_order => 1 :lastname_coma_firstname => {:string => '#{lastname}, #{firstname}', :order => %w(lastname firstname id)},
}, :username => {:string => '#{login}', :order => %w(login id)},
:firstname_lastinitial => {
:string => '#{firstname} #{lastname.to_s.chars.first}.',
:order => %w(firstname lastname id),
:setting_order => 2
},
:firstname => {
:string => '#{firstname}',
:order => %w(firstname id),
:setting_order => 3
},
:lastname_firstname => {
:string => '#{lastname} #{firstname}',
:order => %w(lastname firstname id),
:setting_order => 4
},
:lastname_coma_firstname => {
:string => '#{lastname}, #{firstname}',
:order => %w(lastname firstname id),
:setting_order => 5
},
:lastname => {
:string => '#{lastname}',
:order => %w(lastname id),
:setting_order => 6
},
:username => {
:string => '#{login}',
:order => %w(login id),
:setting_order => 7
},
} }
MAIL_NOTIFICATION_OPTIONS = [ MAIL_NOTIFICATION_OPTIONS = [
@@ -97,7 +67,7 @@ class User < Principal
validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) } validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, :case_sensitive => false validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, :case_sensitive => false
validates_uniqueness_of :mail, :if => Proc.new { |user| user.mail_changed? && user.mail.present? }, :case_sensitive => false validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
# Login must contain lettres, numbers, underscores only # Login must contain lettres, numbers, underscores only
validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
validates_length_of :login, :maximum => LOGIN_LENGTH_LIMIT validates_length_of :login, :maximum => LOGIN_LENGTH_LIMIT
@@ -114,11 +84,11 @@ class User < Principal
scope :in_group, lambda {|group| scope :in_group, lambda {|group|
group_id = group.is_a?(Group) ? group.id : group.to_i group_id = group.is_a?(Group) ? group.id : group.to_i
where("#{User.table_name}.id IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id) { :conditions => ["#{User.table_name}.id IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id] }
} }
scope :not_in_group, lambda {|group| scope :not_in_group, lambda {|group|
group_id = group.is_a?(Group) ? group.id : group.to_i group_id = group.is_a?(Group) ? group.id : group.to_i
where("#{User.table_name}.id NOT IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id) { :conditions => ["#{User.table_name}.id NOT IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id] }
} }
def set_mail_notification def set_mail_notification
@@ -360,10 +330,10 @@ class User < Principal
# version. Exact matches will be given priority. # version. Exact matches will be given priority.
def self.find_by_login(login) def self.find_by_login(login)
# First look for an exact match # First look for an exact match
user = where(:login => login).all.detect {|u| u.login == login} user = all(:conditions => {:login => login}).detect {|u| u.login == login}
unless user unless user
# Fail over to case-insensitive if none was found # Fail over to case-insensitive if none was found
user = where("LOWER(login) = ?", login.to_s.downcase).first user = first(:conditions => ["LOWER(login) = ?", login.to_s.downcase])
end end
user user
end end
@@ -380,7 +350,7 @@ class User < Principal
# Makes find_by_mail case-insensitive # Makes find_by_mail case-insensitive
def self.find_by_mail(mail) def self.find_by_mail(mail)
where("LOWER(mail) = ?", mail.to_s.downcase).first find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
end end
# Returns true if the default admin account can no longer be used # Returns true if the default admin account can no longer be used
@@ -392,17 +362,6 @@ class User < Principal
name name
end end
CSS_CLASS_BY_STATUS = {
STATUS_ANONYMOUS => 'anon',
STATUS_ACTIVE => 'active',
STATUS_REGISTERED => 'registered',
STATUS_LOCKED => 'locked'
}
def css_classes
"user #{CSS_CLASS_BY_STATUS[status]}"
end
# Returns the current day according to user's time zone # Returns the current day according to user's time zone
def today def today
if time_zone.nil? if time_zone.nil?
@@ -503,17 +462,17 @@ class User < Principal
roles = roles_for_project(context) roles = roles_for_project(context)
return false unless roles return false unless roles
roles.any? {|role| roles.detect {|role|
(context.is_public? || role.member?) && (context.is_public? || role.member?) &&
role.allowed_to?(action) && role.allowed_to?(action) &&
(block_given? ? yield(role, self) : true) (block_given? ? yield(role, self) : true)
} }
elsif context && context.is_a?(Array) elsif context && context.is_a?(Array)
if context.empty? # Authorize if user is authorized on every element of the array
false context.map do |project|
else allowed_to?(action, project, options, &block)
# Authorize if user is authorized on every element of the array end.inject do |memo,allowed|
context.map {|project| allowed_to?(action, project, options, &block)}.reduce(:&) memo && allowed
end end
elsif options[:global] elsif options[:global]
# Admin users are always authorized # Admin users are always authorized
@@ -522,7 +481,7 @@ class User < Principal
# authorize if user has at least one role that has this permission # authorize if user has at least one role that has this permission
roles = memberships.collect {|m| m.roles}.flatten.uniq roles = memberships.collect {|m| m.roles}.flatten.uniq
roles << (self.logged? ? Role.non_member : Role.anonymous) roles << (self.logged? ? Role.non_member : Role.anonymous)
roles.any? {|role| roles.detect {|role|
role.allowed_to?(action) && role.allowed_to?(action) &&
(block_given? ? yield(role, self) : true) (block_given? ? yield(role, self) : true)
} }
@@ -540,7 +499,7 @@ class User < Principal
# Returns true if the user is allowed to delete his own account # Returns true if the user is allowed to delete his own account
def own_account_deletable? def own_account_deletable?
Setting.unsubscribe? && Setting.unsubscribe? &&
(!admin? || User.active.where("admin = ? AND id <> ?", true, id).exists?) (!admin? || User.active.first(:conditions => ["admin = ? AND id <> ?", true, id]).present?)
end end
safe_attributes 'login', safe_attributes 'login',
@@ -611,7 +570,7 @@ class User < Principal
# Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
# one anonymous user per database. # one anonymous user per database.
def self.anonymous def self.anonymous
anonymous_user = AnonymousUser.first anonymous_user = AnonymousUser.find(:first)
if anonymous_user.nil? if anonymous_user.nil?
anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0) anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
raise 'Unable to create the anonymous user.' if anonymous_user.new_record? raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
@@ -624,11 +583,11 @@ class User < Principal
# This method is used in the SaltPasswords migration and is to be kept as is # This method is used in the SaltPasswords migration and is to be kept as is
def self.salt_unsalted_passwords! def self.salt_unsalted_passwords!
transaction do transaction do
User.where("salt IS NULL OR salt = ''").find_each do |user| User.find_each(:conditions => "salt IS NULL OR salt = ''") do |user|
next if user.hashed_password.blank? next if user.hashed_password.blank?
salt = User.generate_salt salt = User.generate_salt
hashed_password = User.hash_password("#{salt}#{user.hashed_password}") hashed_password = User.hash_password("#{salt}#{user.hashed_password}")
User.where(:id => user.id).update_all(:salt => salt, :hashed_password => hashed_password) User.update_all("salt = '#{salt}', hashed_password = '#{hashed_password}'", ["id = ?", user.id] )
end end
end end
end end
+9 -8
View File
@@ -35,11 +35,10 @@ class Version < ActiveRecord::Base
validates_inclusion_of :sharing, :in => VERSION_SHARINGS validates_inclusion_of :sharing, :in => VERSION_SHARINGS
validate :validate_version validate :validate_version
scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)} scope :named, lambda {|arg| { :conditions => ["LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip]}}
scope :open, where(:status => 'open') scope :open, :conditions => {:status => 'open'}
scope :visible, lambda {|*args| scope :visible, lambda {|*args| { :include => :project,
includes(:project).where(Project.allowed_to_condition(args.first || User.current, :view_issues)) :conditions => Project.allowed_to_condition(args.first || User.current, :view_issues) } }
}
safe_attributes 'name', safe_attributes 'name',
'description', 'description',
@@ -80,7 +79,7 @@ class Version < ActiveRecord::Base
# Returns the total reported time for this version # Returns the total reported time for this version
def spent_hours def spent_hours
@spent_hours ||= TimeEntry.joins(:issue).where("#{Issue.table_name}.fixed_version_id = ?", id).sum(:hours).to_f @spent_hours ||= TimeEntry.sum(:hours, :joins => :issue, :conditions => ["#{Issue.table_name}.fixed_version_id = ?", id]).to_f
end end
def closed? def closed?
@@ -93,7 +92,7 @@ class Version < ActiveRecord::Base
# Returns true if the version is completed: due date reached and no open issues # Returns true if the version is completed: due date reached and no open issues
def completed? def completed?
effective_date && (effective_date < Date.today) && (open_issues_count == 0) effective_date && (effective_date <= Date.today) && (open_issues_count == 0)
end end
def behind_schedule? def behind_schedule?
@@ -269,7 +268,9 @@ class Version < ActiveRecord::Base
if issues_count > 0 if issues_count > 0
ratio = open ? 'done_ratio' : 100 ratio = open ? 'done_ratio' : 100
done = fixed_issues.open(open).sum("COALESCE(estimated_hours, #{estimated_average}) * #{ratio}").to_f done = fixed_issues.sum("COALESCE(estimated_hours, #{estimated_average}) * #{ratio}",
:joins => :status,
:conditions => ["#{IssueStatus.table_name}.is_closed = ?", !open]).to_f
progress = done / (estimated_average * issues_count) progress = done / (estimated_average * issues_count)
end end
progress progress
+4 -27
View File
@@ -73,8 +73,6 @@ class WikiContent < ActiveRecord::Base
"LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id " + "LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id " +
"LEFT JOIN #{Project.table_name} ON #{Project.table_name}.id = #{Wiki.table_name}.project_id"} "LEFT JOIN #{Project.table_name} ON #{Project.table_name}.id = #{Wiki.table_name}.project_id"}
after_destroy :page_update_after_destroy
def text=(plain) def text=(plain)
case Setting.wiki_compression case Setting.wiki_compression
when 'gzip' when 'gzip'
@@ -117,31 +115,10 @@ class WikiContent < ActiveRecord::Base
# Returns the previous version or nil # Returns the previous version or nil
def previous def previous
@previous ||= WikiContent::Version. @previous ||= WikiContent::Version.find(:first,
reorder('version DESC'). :order => 'version DESC',
includes(:author). :include => :author,
where("wiki_content_id = ? AND version < ?", wiki_content_id, version).first :conditions => ["wiki_content_id = ? AND version < ?", wiki_content_id, version])
end
# Returns the next version or nil
def next
@next ||= WikiContent::Version.
reorder('version ASC').
includes(:author).
where("wiki_content_id = ? AND version > ?", wiki_content_id, version).first
end
private
# Updates page's content if the latest version is removed
# or destroys the page if it was the only version
def page_update_after_destroy
latest = page.content.versions.reorder("#{self.class.table_name}.version DESC").first
if latest && page.content.version != latest.version
raise ActiveRecord::Rollback unless page.content.revert_to!(latest)
elsif latest.nil?
raise ActiveRecord::Rollback unless page.destroy
end
end end
end end
end end
+6 -8
View File
@@ -50,14 +50,14 @@ class WikiPage < ActiveRecord::Base
# eager load information about last updates, without loading text # eager load information about last updates, without loading text
scope :with_updated_on, { scope :with_updated_on, {
:select => "#{WikiPage.table_name}.*, #{WikiContent.table_name}.updated_on, #{WikiContent.table_name}.version", :select => "#{WikiPage.table_name}.*, #{WikiContent.table_name}.updated_on",
:joins => "LEFT JOIN #{WikiContent.table_name} ON #{WikiContent.table_name}.page_id = #{WikiPage.table_name}.id" :joins => "LEFT JOIN #{WikiContent.table_name} ON #{WikiContent.table_name}.page_id = #{WikiPage.table_name}.id"
} }
# Wiki pages that are protected by default # Wiki pages that are protected by default
DEFAULT_PROTECTED_PAGES = %w(sidebar) DEFAULT_PROTECTED_PAGES = %w(sidebar)
safe_attributes 'parent_id', 'parent_title', safe_attributes 'parent_id',
:if => lambda {|page, user| page.new_record? || user.allowed_to?(:rename_wiki_pages, page.project)} :if => lambda {|page, user| page.new_record? || user.allowed_to?(:rename_wiki_pages, page.project)}
def initialize(attributes=nil, *args) def initialize(attributes=nil, *args)
@@ -111,13 +111,11 @@ class WikiPage < ActiveRecord::Base
def diff(version_to=nil, version_from=nil) def diff(version_to=nil, version_from=nil)
version_to = version_to ? version_to.to_i : self.content.version version_to = version_to ? version_to.to_i : self.content.version
content_to = content.versions.find_by_version(version_to) version_from = version_from ? version_from.to_i : version_to - 1
content_from = version_from ? content.versions.find_by_version(version_from.to_i) : content_to.try(:previous) version_to, version_from = version_from, version_to unless version_from < version_to
return nil unless content_to && content_from
if content_from.version > content_to.version content_to = content.versions.find_by_version(version_to)
content_to, content_from = content_from, content_to content_from = content.versions.find_by_version(version_from)
end
(content_to && content_from) ? WikiDiff.new(content_to, content_from) : nil (content_to && content_from) ? WikiDiff.new(content_to, content_from) : nil
end end
+2 -7
View File
@@ -5,7 +5,7 @@
<table> <table>
<tr> <tr>
<td align="right"><label for="username"><%=l(:field_login)%>:</label></td> <td align="right"><label for="username"><%=l(:field_login)%>:</label></td>
<td align="left"><%= text_field_tag 'username', params[:username], :tabindex => '1' %></td> <td align="left"><%= text_field_tag 'username', nil, :tabindex => '1' %></td>
</tr> </tr>
<tr> <tr>
<td align="right"><label for="password"><%=l(:field_password)%>:</label></td> <td align="right"><label for="password"><%=l(:field_password)%>:</label></td>
@@ -36,12 +36,7 @@
</td> </td>
</tr> </tr>
</table> </table>
<%= javascript_tag "$('#username').focus();" %>
<% end %> <% end %>
</div> </div>
<%= call_hook :view_account_login_bottom %> <%= call_hook :view_account_login_bottom %>
<% if params[:username].present? %>
<%= javascript_tag "$('#password').focus();" %>
<% else %>
<%= javascript_tag "$('#username').focus();" %>
<% end %>
+8 -6
View File
@@ -6,14 +6,16 @@
<p><%= link_to_attachment @attachment, :text => l(:button_download), :download => true -%> <p><%= link_to_attachment @attachment, :text => l(:button_download), :download => true -%>
<span class="size">(<%= number_to_human_size @attachment.filesize %>)</span></p> <span class="size">(<%= number_to_human_size @attachment.filesize %>)</span></p>
</div> </div>
<p>
<%= form_tag({}, :method => 'get') do %> <%= form_tag({}, :method => 'get') do %>
<p> <label><%= l(:label_view_diff) %></label>
<%= l(:label_view_diff) %>: <%= select_tag 'type',
<label><%= radio_button_tag 'type', 'inline', @diff_type != 'sbs', :onchange => "this.form.submit()" %> <%= l(:label_diff_inline) %></label> options_for_select(
<label><%= radio_button_tag 'type', 'sbs', @diff_type == 'sbs', :onchange => "this.form.submit()" %> <%= l(:label_diff_side_by_side) %></label> [[l(:label_diff_inline), "inline"], [l(:label_diff_side_by_side), "sbs"]], @diff_type),
</p> :onchange => "if (this.value != '') {this.form.submit()}" %>
<% end %> <% end %>
<%= render :partial => 'common/diff', :locals => {:diff => @diff, :diff_type => @diff_type, :diff_style => nil} %> </p>
<%= render :partial => 'common/diff', :locals => {:diff => @diff, :diff_type => @diff_type} %>
<% html_title @attachment.filename %> <% html_title @attachment.filename %>
+1 -2
View File
@@ -1,7 +1,6 @@
<% diff = Redmine::UnifiedDiff.new( <% diff = Redmine::UnifiedDiff.new(
diff, :type => diff_type, diff, :type => diff_type,
:max_lines => Setting.diff_max_lines_displayed.to_i, :max_lines => Setting.diff_max_lines_displayed.to_i) -%>
:style => diff_style) -%>
<% diff.each do |table_file| -%> <% diff.each do |table_file| -%>
<div class="autoscroll"> <div class="autoscroll">
+1 -1
View File
@@ -111,7 +111,7 @@
<li><%= bulk_update_custom_field_context_menu_link(field, text, value || text) %></li> <li><%= bulk_update_custom_field_context_menu_link(field, text, value || text) %></li>
<% end %> <% end %>
<% unless field.is_required? %> <% unless field.is_required? %>
<li><%= bulk_update_custom_field_context_menu_link(field, l(:label_none), '__none__') %></li> <li><%= bulk_update_custom_field_context_menu_link(field, l(:label_none), '') %></li>
<% end %> <% end %>
</ul> </ul>
</li> </li>
-9
View File
@@ -1,9 +0,0 @@
api.array @klass.name.underscore.pluralize do
@enumerations.each do |enumeration|
api.__send__ @klass.name.underscore do
api.id enumeration.id
api.name enumeration.name
api.is_default enumeration.is_default
end
end
end
+2 -3
View File
@@ -201,10 +201,9 @@
style += "width: #{width}px;" style += "width: #{width}px;"
style += "height: #{height}px;" style += "height: #{height}px;"
style += "font-size:0.7em;" style += "font-size:0.7em;"
clss = "gantt_hdr" style += 'background:#f1f1f1;' if wday > 5
clss << " nwday" if @gantt.non_working_week_days.include?(wday)
%> %>
<%= content_tag(:div, :style => style, :class => clss) do %> <%= content_tag(:div, :style => style, :class => "gantt_hdr") do %>
<%= day_letter(wday) %> <%= day_letter(wday) %>
<% end %> <% end %>
<% <%
+1 -1
View File
@@ -1,5 +1,5 @@
<div class="contextual"> <div class="contextual">
<%= link_to_if_authorized(l(:button_update), {:controller => 'issues', :action => 'edit', :id => @issue }, :onclick => 'showAndScrollTo("update", "issue_notes"); return false;', :class => 'icon icon-edit', :accesskey => accesskey(:edit)) %> <%= link_to_if_authorized(l(:button_update), {:controller => 'issues', :action => 'edit', :id => @issue }, :onclick => 'showAndScrollTo("update", "notes"); return false;', :class => 'icon icon-edit', :accesskey => accesskey(:edit)) %>
<%= link_to l(:button_log_time), new_issue_time_entry_path(@issue), :class => 'icon icon-time-add' if User.current.allowed_to?(:log_time, @project) %> <%= link_to l(:button_log_time), new_issue_time_entry_path(@issue), :class => 'icon icon-time-add' if User.current.allowed_to?(:log_time, @project) %>
<%= watcher_tag(@issue, User.current) %> <%= watcher_tag(@issue, User.current) %>
<%= link_to_if_authorized l(:button_copy), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue}, :class => 'icon icon-copy' %> <%= link_to_if_authorized l(:button_copy), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue}, :class => 'icon icon-copy' %>
+2 -2
View File
@@ -2,7 +2,7 @@
<div class="splitcontent"> <div class="splitcontent">
<div class="splitcontentleft"> <div class="splitcontentleft">
<% if @issue.safe_attribute?('status_id') && @allowed_statuses.present? %> <% if @issue.safe_attribute? 'status_id' %>
<p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), {:required => true}, <p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), {:required => true},
:onchange => "updateIssueFrom('#{escape_javascript project_issue_form_path(@project, :id => @issue, :format => 'js')}')" %></p> :onchange => "updateIssueFrom('#{escape_javascript project_issue_form_path(@project, :id => @issue, :format => 'js')}')" %></p>
@@ -43,7 +43,7 @@
<div class="splitcontentright"> <div class="splitcontentright">
<% if @issue.safe_attribute? 'parent_issue_id' %> <% if @issue.safe_attribute? 'parent_issue_id' %>
<p id="parent_issue"><%= f.text_field :parent_issue_id, :size => 10, :required => @issue.required_attribute?('parent_issue_id') %></p> <p id="parent_issue"><%= f.text_field :parent_issue_id, :size => 10, :required => @issue.required_attribute?('parent_issue_id') %></p>
<%= javascript_tag "observeAutocompleteField('issue_parent_issue_id', '#{escape_javascript auto_complete_issues_path}')" %> <%= javascript_tag "observeAutocompleteField('issue_parent_issue_id', '#{escape_javascript auto_complete_issues_path(:project_id => @issue.project)}')" %>
<% end %> <% end %>
<% if @issue.safe_attribute? 'start_date' %> <% if @issue.safe_attribute? 'start_date' %>
+1 -1
View File
@@ -18,7 +18,7 @@
</div> </div>
<p> <p>
<label><%= radio_button_tag 'conflict_resolution', 'overwrite' %> <%= l(:text_issue_conflict_resolution_overwrite) %></label><br /> <label><%= radio_button_tag 'conflict_resolution', 'overwrite' %> <%= l(:text_issue_conflict_resolution_overwrite) %></label><br />
<% if @issue.notes.present? %> <% if @notes.present? %>
<label><%= radio_button_tag 'conflict_resolution', 'add_notes' %> <%= l(:text_issue_conflict_resolution_add_notes) %></label><br /> <label><%= radio_button_tag 'conflict_resolution', 'add_notes' %> <%= l(:text_issue_conflict_resolution_add_notes) %></label><br />
<% end %> <% end %>
<label><%= radio_button_tag 'conflict_resolution', 'cancel' %> <%= l(:text_issue_conflict_resolution_cancel, :link => link_to_issue(@issue, :subject => false)).html_safe %></label> <label><%= radio_button_tag 'conflict_resolution', 'cancel' %> <%= l(:text_issue_conflict_resolution_cancel, :link => link_to_issue(@issue, :subject => false)).html_safe %></label>
+3 -10
View File
@@ -27,18 +27,11 @@
<% end %> <% end %>
<fieldset><legend><%= l(:field_notes) %></legend> <fieldset><legend><%= l(:field_notes) %></legend>
<%= f.text_area :notes, :cols => 60, :rows => 10, :class => 'wiki-edit', :no_label => true %> <%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %>
<%= wikitoolbar_for 'issue_notes' %> <%= wikitoolbar_for 'notes' %>
<% if @issue.safe_attribute? 'private_notes' %>
<label for="issue_private_notes"><%= f.check_box :private_notes, :no_label => true %> <%= l(:field_private_notes) %></label>
<% end %>
<%= call_hook(:view_issues_edit_notes_bottom, { :issue => @issue, :notes => @notes, :form => f }) %> <%= call_hook(:view_issues_edit_notes_bottom, { :issue => @issue, :notes => @notes, :form => f }) %>
</fieldset>
<fieldset><legend><%= l(:label_attachment_plural) %></legend> <p><%=l(:label_attachment_plural)%><br /><%= render :partial => 'attachments/form', :locals => {:container => @issue} %></p>
<p><%= render :partial => 'attachments/form', :locals => {:container => @issue} %></p>
</fieldset> </fieldset>
</div> </div>
+1 -1
View File
@@ -18,7 +18,7 @@
<% end %> <% end %>
<% if @issue.safe_attribute? 'subject' %> <% if @issue.safe_attribute? 'subject' %>
<p><%= f.text_field :subject, :size => 80, :maxlength => 255, :required => true %></p> <p><%= f.text_field :subject, :size => 80, :required => true %></p>
<% end %> <% end %>
<% if @issue.safe_attribute? 'description' %> <% if @issue.safe_attribute? 'description' %>
+13 -25
View File
@@ -2,30 +2,25 @@
<%= hidden_field_tag 'back_url', url_for(params), :id => nil %> <%= hidden_field_tag 'back_url', url_for(params), :id => nil %>
<div class="autoscroll"> <div class="autoscroll">
<table class="list issues"> <table class="list issues">
<thead> <thead><tr>
<tr> <th class="checkbox hide-when-print"><%= link_to image_tag('toggle_check.png'), {}, :onclick => 'toggleIssuesSelection(this); return false;',
<th class="checkbox hide-when-print"> :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %>
<%= link_to image_tag('toggle_check.png'), {}, </th>
:onclick => 'toggleIssuesSelection(this); return false;', <%= sort_header_tag('id', :caption => '#', :default_order => 'desc') %>
:title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %> <% query.columns.each do |column| %>
</th> <%= column_header(column) %>
<%= sort_header_tag('id', :caption => '#', :default_order => 'desc') %> <% end %>
<% query.inline_columns.each do |column| %> </tr></thead>
<%= column_header(column) %>
<% end %>
</tr>
</thead>
<% previous_group = false %> <% previous_group = false %>
<tbody> <tbody>
<% issue_list(issues) do |issue, level| -%> <% issue_list(issues) do |issue, level| -%>
<% if @query.grouped? && (group = @query.group_by_column.value(issue)) != previous_group %> <% if @query.grouped? && (group = @query.group_by_column.value(issue)) != previous_group %>
<% reset_cycle %> <% reset_cycle %>
<tr class="group open"> <tr class="group open">
<td colspan="<%= query.inline_columns.size + 2 %>"> <td colspan="<%= query.columns.size + 2 %>">
<span class="expander" onclick="toggleRowGroup(this);">&nbsp;</span> <span class="expander" onclick="toggleRowGroup(this);">&nbsp;</span>
<%= group.blank? ? l(:label_none) : column_content(@query.group_by_column, issue) %> <span class="count"><%= @issue_count_by_group[group] %></span> <%= group.blank? ? l(:label_none) : column_content(@query.group_by_column, issue) %> <span class="count">(<%= @issue_count_by_group[group] %>)</span>
<%= link_to_function("#{l(:button_collapse_all)}/#{l(:button_expand_all)}", <%= link_to_function("#{l(:button_collapse_all)}/#{l(:button_expand_all)}", "toggleAllRowGroups(this)", :class => 'toggle-all') %>
"toggleAllRowGroups(this)", :class => 'toggle-all') %>
</td> </td>
</tr> </tr>
<% previous_group = group %> <% previous_group = group %>
@@ -33,15 +28,8 @@
<tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= issue.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>"> <tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= issue.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>">
<td class="checkbox hide-when-print"><%= check_box_tag("ids[]", issue.id, false, :id => nil) %></td> <td class="checkbox hide-when-print"><%= check_box_tag("ids[]", issue.id, false, :id => nil) %></td>
<td class="id"><%= link_to issue.id, issue_path(issue) %></td> <td class="id"><%= link_to issue.id, issue_path(issue) %></td>
<%= raw query.inline_columns.map {|column| "<td class=\"#{column.css_classes}\">#{column_content(column, issue)}</td>"}.join %> <%= raw query.columns.map {|column| "<td class=\"#{column.css_classes}\">#{column_content(column, issue)}</td>"}.join %>
</tr> </tr>
<% @query.block_columns.each do |column|
if (text = column_content(column, issue)) && text.present? -%>
<tr class="<%= current_cycle %>">
<td colspan="<%= @query.inline_columns.size + 2 %>" class="<%= column.css_classes %>"><%= text %></td>
</tr>
<% end -%>
<% end -%>
<% end -%> <% end -%>
</tbody> </tbody>
</table> </table>
+1 -1
View File
@@ -1 +1 @@
$('#content').html('<%= escape_javascript(render :template => 'issues/bulk_edit', :formats => [:html]) %>'); $('#content').html('<%= escape_javascript(render :template => 'issues/bulk_edit.html') %>');
+1 -5
View File
@@ -34,10 +34,6 @@
@query.group_by) @query.group_by)
) %></td> ) %></td>
</tr> </tr>
<tr>
<td><%= l(:button_show) %></td>
<td><%= available_block_columns_tags(@query) %></td>
</tr>
</table> </table>
</div> </div>
</fieldset> </fieldset>
@@ -77,7 +73,7 @@
<label><%= radio_button_tag 'columns', 'all' %> <%= l(:description_all_columns) %></label> <label><%= radio_button_tag 'columns', 'all' %> <%= l(:description_all_columns) %></label>
</p> </p>
<p> <p>
<label><%= check_box_tag 'description', '1', @query.has_column?(:description) %> <%= l(:field_description) %></label> <label><%= check_box_tag 'description', '1' %> <%= l(:field_description) %></label>
</p> </p>
<p class="buttons"> <p class="buttons">
<%= submit_tag l(:button_export), :name => nil, :onclick => "hideModal(this);" %> <%= submit_tag l(:button_export), :name => nil, :onclick => "hideModal(this);" %>
+1 -1
View File
@@ -48,7 +48,7 @@ api.issue do
end if include_in_api_response?('changesets') && User.current.allowed_to?(:view_changesets, @project) end if include_in_api_response?('changesets') && User.current.allowed_to?(:view_changesets, @project)
api.array :journals do api.array :journals do
@journals.each do |journal| @issue.journals.each do |journal|
api.journal :id => journal.id do api.journal :id => journal.id do
api.user(:id => journal.user_id, :name => journal.user.name) unless journal.user.nil? api.user(:id => journal.user_id, :name => journal.user.name) unless journal.user.nil?
api.notes journal.notes api.notes journal.notes
+1 -3
View File
@@ -71,7 +71,6 @@ end %>
<% if @issue.description? || @issue.attachments.any? -%> <% if @issue.description? || @issue.attachments.any? -%>
<hr /> <hr />
<% if @issue.description? %> <% if @issue.description? %>
<div class="description">
<div class="contextual"> <div class="contextual">
<%= link_to l(:button_quote), <%= link_to l(:button_quote),
{:controller => 'journals', :action => 'new', :id => @issue}, {:controller => 'journals', :action => 'new', :id => @issue},
@@ -84,7 +83,6 @@ end %>
<div class="wiki"> <div class="wiki">
<%= textilizable @issue, :description, :attachments => @issue.attachments %> <%= textilizable @issue, :description, :attachments => @issue.attachments %>
</div> </div>
</div>
<% end %> <% end %>
<%= link_to_attachments @issue, :thumbnails => true %> <%= link_to_attachments @issue, :thumbnails => true %>
<% end -%> <% end -%>
@@ -95,7 +93,7 @@ end %>
<hr /> <hr />
<div id="issue_tree"> <div id="issue_tree">
<div class="contextual"> <div class="contextual">
<%= link_to_new_subtask(@issue) if User.current.allowed_to?(:manage_subtasks, @project) %> <%= link_to(l(:button_add), {:controller => 'issues', :action => 'new', :project_id => @project, :issue => {:parent_issue_id => @issue}}) if User.current.allowed_to?(:manage_subtasks, @project) %>
</div> </div>
<p><strong><%=l(:label_subtask_plural)%></strong></p> <p><strong><%=l(:label_subtask_plural)%></strong></p>
<%= render_descendants_tree(@issue) unless @issue.leaf? %> <%= render_descendants_tree(@issue) unless @issue.leaf? %>
+1 -8
View File
@@ -1,10 +1,3 @@
$('#issue_notes').val("<%= raw escape_javascript(@content) %>"); $('#notes').val("<%= raw escape_javascript(@content) %>");
<%
# when quoting a private journal, check the private checkbox
if @journal && @journal.private_notes?
%>
$('#issue_private_notes').attr('checked', true);
<% end %>
showAndScrollTo("update", "notes"); showAndScrollTo("update", "notes");
$('#notes').scrollTop = $('#notes').scrollHeight - $('#notes').clientHeight; $('#notes').scrollTop = $('#notes').scrollHeight - $('#notes').clientHeight;
-2
View File
@@ -18,7 +18,6 @@
<body class="<%=h body_css_classes %>"> <body class="<%=h body_css_classes %>">
<div id="wrapper"> <div id="wrapper">
<div id="wrapper2"> <div id="wrapper2">
<div id="wrapper3">
<div id="top-menu"> <div id="top-menu">
<div id="account"> <div id="account">
<%= render_menu :account_menu -%> <%= render_menu :account_menu -%>
@@ -63,7 +62,6 @@
<div style="clear:both;"></div> <div style="clear:both;"></div>
</div> </div>
</div> </div>
</div>
<div id="ajax-indicator" style="display:none;"><span><%= l(:label_loading) %></span></div> <div id="ajax-indicator" style="display:none;"><span><%= l(:label_loading) %></span></div>
<div id="ajax-modal" style="display:none;"></div> <div id="ajax-modal" style="display:none;"></div>
+1 -1
View File
@@ -3,7 +3,7 @@
<%= labelled_form_for @news, :html => { :id => 'news-form', :multipart => true, :method => :put } do |f| %> <%= labelled_form_for @news, :html => { :id => 'news-form', :multipart => true, :method => :put } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %> <%= render :partial => 'form', :locals => { :f => f } %>
<%= submit_tag l(:button_save) %> <%= submit_tag l(:button_save) %>
<%= preview_link preview_news_path(:project_id => @project, :id => @news), 'news-form' %> <%= preview_link preview_news_path(:project_id => @project), 'news-form' %>
<% end %> <% end %>
<div id="preview" class="wiki"></div> <div id="preview" class="wiki"></div>
+1 -1
View File
@@ -16,7 +16,7 @@
:html => { :id => 'news-form', :multipart => true, :method => :put } do |f| %> :html => { :id => 'news-form', :multipart => true, :method => :put } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %> <%= render :partial => 'form', :locals => { :f => f } %>
<%= submit_tag l(:button_save) %> <%= submit_tag l(:button_save) %>
<%= preview_link preview_news_path(:project_id => @project, :id => @news), 'news-form' %> | <%= preview_link preview_news_path(:project_id => @project), 'news-form' %> |
<%= link_to l(:button_cancel), "#", :onclick => '$("#edit-news").hide(); return false;' %> <%= link_to l(:button_cancel), "#", :onclick => '$("#edit-news").hide(); return false;' %>
<% end %> <% end %>
<div id="preview" class="wiki"></div> <div id="preview" class="wiki"></div>
+2 -2
View File
@@ -4,7 +4,7 @@
<%= label_tag "available_columns", l(:description_available_columns) %> <%= label_tag "available_columns", l(:description_available_columns) %>
<br /> <br />
<%= select_tag 'available_columns', <%= select_tag 'available_columns',
options_for_select((query.available_inline_columns - query.columns).collect {|column| [column.caption, column.name]}), options_for_select((query.available_columns - query.columns).collect {|column| [column.caption, column.name]}),
:multiple => true, :size => 10, :style => "width:150px", :multiple => true, :size => 10, :style => "width:150px",
:ondblclick => "moveOptions(this.form.available_columns, this.form.selected_columns);" %> :ondblclick => "moveOptions(this.form.available_columns, this.form.selected_columns);" %>
</td> </td>
@@ -18,7 +18,7 @@
<%= label_tag "selected_columns", l(:description_selected_columns) %> <%= label_tag "selected_columns", l(:description_selected_columns) %>
<br /> <br />
<%= select_tag((defined?(tag_name) ? tag_name : 'c[]'), <%= select_tag((defined?(tag_name) ? tag_name : 'c[]'),
options_for_select(query.inline_columns.collect {|column| [column.caption, column.name]}), options_for_select(query.columns.collect {|column| [column.caption, column.name]}),
:id => 'selected_columns', :multiple => true, :size => 10, :style => "width:150px", :id => 'selected_columns', :multiple => true, :size => 10, :style => "width:150px",
:ondblclick => "moveOptions(this.form.selected_columns, this.form.available_columns);") %> :ondblclick => "moveOptions(this.form.selected_columns, this.form.available_columns);") %>
</td> </td>
-1
View File
@@ -3,7 +3,6 @@ var operatorLabels = <%= raw_json Query.operators_labels %>;
var operatorByType = <%= raw_json Query.operators_by_filter_type %>; var operatorByType = <%= raw_json Query.operators_by_filter_type %>;
var availableFilters = <%= raw_json query.available_filters_as_json %>; var availableFilters = <%= raw_json query.available_filters_as_json %>;
var labelDayPlural = <%= raw_json l(:label_day_plural) %>; var labelDayPlural = <%= raw_json l(:label_day_plural) %>;
var allProjects = <%= raw query.all_projects_values.to_json %>;
$(document).ready(function(){ $(document).ready(function(){
initFilters(); initFilters();
<% query.filters.each do |field, options| %> <% query.filters.each do |field, options| %>
-3
View File
@@ -21,9 +21,6 @@
<p><label for="query_group_by"><%= l(:field_group_by) %></label> <p><label for="query_group_by"><%= l(:field_group_by) %></label>
<%= select 'query', 'group_by', @query.groupable_columns.collect {|c| [c.caption, c.name.to_s]}, :include_blank => true %></p> <%= select 'query', 'group_by', @query.groupable_columns.collect {|c| [c.caption, c.name.to_s]}, :include_blank => true %></p>
<p><label><%= l(:button_show) %></label>
<%= available_block_columns_tags(@query) %></p>
</div> </div>
<fieldset id="filters"><legend><%= l(:label_filter_plural) %></legend> <fieldset id="filters"><legend><%= l(:label_filter_plural) %></legend>
+6 -4
View File
@@ -6,14 +6,16 @@
:path => to_path_param(@path), :rev=> @rev}, :method => 'get') do %> :path => to_path_param(@path), :rev=> @rev}, :method => 'get') do %>
<%= hidden_field_tag('rev_to', params[:rev_to]) if params[:rev_to] %> <%= hidden_field_tag('rev_to', params[:rev_to]) if params[:rev_to] %>
<p> <p>
<%= l(:label_view_diff) %>: <label><%= l(:label_view_diff) %></label>
<label><%= radio_button_tag 'type', 'inline', @diff_type != 'sbs', :onchange => "this.form.submit()" %> <%= l(:label_diff_inline) %></label> <%= select_tag 'type',
<label><%= radio_button_tag 'type', 'sbs', @diff_type == 'sbs', :onchange => "this.form.submit()" %> <%= l(:label_diff_side_by_side) %></label> options_for_select(
[[l(:label_diff_inline), "inline"], [l(:label_diff_side_by_side), "sbs"]], @diff_type),
:onchange => "if (this.value != '') {this.form.submit()}" %>
</p> </p>
<% end %> <% end %>
<% cache(@cache_key) do -%> <% cache(@cache_key) do -%>
<%= render :partial => 'common/diff', :locals => {:diff => @diff, :diff_type => @diff_type, :diff_style => @repository.class.scm_name} %> <%= render :partial => 'common/diff', :locals => {:diff => @diff, :diff_type => @diff_type} %>
<% end -%> <% end -%>
<% other_formats_links do |f| %> <% other_formats_links do |f| %>
+1 -1
View File
@@ -1 +1 @@
$('#content').html('<%= escape_javascript(render :template => 'repositories/new', :formats => [:html]) %>'); $('#content').html('<%= escape_javascript(render :template => 'repositories/new.html') %>');
+1 -1
View File
@@ -4,7 +4,7 @@
:repository_id => @repository.identifier_param}, :repository_id => @repository.identifier_param},
:method => :get :method => :get
) do %> ) do %>
<%= l(:label_revision) %>: <%= text_field_tag 'rev', nil, :size => 8 %> <%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 8 %>
<%= submit_tag 'OK' %> <%= submit_tag 'OK' %>
<% end %> <% end %>
</div> </div>
-9
View File
@@ -1,9 +0,0 @@
api.role do
api.id @role.id
api.name @role.name
api.array :permissions do
@role.permissions.each do |perm|
api.permission(perm.to_s)
end
end
end
-4
View File
@@ -3,8 +3,6 @@
<div class="box tabular settings"> <div class="box tabular settings">
<p><%= setting_check_box :cross_project_issue_relations %></p> <p><%= setting_check_box :cross_project_issue_relations %></p>
<p><%= setting_select :cross_project_subtasks, cross_project_subtasks_options %></p>
<p><%= setting_check_box :issue_group_assignment %></p> <p><%= setting_check_box :issue_group_assignment %></p>
<p><%= setting_check_box :default_issue_start_date_to_creation_date %></p> <p><%= setting_check_box :default_issue_start_date_to_creation_date %></p>
@@ -13,8 +11,6 @@
<p><%= setting_select :issue_done_ratio, Issue::DONE_RATIO_OPTIONS.collect {|i| [l("setting_issue_done_ratio_#{i}"), i]} %></p> <p><%= setting_select :issue_done_ratio, Issue::DONE_RATIO_OPTIONS.collect {|i| [l("setting_issue_done_ratio_#{i}"), i]} %></p>
<p><%= setting_multiselect :non_working_week_days, (1..7).map {|d| [day_name(d), d.to_s]}, :inline => true %></p>
<p><%= setting_text_field :issues_export_limit, :size => 6 %></p> <p><%= setting_text_field :issues_export_limit, :size => 6 %></p>
<p><%= setting_text_field :gantt_items_limit, :size => 6 %></p> <p><%= setting_text_field :gantt_items_limit, :size => 6 %></p>
+1 -1
View File
@@ -12,7 +12,7 @@
<p><%= f.text_field :issue_id, :size => 6 %> <em><%= h("#{@time_entry.issue.tracker.name} ##{@time_entry.issue.id}: #{@time_entry.issue.subject}") if @time_entry.issue %></em></p> <p><%= f.text_field :issue_id, :size => 6 %> <em><%= h("#{@time_entry.issue.tracker.name} ##{@time_entry.issue.id}: #{@time_entry.issue.subject}") if @time_entry.issue %></em></p>
<p><%= f.text_field :spent_on, :size => 10, :required => true %><%= calendar_for('time_entry_spent_on') %></p> <p><%= f.text_field :spent_on, :size => 10, :required => true %><%= calendar_for('time_entry_spent_on') %></p>
<p><%= f.text_field :hours, :size => 6, :required => true %></p> <p><%= f.text_field :hours, :size => 6, :required => true %></p>
<p><%= f.text_field :comments, :size => 100, :maxlength => 255 %></p> <p><%= f.text_field :comments, :size => 100 %></p>
<p><%= f.select :activity_id, activity_collection_for_select_options(@time_entry), :required => true %></p> <p><%= f.select :activity_id, activity_collection_for_select_options(@time_entry), :required => true %></p>
<% @time_entry.custom_field_values.each do |value| %> <% @time_entry.custom_field_values.each do |value| %>
<p><%= custom_field_tag_with_label :time_entry, value %></p> <p><%= custom_field_tag_with_label :time_entry, value %></p>
+1 -1
View File
@@ -36,7 +36,7 @@
</tr></thead> </tr></thead>
<tbody> <tbody>
<% for user in @users -%> <% for user in @users -%>
<tr class="<%= user.css_classes %> <%= cycle("odd", "even") %>"> <tr class="user <%= cycle("odd", "even") %> <%= %w(anon active registered locked)[user.status] %>">
<td class="username"><%= avatar(user, :size => "14") %><%= link_to h(user.login), edit_user_path(user) %></td> <td class="username"><%= avatar(user, :size => "14") %><%= link_to h(user.login), edit_user_path(user) %></td>
<td class="firstname"><%= h(user.firstname) %></td> <td class="firstname"><%= h(user.firstname) %></td>
<td class="lastname"><%= h(user.lastname) %></td> <td class="lastname"><%= h(user.lastname) %></td>
+6 -5
View File
@@ -14,11 +14,12 @@
<% counts.each do |count| %> <% counts.each do |count| %>
<tr> <tr>
<td width="130px" align="right" > <td width="130px" align="right" >
<% if count[:group] -%> <%= link_to h(count[:group]), {:controller => 'issues',
<%= link_to(h(count[:group]), project_issues_path(version.project, :set_filter => 1, :status_id => '*', :fixed_version_id => version, "#{criteria}_id" => count[:group])) %> :action => 'index',
<% else -%> :project_id => version.project,
<%= link_to(l(:label_none), project_issues_path(version.project, :set_filter => 1, :status_id => '*', :fixed_version_id => version, "#{criteria}_id" => "!*")) %> :set_filter => 1,
<% end %> :status_id => '*',
:fixed_version_id => version}.merge("#{criteria}_id".to_sym => count[:group]) %>
</td> </td>
<td width="240px"> <td width="240px">
<%= progress_bar((count[:closed].to_f / count[:total])*100, <%= progress_bar((count[:closed].to_f / count[:total])*100,
+2 -5
View File
@@ -28,15 +28,12 @@
<td class="updated_on"><%= format_time(ver.updated_on) %></td> <td class="updated_on"><%= format_time(ver.updated_on) %></td>
<td class="author"><%= link_to_user ver.author %></td> <td class="author"><%= link_to_user ver.author %></td>
<td class="comments"><%=h ver.comments %></td> <td class="comments"><%=h ver.comments %></td>
<td class="buttons"> <td class="buttons"><%= link_to l(:button_annotate), :action => 'annotate', :id => @page.title, :version => ver.version %></td>
<%= link_to l(:button_annotate), :action => 'annotate', :id => @page.title, :version => ver.version %>
<%= delete_link wiki_page_path(@page, :version => ver.version) if User.current.allowed_to?(:delete_wiki_pages, @page.project) && @version_count > 1 %>
</td>
</tr> </tr>
<% line_num += 1 %> <% line_num += 1 %>
<% end %> <% end %>
</tbody> </tbody>
</table> </table>
<%= submit_tag l(:label_view_diff), :class => 'small' if show_diff %> <%= submit_tag l(:label_view_diff), :class => 'small' if show_diff %>
<span class="pagination"><%= pagination_links_full @version_pages, @version_count %></span> <span class="pagination"><%= pagination_links_full @version_pages, @version_count, :page_param => :p %></span>
<% end %> <% end %>
-13
View File
@@ -1,13 +0,0 @@
api.array :wiki_pages do
@pages.each do |page|
api.wiki_page do
api.title page.title
if page.parent
api.parent :title => page.parent.title
end
api.version page.version
api.created_on page.created_on
api.updated_on page.updated_on
end
end
end
-18
View File
@@ -1,18 +0,0 @@
api.wiki_page do
api.title @page.title
if @page.parent
api.parent :title => @page.parent.title
end
api.text @content.text
api.version @content.version
api.author(:id => @content.author_id, :name => @content.author.name)
api.comments @page.content.comments
api.created_on @page.created_on
api.updated_on @content.updated_on
api.array :attachments do
@page.attachments.each do |attachment|
render_api_attachment(attachment, api)
end
end if include_in_api_response?('attachments')
end
+11 -14
View File
@@ -1,15 +1,12 @@
<div class="contextual"> <div class="contextual">
<% if @editable %> <% if @editable %>
<% if @content.current_version? %> <%= link_to_if_authorized(l(:button_edit), {:action => 'edit', :id => @page.title}, :class => 'icon icon-edit', :accesskey => accesskey(:edit)) if @content.current_version? %>
<%= link_to_if_authorized(l(:button_edit), {:action => 'edit', :id => @page.title}, :class => 'icon icon-edit', :accesskey => accesskey(:edit)) %> <%= watcher_tag(@page, User.current) %>
<%= watcher_tag(@page, User.current) %> <%= link_to_if_authorized(l(:button_lock), {:action => 'protect', :id => @page.title, :protected => 1}, :method => :post, :class => 'icon icon-lock') if !@page.protected? %>
<%= link_to_if_authorized(l(:button_lock), {:action => 'protect', :id => @page.title, :protected => 1}, :method => :post, :class => 'icon icon-lock') if !@page.protected? %> <%= link_to_if_authorized(l(:button_unlock), {:action => 'protect', :id => @page.title, :protected => 0}, :method => :post, :class => 'icon icon-unlock') if @page.protected? %>
<%= link_to_if_authorized(l(:button_unlock), {:action => 'protect', :id => @page.title, :protected => 0}, :method => :post, :class => 'icon icon-unlock') if @page.protected? %> <%= link_to_if_authorized(l(:button_rename), {:action => 'rename', :id => @page.title}, :class => 'icon icon-move') if @content.current_version? %>
<%= link_to_if_authorized(l(:button_rename), {:action => 'rename', :id => @page.title}, :class => 'icon icon-move') %> <%= link_to_if_authorized(l(:button_delete), {:action => 'destroy', :id => @page.title}, :method => :delete, :data => {:confirm => l(:text_are_you_sure)}, :class => 'icon icon-del') %>
<%= link_to_if_authorized(l(:button_delete), {:action => 'destroy', :id => @page.title}, :method => :delete, :data => {:confirm => l(:text_are_you_sure)}, :class => 'icon icon-del') %> <%= link_to_if_authorized(l(:button_rollback), {:action => 'edit', :id => @page.title, :version => @content.version }, :class => 'icon icon-cancel') unless @content.current_version? %>
<% else %>
<%= link_to_if_authorized(l(:button_rollback), {:action => 'edit', :id => @page.title, :version => @content.version }, :class => 'icon icon-cancel') %>
<% end %>
<% end %> <% end %>
<%= link_to_if_authorized(l(:label_history), {:action => 'history', :id => @page.title}, :class => 'icon icon-history') %> <%= link_to_if_authorized(l(:label_history), {:action => 'history', :id => @page.title}, :class => 'icon icon-history') %>
</div> </div>
@@ -20,15 +17,15 @@
<p> <p>
<%= link_to(("\xc2\xab " + l(:label_previous)), <%= link_to(("\xc2\xab " + l(:label_previous)),
:action => 'show', :id => @page.title, :project_id => @page.project, :action => 'show', :id => @page.title, :project_id => @page.project,
:version => @content.previous.version) + " - " if @content.previous %> :version => (@content.version - 1)) + " - " if @content.version > 1 %>
<%= "#{l(:label_version)} #{@content.version}/#{@page.content.version}" %> <%= "#{l(:label_version)} #{@content.version}/#{@page.content.version}" %>
<%= '('.html_safe + link_to(l(:label_diff), :controller => 'wiki', :action => 'diff', <%= '('.html_safe + link_to(l(:label_diff), :controller => 'wiki', :action => 'diff',
:id => @page.title, :project_id => @page.project, :id => @page.title, :project_id => @page.project,
:version => @content.version) + ')'.html_safe if @content.previous %> - :version => @content.version) + ')'.html_safe if @content.version > 1 %> -
<%= link_to((l(:label_next) + " \xc2\xbb"), :action => 'show', <%= link_to((l(:label_next) + " \xc2\xbb"), :action => 'show',
:id => @page.title, :project_id => @page.project, :id => @page.title, :project_id => @page.project,
:version => @content.next.version) + " - " if @content.next %> :version => (@content.version + 1)) + " - " if @content.version < @page.content.version %>
<%= link_to(l(:label_current_version), :action => 'show', :id => @page.title, :project_id => @page.project, :version => nil) %> <%= link_to(l(:label_current_version), :action => 'show', :id => @page.title, :project_id => @page.project) %>
<br /> <br />
<em><%= @content.author ? link_to_user(@content.author) : l(:label_user_anonymous) <em><%= @content.author ? link_to_user(@content.author) : l(:label_user_anonymous)
%>, <%= format_time(@content.updated_on) %> </em><br /> %>, <%= format_time(@content.updated_on) %> </em><br />
-3
View File
@@ -50,9 +50,6 @@ module RedmineApp
config.action_mailer.perform_deliveries = false config.action_mailer.perform_deliveries = false
# Do not include all helpers
config.action_controller.include_all_helpers = false
config.session_store :cookie_store, :key => '_redmine_session' config.session_store :cookie_store, :key => '_redmine_session'
if File.exists?(File.join(File.dirname(__FILE__), 'additional_environment.rb')) if File.exists?(File.join(File.dirname(__FILE__), 'additional_environment.rb'))
-22
View File
@@ -166,28 +166,6 @@ default:
# the ImageMagick's `convert` binary. Used to generate attachment thumbnails. # the ImageMagick's `convert` binary. Used to generate attachment thumbnails.
#imagemagick_convert_command: #imagemagick_convert_command:
# Configuration of RMagcik font.
#
# Redmine uses RMagcik in order to export gantt png.
# You don't need this setting if you don't install RMagcik.
#
# In CJK (Chinese, Japanese and Korean),
# in order to show CJK characters correctly,
# you need to set this configuration.
#
# Because there is no standard font across platforms in CJK,
# you need to set a font installed in your server.
#
# This setting is not necessary in non CJK.
#
# Examples for Japanese:
# Windows:
# rmagick_font_path: C:\windows\fonts\msgothic.ttc
# Linux:
# rmagick_font_path: /usr/share/fonts/ipa-mincho/ipam.ttf
#
rmagick_font_path:
# specific configuration options for production environment # specific configuration options for production environment
# that overrides the default ones # that overrides the default ones
production: production:
+3 -3
View File
@@ -7,7 +7,7 @@ production:
database: redmine database: redmine
host: localhost host: localhost
username: root username: root
password: "" password:
encoding: utf8 encoding: utf8
development: development:
@@ -15,7 +15,7 @@ development:
database: redmine_development database: redmine_development
host: localhost host: localhost
username: root username: root
password: "" password:
encoding: utf8 encoding: utf8
# Warning: The database defined as "test" will be erased and # Warning: The database defined as "test" will be erased and
@@ -26,7 +26,7 @@ test:
database: redmine_test database: redmine_test
host: localhost host: localhost
username: root username: root
password: "" password:
encoding: utf8 encoding: utf8
test_pgsql: test_pgsql:
-18
View File
@@ -125,24 +125,6 @@ ActionMailer::Base.add_delivery_method :async_smtp, DeliveryMethods::AsyncSMTP
ActionMailer::Base.add_delivery_method :async_sendmail, DeliveryMethods::AsyncSendmail ActionMailer::Base.add_delivery_method :async_sendmail, DeliveryMethods::AsyncSendmail
ActionMailer::Base.add_delivery_method :tmp_file, DeliveryMethods::TmpFile ActionMailer::Base.add_delivery_method :tmp_file, DeliveryMethods::TmpFile
# Changes how sent emails are logged
# Rails doesn't log cc and bcc which is misleading when using bcc only (#12090)
module ActionMailer
class LogSubscriber < ActiveSupport::LogSubscriber
def deliver(event)
recipients = [:to, :cc, :bcc].inject("") do |s, header|
r = Array.wrap(event.payload[header])
if r.any?
s << "\n #{header}: #{r.join(', ')}"
end
s
end
info("\nSent email \"#{event.payload[:subject]}\" (%1.fms)#{recipients}" % event.duration)
debug(event.payload[:mail])
end
end
end
module ActionController module ActionController
module MimeResponds module MimeResponds
class Collector class Collector
+2 -1
View File
@@ -1,5 +1,6 @@
I18n.default_locale = 'en' I18n.default_locale = 'en'
I18n.backend = Redmine::I18n::Backend.new # Adds fallback to default locale for untranslated strings
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
require 'redmine' require 'redmine'
+1 -19
View File
@@ -899,6 +899,7 @@ ar:
text_subprojects_destroy_warning: "subproject(s): سيتم حذف أيضا." text_subprojects_destroy_warning: "subproject(s): سيتم حذف أيضا."
text_workflow_edit: حدد دوراً وتعقب لتحرير سير العمل text_workflow_edit: حدد دوراً وتعقب لتحرير سير العمل
text_are_you_sure: هل أنت متأكد؟ text_are_you_sure: هل أنت متأكد؟
text_are_you_sure_with_children: "حذف الموضوع وجميع المسائل المتعلقة بالطفل؟"
text_journal_changed: "%{label} تغير %{old} الى %{new}" text_journal_changed: "%{label} تغير %{old} الى %{new}"
text_journal_changed_no_detail: "%{label} تم التحديث" text_journal_changed_no_detail: "%{label} تم التحديث"
text_journal_set_to: "%{label} تغير الى %{value}" text_journal_set_to: "%{label} تغير الى %{value}"
@@ -1062,22 +1063,3 @@ ar:
label_attribute_of_assigned_to: Assignee's %{name} label_attribute_of_assigned_to: Assignee's %{name}
label_attribute_of_fixed_version: Target version's %{name} label_attribute_of_fixed_version: Target version's %{name}
label_copy_subtasks: Copy subtasks label_copy_subtasks: Copy subtasks
label_copied_to: copied to
label_copied_from: copied from
label_any_issues_in_project: any issues in project
label_any_issues_not_in_project: any issues not in project
field_private_notes: Private notes
permission_view_private_notes: View private notes
permission_set_notes_private: Set notes as private
label_no_issues_in_project: no issues in project
label_any: جميع
label_last_n_weeks: last %{count} weeks
setting_cross_project_subtasks: Allow cross-project subtasks
label_cross_project_descendants: يشارك
label_cross_project_tree: مع شجرة المشروع
label_cross_project_hierarchy: مع التسلسل الهرمي للمشروع
label_cross_project_system: مع جميع المشاريع
button_hide: Hide
setting_non_working_week_days: Non-working days
label_in_the_next_days: in the next
label_in_the_past_days: in the past
+3 -21
View File
@@ -332,7 +332,6 @@ bg:
field_core_fields: Стандартни полета field_core_fields: Стандартни полета
field_timeout: Таймаут (в секунди) field_timeout: Таймаут (в секунди)
field_board_parent: Родителски форум field_board_parent: Родителски форум
field_private_notes: Лични бележки
setting_app_title: Заглавие setting_app_title: Заглавие
setting_app_subtitle: Описание setting_app_subtitle: Описание
@@ -358,7 +357,6 @@ bg:
setting_date_format: Формат на датата setting_date_format: Формат на датата
setting_time_format: Формат на часа setting_time_format: Формат на часа
setting_cross_project_issue_relations: Релации на задачи между проекти setting_cross_project_issue_relations: Релации на задачи между проекти
setting_cross_project_subtasks: Подзадачи от други проекти
setting_issue_list_default_columns: Показвани колони по подразбиране setting_issue_list_default_columns: Показвани колони по подразбиране
setting_repositories_encodings: Кодова таблица на прикачените файлове и хранилищата setting_repositories_encodings: Кодова таблица на прикачените файлове и хранилищата
setting_emails_header: Emails header setting_emails_header: Emails header
@@ -400,7 +398,6 @@ bg:
setting_session_timeout: Таймаут за неактивност преди прекратяване на сесиите setting_session_timeout: Таймаут за неактивност преди прекратяване на сесиите
setting_thumbnails_enabled: Показване на миниатюри на прикачените изображения setting_thumbnails_enabled: Показване на миниатюри на прикачените изображения
setting_thumbnails_size: Размер на миниатюрите (в пиксели) setting_thumbnails_size: Размер на миниатюрите (в пиксели)
setting_non_working_week_days: Не работни дни
permission_add_project: Създаване на проект permission_add_project: Създаване на проект
permission_add_subprojects: Създаване на подпроекти permission_add_subprojects: Създаване на подпроекти
@@ -420,8 +417,6 @@ bg:
permission_add_issue_notes: Добавяне на бележки permission_add_issue_notes: Добавяне на бележки
permission_edit_issue_notes: Редактиране на бележки permission_edit_issue_notes: Редактиране на бележки
permission_edit_own_issue_notes: Редактиране на собствени бележки permission_edit_own_issue_notes: Редактиране на собствени бележки
permission_view_private_notes: Разглеждане на лични бележки
permission_set_notes_private: Установяване на бележките лични
permission_move_issues: Преместване на задачи permission_move_issues: Преместване на задачи
permission_delete_issues: Изтриване на задачи permission_delete_issues: Изтриване на задачи
permission_manage_public_queries: Управление на публичните заявки permission_manage_public_queries: Управление на публичните заявки
@@ -622,7 +617,6 @@ bg:
label_current_status: Текущо състояние label_current_status: Текущо състояние
label_new_statuses_allowed: Позволени състояния label_new_statuses_allowed: Позволени състояния
label_all: всички label_all: всички
label_any: която и да е
label_none: никакви label_none: никакви
label_nobody: никой label_nobody: никой
label_next: Следващ label_next: Следващ
@@ -657,8 +651,6 @@ bg:
label_not_equals: не е label_not_equals: не е
label_in_less_than: след по-малко от label_in_less_than: след по-малко от
label_in_more_than: след повече от label_in_more_than: след повече от
label_in_the_next_days: в следващите
label_in_the_past_days: в предишните
label_greater_or_equal: ">=" label_greater_or_equal: ">="
label_less_or_equal: <= label_less_or_equal: <=
label_between: между label_between: между
@@ -668,7 +660,6 @@ bg:
label_yesterday: вчера label_yesterday: вчера
label_this_week: тази седмица label_this_week: тази седмица
label_last_week: последната седмица label_last_week: последната седмица
label_last_n_weeks: последните %{count} седмици
label_last_n_days: "последните %{count} дни" label_last_n_days: "последните %{count} дни"
label_this_month: текущия месец label_this_month: текущия месец
label_last_month: последния месец label_last_month: последния месец
@@ -679,9 +670,6 @@ bg:
label_ago: преди label_ago: преди
label_contains: съдържа label_contains: съдържа
label_not_contains: не съдържа label_not_contains: не съдържа
label_any_issues_in_project: задачи от проект
label_any_issues_not_in_project: задачи, които не са в проект
label_no_issues_in_project: никакви задачи в проект
label_day_plural: дни label_day_plural: дни
label_repository: Хранилище label_repository: Хранилище
label_repository_new: Ново хранилище label_repository_new: Ново хранилище
@@ -757,8 +745,6 @@ bg:
label_blocked_by: блокирана от label_blocked_by: блокирана от
label_precedes: предшества label_precedes: предшества
label_follows: изпълнява се след label_follows: изпълнява се след
label_copied_to: копирана в
label_copied_from: копирана от
label_end_to_start: край към начало label_end_to_start: край към начало
label_end_to_end: край към край label_end_to_end: край към край
label_start_to_start: начало към начало label_start_to_start: начало към начало
@@ -833,7 +819,7 @@ bg:
label_generate_key: Генериране на ключ label_generate_key: Генериране на ключ
label_issue_watchers: Наблюдатели label_issue_watchers: Наблюдатели
label_example: Пример label_example: Пример
label_display: Показване label_display: Display
label_sort: Сортиране label_sort: Сортиране
label_ascending: Нарастващ label_ascending: Нарастващ
label_descending: Намаляващ label_descending: Намаляващ
@@ -886,13 +872,9 @@ bg:
label_attribute_of_author: Author's %{name} label_attribute_of_author: Author's %{name}
label_attribute_of_assigned_to: Assignee's %{name} label_attribute_of_assigned_to: Assignee's %{name}
label_attribute_of_fixed_version: Target version's %{name} label_attribute_of_fixed_version: Target version's %{name}
label_cross_project_descendants: С подпроекти
label_cross_project_tree: С дърво на проектите
label_cross_project_hierarchy: С проектна йерархия
label_cross_project_system: С всички проекти
button_login: Вход button_login: Вход
button_submit: Изпращане button_submit: Прикачване
button_save: Запис button_save: Запис
button_check_all: Избор на всички button_check_all: Избор на всички
button_uncheck_all: Изчистване на всички button_uncheck_all: Изчистване на всички
@@ -937,7 +919,6 @@ bg:
button_quote: Цитат button_quote: Цитат
button_duplicate: Дублиране button_duplicate: Дублиране
button_show: Показване button_show: Показване
button_hide: Скриване
button_edit_section: Редактиране на тази секция button_edit_section: Редактиране на тази секция
button_export: Експорт button_export: Експорт
button_delete_my_account: Премахване на моя профил button_delete_my_account: Премахване на моя профил
@@ -965,6 +946,7 @@ bg:
text_subprojects_destroy_warning: "Неговите подпроекти: %{value} също ще бъдат изтрити." text_subprojects_destroy_warning: "Неговите подпроекти: %{value} също ще бъдат изтрити."
text_workflow_edit: Изберете роля и тракер за да редактирате работния процес text_workflow_edit: Изберете роля и тракер за да редактирате работния процес
text_are_you_sure: Сигурни ли сте? text_are_you_sure: Сигурни ли сте?
text_are_you_sure_with_children: Изтриване на задачата и нейните подзадачи?
text_journal_changed: "%{label} променен от %{old} на %{new}" text_journal_changed: "%{label} променен от %{old} на %{new}"
text_journal_changed_no_detail: "%{label} променен" text_journal_changed_no_detail: "%{label} променен"
text_journal_set_to: "%{label} установен на %{value}" text_journal_set_to: "%{label} установен на %{value}"
+1 -19
View File
@@ -933,6 +933,7 @@ bs:
project_module_gantt: Gantt project_module_gantt: Gantt
project_module_calendar: Calendar project_module_calendar: Calendar
button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}" button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}"
text_are_you_sure_with_children: Delete issue and all child issues?
field_text: Text field field_text: Text field
label_user_mail_option_only_owner: Only for things I am the owner of label_user_mail_option_only_owner: Only for things I am the owner of
setting_default_notification_option: Default notification option setting_default_notification_option: Default notification option
@@ -1075,22 +1076,3 @@ bs:
label_attribute_of_assigned_to: Assignee's %{name} label_attribute_of_assigned_to: Assignee's %{name}
label_attribute_of_fixed_version: Target version's %{name} label_attribute_of_fixed_version: Target version's %{name}
label_copy_subtasks: Copy subtasks label_copy_subtasks: Copy subtasks
label_copied_to: copied to
label_copied_from: copied from
label_any_issues_in_project: any issues in project
label_any_issues_not_in_project: any issues not in project
field_private_notes: Private notes
permission_view_private_notes: View private notes
permission_set_notes_private: Set notes as private
label_no_issues_in_project: no issues in project
label_any: sve
label_last_n_weeks: last %{count} weeks
setting_cross_project_subtasks: Allow cross-project subtasks
label_cross_project_descendants: With subprojects
label_cross_project_tree: With project tree
label_cross_project_hierarchy: With project hierarchy
label_cross_project_system: With all projects
button_hide: Hide
setting_non_working_week_days: Non-working days
label_in_the_next_days: in the next
label_in_the_past_days: in the past
+1 -19
View File
@@ -922,6 +922,7 @@ ca:
enumeration_system_activity: Activitat del sistema enumeration_system_activity: Activitat del sistema
button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}" button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}"
text_are_you_sure_with_children: Delete issue and all child issues?
field_text: Text field field_text: Text field
label_user_mail_option_only_owner: Only for things I am the owner of label_user_mail_option_only_owner: Only for things I am the owner of
setting_default_notification_option: Default notification option setting_default_notification_option: Default notification option
@@ -1064,22 +1065,3 @@ ca:
label_attribute_of_assigned_to: Assignee's %{name} label_attribute_of_assigned_to: Assignee's %{name}
label_attribute_of_fixed_version: Target version's %{name} label_attribute_of_fixed_version: Target version's %{name}
label_copy_subtasks: Copy subtasks label_copy_subtasks: Copy subtasks
label_copied_to: copied to
label_copied_from: copied from
label_any_issues_in_project: any issues in project
label_any_issues_not_in_project: any issues not in project
field_private_notes: Private notes
permission_view_private_notes: View private notes
permission_set_notes_private: Set notes as private
label_no_issues_in_project: no issues in project
label_any: tots
label_last_n_weeks: last %{count} weeks
setting_cross_project_subtasks: Allow cross-project subtasks
label_cross_project_descendants: "Amb tots els subprojectes"
label_cross_project_tree: "Amb l'arbre del projecte"
label_cross_project_hierarchy: "Amb la jerarquia del projecte"
label_cross_project_system: "Amb tots els projectes"
button_hide: Hide
setting_non_working_week_days: Non-working days
label_in_the_next_days: in the next
label_in_the_past_days: in the past
+117 -136
View File
@@ -1,4 +1,3 @@
# Update to 2.2 by Karel Picman <karel.picman@kontron.com>
# Update to 1.1 by Michal Gebauer <mishak@mishak.net> # Update to 1.1 by Michal Gebauer <mishak@mishak.net>
# Updated by Josef Liška <jl@chl.cz> # Updated by Josef Liška <jl@chl.cz>
# CZ translation by Maxim Krušina | Massimo Filippi, s.r.o. | maxim@mxm.cz # CZ translation by Maxim Krušina | Massimo Filippi, s.r.o. | maxim@mxm.cz
@@ -141,7 +140,7 @@ cs:
general_text_Yes: 'Ano' general_text_Yes: 'Ano'
general_text_no: 'ne' general_text_no: 'ne'
general_text_yes: 'ano' general_text_yes: 'ano'
general_lang_name: 'Czech (Čeština)' general_lang_name: 'Čeština'
general_csv_separator: ',' general_csv_separator: ','
general_csv_decimal_separator: '.' general_csv_decimal_separator: '.'
general_csv_encoding: UTF-8 general_csv_encoding: UTF-8
@@ -864,6 +863,7 @@ cs:
text_subprojects_destroy_warning: "Jeho podprojek(y): %{value} budou také smazány." text_subprojects_destroy_warning: "Jeho podprojek(y): %{value} budou také smazány."
text_workflow_edit: Vyberte roli a frontu k editaci průběhu práce text_workflow_edit: Vyberte roli a frontu k editaci průběhu práce
text_are_you_sure: Jste si jisti? text_are_you_sure: Jste si jisti?
text_are_you_sure_with_children: Smazat úkol včetně všech podúkolů?
text_journal_changed: "%{label} změněn z %{old} na %{new}" text_journal_changed: "%{label} změněn z %{old} na %{new}"
text_journal_set_to: "%{label} nastaven na %{value}" text_journal_set_to: "%{label} nastaven na %{value}"
text_journal_deleted: "%{label} smazán (%{old})" text_journal_deleted: "%{label} smazán (%{old})"
@@ -942,146 +942,127 @@ cs:
enumeration_activities: Aktivity (sledování času) enumeration_activities: Aktivity (sledování času)
enumeration_system_activity: Systémová aktivita enumeration_system_activity: Systémová aktivita
field_warn_on_leaving_unsaved: Varuj mě před opuštěním stránky s neuloženým textem field_warn_on_leaving_unsaved: Warn me when leaving a page with unsaved text
text_warn_on_leaving_unsaved: Aktuální stránka obsahuje neuložený text, který bude ztracen, když opustíte stránku. text_warn_on_leaving_unsaved: The current page contains unsaved text that will be lost if you leave this page.
label_my_queries: Moje vlastní dotazy label_my_queries: My custom queries
text_journal_changed_no_detail: "%{label} aktualizován" text_journal_changed_no_detail: "%{label} updated"
label_news_comment_added: K novince byl přidán komentář label_news_comment_added: Comment added to a news
button_expand_all: Rozbal vše button_expand_all: Expand all
button_collapse_all: Sbal vše button_collapse_all: Collapse all
label_additional_workflow_transitions_for_assignee: Další změna stavu povolena, jestliže je uživatel přiřazen label_additional_workflow_transitions_for_assignee: Additional transitions allowed when the user is the assignee
label_additional_workflow_transitions_for_author: Další změna stavu povolena, jestliže je uživatel autorem label_additional_workflow_transitions_for_author: Additional transitions allowed when the user is the author
label_bulk_edit_selected_time_entries: Hromadná změna záznamů času label_bulk_edit_selected_time_entries: Bulk edit selected time entries
text_time_entries_destroy_confirmation: Jste si jistí, že chcete smazat vybraný záznam(y) času? text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies)?
label_role_anonymous: Anonymní label_role_anonymous: Anonymous
label_role_non_member: Není členem label_role_non_member: Non member
label_issue_note_added: Přidána poznámka label_issue_note_added: Note added
label_issue_status_updated: Aktualizován stav label_issue_status_updated: Status updated
label_issue_priority_updated: Aktualizována priorita label_issue_priority_updated: Priority updated
label_issues_visibility_own: Úkol vytvořen nebo přiřazen uživatel(i/em) label_issues_visibility_own: Issues created by or assigned to the user
field_issues_visibility: Viditelnost úkolů field_issues_visibility: Issues visibility
label_issues_visibility_all: Všechny úkoly label_issues_visibility_all: All issues
permission_set_own_issues_private: Nastavit vlastní úkoly jako veřejné nebo soukromé permission_set_own_issues_private: Set own issues public or private
field_is_private: Soukromý field_is_private: Private
permission_set_issues_private: Nastavit úkoly jako veřejné nebo soukromé permission_set_issues_private: Set issues public or private
label_issues_visibility_public: Všechny úkoly, které nejsou soukromé label_issues_visibility_public: All non private issues
text_issues_destroy_descendants_confirmation: "%{count} podúkol(ů) bude rovněž smazán(o)." text_issues_destroy_descendants_confirmation: This will also delete %{count} subtask(s).
field_commit_logs_encoding: Kódování zpráv při commitu field_commit_logs_encoding: Kódování zpráv při commitu
field_scm_path_encoding: Kódování cesty SCM field_scm_path_encoding: Path encoding
text_scm_path_encoding_note: "Výchozí: UTF-8" text_scm_path_encoding_note: "Default: UTF-8"
field_path_to_repository: Cesta k repositáři field_path_to_repository: Path to repository
field_root_directory: Kořenový adresář field_root_directory: Root directory
field_cvs_module: Modul field_cvs_module: Module
field_cvsroot: CVSROOT field_cvsroot: CVSROOT
text_mercurial_repository_note: Lokální repositář (např. /hgrepo, c:\hgrepo) text_mercurial_repository_note: Local repository (e.g. /hgrepo, c:\hgrepo)
text_scm_command: Příkaz text_scm_command: Command
text_scm_command_version: Verze text_scm_command_version: Version
label_git_report_last_commit: Reportovat poslední commit pro soubory a adresáře label_git_report_last_commit: Report last commit for files and directories
text_scm_config: Můžete si nastavit vaše SCM příkazy v config/configuration.yml. Restartujte, prosím, aplikaci po jejich úpravě. text_scm_config: You can configure your scm commands in config/configuration.yml. Please restart the application after editing it.
text_scm_command_not_available: SCM příkaz není k dispozici. Zkontrolujte, prosím, nastavení v panelu Administrace. text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel.
notice_issue_successful_create: Úkol %{id} vytvořen. notice_issue_successful_create: Issue %{id} created.
label_between: mezi label_between: between
setting_issue_group_assignment: Povolit přiřazení úkolu skupině setting_issue_group_assignment: Allow issue assignment to groups
label_diff: rozdíl label_diff: diff
text_git_repository_note: Repositář je "bare and local" (např. /gitrepo, c:\gitrepo) text_git_repository_note: Repository is bare and local (e.g. /gitrepo, c:\gitrepo)
description_query_sort_criteria_direction: Směr třídění description_query_sort_criteria_direction: Sort direction
description_project_scope: Rozsah vyhledávání description_project_scope: Search scope
description_filter: Filtr description_filter: Filter
description_user_mail_notification: Nastavení emailových notifikací description_user_mail_notification: Mail notification settings
description_date_from: Zadejte počáteční datum description_date_from: Enter start date
description_message_content: Obsah zprávy description_message_content: Message content
description_available_columns: Dostupné sloupce description_available_columns: Available Columns
description_date_range_interval: Zvolte rozsah výběrem počátečního a koncového data description_date_range_interval: Choose range by selecting start and end date
description_issue_category_reassign: Zvolte kategorii úkolu description_issue_category_reassign: Choose issue category
description_search: Vyhledávací pole description_search: Searchfield
description_notes: Poznámky description_notes: Notes
description_date_range_list: Zvolte rozsah ze seznamu description_date_range_list: Choose range from list
description_choose_project: Projekty description_choose_project: Projects
description_date_to: Zadejte datum description_date_to: Enter end date
description_query_sort_criteria_attribute: Třídící atribut description_query_sort_criteria_attribute: Sort attribute
description_wiki_subpages_reassign: Zvolte novou rodičovskou stránku description_wiki_subpages_reassign: Choose new parent page
description_selected_columns: Vybraný sloupec description_selected_columns: Selected Columns
label_parent_revision: Rodič label_parent_revision: Parent
label_child_revision: Potomek label_child_revision: Child
error_scm_annotate_big_text_file: Vstup nemůže být anotován, protože překračuje povolenou velikost textového souboru error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
setting_default_issue_start_date_to_creation_date: Použij aktuální datum jako počáteční datum pro nové úkoly setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
button_edit_section: Uprav tuto sekci button_edit_section: Edit this section
setting_repositories_encodings: Kódování příloh a repositářů setting_repositories_encodings: Attachments and repositories encodings
description_all_columns: Všechny sloupce description_all_columns: All Columns
button_export: Export button_export: Export
label_export_options: "nastavení exportu %{export_format}" label_export_options: "%{export_format} export options"
error_attachment_too_big: Soubor nemůže být nahrán, protože jeho velikost je větší než maximum (%{max_size}) error_attachment_too_big: This file cannot be uploaded because it exceeds the maximum allowed file size (%{max_size})
notice_failed_to_save_time_entries: "Chyba při ukládání %{count} časov(ých/ého) záznam(ů) z %{total} vybraného: %{ids}." notice_failed_to_save_time_entries: "Failed to save %{count} time entrie(s) on %{total} selected: %{ids}."
label_x_issues: label_x_issues:
zero: 0 Úkol zero: 0 Úkol
one: 1 Úkol one: 1 Úkol
other: "%{count} Úkoly" other: "%{count} Úkoly"
label_repository_new: Nový repositář label_repository_new: New repository
field_repository_is_default: Hlavní repositář field_repository_is_default: Main repository
label_copy_attachments: Kopírovat přílohy label_copy_attachments: Copy attachments
label_item_position: "%{position}/%{count}" label_item_position: "%{position}/%{count}"
label_completed_versions: Dokončené verze label_completed_versions: Completed versions
text_project_identifier_info: Jsou povolena pouze malá písmena (a-z), číslice, pomlčky a podtržítka.<br />Po uložení již nelze identifikátor měnit. text_project_identifier_info: Only lower case letters (a-z), numbers, dashes and underscores are allowed.<br />Once saved, the identifier cannot be changed.
field_multiple: Více hodnot field_multiple: Multiple values
setting_commit_cross_project_ref: Povolit reference a opravy úklů ze všech ostatních projektů setting_commit_cross_project_ref: Allow issues of all the other projects to be referenced and fixed
text_issue_conflict_resolution_add_notes: Přidat moje poznámky a zahodit ostatní změny text_issue_conflict_resolution_add_notes: Add my notes and discard my other changes
text_issue_conflict_resolution_overwrite: Přesto přijmout moje úpravy (předchozí poznámky budou zachovány, ale některé změny mohou být přepsány) text_issue_conflict_resolution_overwrite: Apply my changes anyway (previous notes will be kept but some changes may be overwritten)
notice_issue_update_conflict: Během vašich úprav byl úkol aktualizován jiným uživatelem. notice_issue_update_conflict: The issue has been updated by an other user while you were editing it.
text_issue_conflict_resolution_cancel: Zahoď všechny moje změny a znovu zobraz %{link} text_issue_conflict_resolution_cancel: Discard all my changes and redisplay %{link}
permission_manage_related_issues: Spravuj související úkoly permission_manage_related_issues: Manage related issues
field_auth_source_ldap_filter: LDAP filtr field_auth_source_ldap_filter: LDAP filter
label_search_for_watchers: Hledej sledující pro přidání label_search_for_watchers: Search for watchers to add
notice_account_deleted: Váš účet byl trvale smazán. notice_account_deleted: Your account has been permanently deleted.
setting_unsubscribe: Povolit uživatelům smazání jejich vlastního účtu setting_unsubscribe: Allow users to delete their own account
button_delete_my_account: Smazat můj účet button_delete_my_account: Delete my account
text_account_destroy_confirmation: |- text_account_destroy_confirmation: |-
Skutečně chcete pokračovat? Are you sure you want to proceed?
Váš účet bude nenávratně smazán. Your account will be permanently deleted, with no way to reactivate it.
error_session_expired: Vaše sezení vypršelo. Znovu se přihlaste, prosím. error_session_expired: Your session has expired. Please login again.
text_session_expiration_settings: "Varování: změnou tohoto nastavení mohou vypršet aktuální sezení včetně toho vašeho." text_session_expiration_settings: "Warning: changing these settings may expire the current sessions including yours."
setting_session_lifetime: Maximální čas sezení setting_session_lifetime: Session maximum lifetime
setting_session_timeout: Vypršení sezení bez aktivity setting_session_timeout: Session inactivity timeout
label_session_expiration: Vypršení sezení label_session_expiration: Session expiration
permission_close_project: Zavřít / Otevřít projekt permission_close_project: Close / reopen the project
label_show_closed_projects: Zobrazit zavřené projekty label_show_closed_projects: View closed projects
button_close: Zavřít button_close: Close
button_reopen: Znovu otevřít button_reopen: Reopen
project_status_active: aktivní project_status_active: active
project_status_closed: zavřený project_status_closed: closed
project_status_archived: archivovaný project_status_archived: archived
text_project_closed: Tento projekt je uzevřený a je pouze pro čtení. text_project_closed: This project is closed and read-only.
notice_user_successful_create: Uživatel %{id} vytvořen. notice_user_successful_create: User %{id} created.
field_core_fields: Standardní pole field_core_fields: Standard fields
field_timeout: Vypršení (v sekundách) field_timeout: Timeout (in seconds)
setting_thumbnails_enabled: Zobrazit náhled přílohy setting_thumbnails_enabled: Display attachment thumbnails
setting_thumbnails_size: Velikost náhledu (v pixelech) setting_thumbnails_size: Thumbnails size (in pixels)
label_status_transitions: Změna stavu label_status_transitions: Status transitions
label_fields_permissions: Práva k polím label_fields_permissions: Fields permissions
label_readonly: Pouze pro čtení label_readonly: Read-only
label_required: Vyžadováno label_required: Required
text_repository_identifier_info: Jou povoleny pouze malá písmena (a-z), číslice, pomlčky a podtržítka.<br />Po uložení již nelze identifikátor změnit. text_repository_identifier_info: Only lower case letters (a-z), numbers, dashes and underscores are allowed.<br />Once saved, the identifier cannot be changed.
field_board_parent: Rodičovské fórum field_board_parent: Parent forum
label_attribute_of_project: Projektové %{name} label_attribute_of_project: Project's %{name}
label_attribute_of_author: Autorovo %{name} label_attribute_of_author: Author's %{name}
label_attribute_of_assigned_to: "%{name} přiřazené(ho)" label_attribute_of_assigned_to: Assignee's %{name}
label_attribute_of_fixed_version: Cílová verze %{name} label_attribute_of_fixed_version: Target version's %{name}
label_copy_subtasks: Kopírovat podúkoly label_copy_subtasks: Copy subtasks
label_copied_to: zkopírováno do
label_copied_from: zkopírováno z
label_any_issues_in_project: jakékoli úkoly v projektu
label_any_issues_not_in_project: jakékoli úkoly mimo projektu
field_private_notes: Soukromé poznámky
permission_view_private_notes: Zobrazit soukromé poznámky
permission_set_notes_private: Nastavit poznámky jako soukromé
label_no_issues_in_project: žádné úkoly v projektu
label_any: vše
label_last_n_weeks: poslední %{count} týdny
setting_cross_project_subtasks: Povolit podúkoly napříč projekty
label_cross_project_descendants: S podprojekty
label_cross_project_tree: Se stromem projektu
label_cross_project_hierarchy: S hierarchií projektu
label_cross_project_system: Se všemi projekty
button_hide: Skrýt
setting_non_working_week_days: Dny pracovního volna/klidu
label_in_the_next_days: v přístích
label_in_the_past_days: v minulých
+1 -19
View File
@@ -936,6 +936,7 @@ da:
project_module_gantt: Gantt project_module_gantt: Gantt
project_module_calendar: Kalender project_module_calendar: Kalender
button_edit_associated_wikipage: "Redigér tilknyttet Wiki side: %{page_title}" button_edit_associated_wikipage: "Redigér tilknyttet Wiki side: %{page_title}"
text_are_you_sure_with_children: Slet sag og alle undersager?
field_text: Tekstfelt field_text: Tekstfelt
label_user_mail_option_only_owner: Kun for ting jeg er ejer af label_user_mail_option_only_owner: Kun for ting jeg er ejer af
setting_default_notification_option: Standardpåmindelsesmulighed setting_default_notification_option: Standardpåmindelsesmulighed
@@ -1079,22 +1080,3 @@ da:
label_attribute_of_assigned_to: Assignee's %{name} label_attribute_of_assigned_to: Assignee's %{name}
label_attribute_of_fixed_version: Target version's %{name} label_attribute_of_fixed_version: Target version's %{name}
label_copy_subtasks: Copy subtasks label_copy_subtasks: Copy subtasks
label_copied_to: copied to
label_copied_from: copied from
label_any_issues_in_project: any issues in project
label_any_issues_not_in_project: any issues not in project
field_private_notes: Private notes
permission_view_private_notes: View private notes
permission_set_notes_private: Set notes as private
label_no_issues_in_project: no issues in project
label_any: alle
label_last_n_weeks: last %{count} weeks
setting_cross_project_subtasks: Allow cross-project subtasks
label_cross_project_descendants: Med underprojekter
label_cross_project_tree: Med projekttræ
label_cross_project_hierarchy: Med projekthierarki
label_cross_project_system: Med alle projekter
button_hide: Hide
setting_non_working_week_days: Non-working days
label_in_the_next_days: in the next
label_in_the_past_days: in the past
+5 -23
View File
@@ -322,7 +322,7 @@ de:
setting_app_title: Applikations-Titel setting_app_title: Applikations-Titel
setting_app_subtitle: Applikations-Untertitel setting_app_subtitle: Applikations-Untertitel
setting_welcome_text: Willkommenstext setting_welcome_text: Willkommenstext
setting_default_language: Standardsprache setting_default_language: Default-Sprache
setting_login_required: Authentifizierung erforderlich setting_login_required: Authentifizierung erforderlich
setting_self_registration: Anmeldung ermöglicht setting_self_registration: Anmeldung ermöglicht
setting_attachment_max_size: Max. Dateigröße setting_attachment_max_size: Max. Dateigröße
@@ -343,7 +343,7 @@ de:
setting_date_format: Datumsformat setting_date_format: Datumsformat
setting_time_format: Zeitformat setting_time_format: Zeitformat
setting_cross_project_issue_relations: Ticket-Beziehungen zwischen Projekten erlauben setting_cross_project_issue_relations: Ticket-Beziehungen zwischen Projekten erlauben
setting_issue_list_default_columns: Standard-Spalten in der Ticket-Auflistung setting_issue_list_default_columns: Default-Spalten in der Ticket-Auflistung
setting_emails_footer: E-Mail-Fußzeile setting_emails_footer: E-Mail-Fußzeile
setting_protocol: Protokoll setting_protocol: Protokoll
setting_per_page_options: Objekte pro Seite setting_per_page_options: Objekte pro Seite
@@ -698,7 +698,7 @@ de:
label_blocks: Blockiert label_blocks: Blockiert
label_blocked_by: Blockiert durch label_blocked_by: Blockiert durch
label_precedes: Vorgänger von label_precedes: Vorgänger von
label_follows: Folgt label_follows: folgt
label_end_to_start: Ende - Anfang label_end_to_start: Ende - Anfang
label_end_to_end: Ende - Ende label_end_to_end: Ende - Ende
label_start_to_start: Anfang - Anfang label_start_to_start: Anfang - Anfang
@@ -860,6 +860,7 @@ de:
text_subprojects_destroy_warning: "Dessen Unterprojekte (%{value}) werden ebenfalls gelöscht." text_subprojects_destroy_warning: "Dessen Unterprojekte (%{value}) werden ebenfalls gelöscht."
text_workflow_edit: Workflow zum Bearbeiten auswählen text_workflow_edit: Workflow zum Bearbeiten auswählen
text_are_you_sure: Sind Sie sicher? text_are_you_sure: Sind Sie sicher?
text_are_you_sure_with_children: "Lösche Aufgabe und alle Unteraufgaben?"
text_journal_changed: "%{label} wurde von %{old} zu %{new} geändert" text_journal_changed: "%{label} wurde von %{old} zu %{new} geändert"
text_journal_set_to: "%{label} wurde auf %{value} gesetzt" text_journal_set_to: "%{label} wurde auf %{value} gesetzt"
text_journal_deleted: "%{label} %{old} wurde gelöscht" text_journal_deleted: "%{label} %{old} wurde gelöscht"
@@ -994,7 +995,7 @@ de:
notice_issue_successful_create: Ticket %{id} erstellt. notice_issue_successful_create: Ticket %{id} erstellt.
label_between: zwischen label_between: zwischen
setting_issue_group_assignment: Ticketzuweisung an Gruppen erlauben setting_issue_group_assignment: Erlaubt die Ticket-Zuweisung an Gruppen
label_diff: diff label_diff: diff
text_git_repository_note: Repository steht für sich alleine (bare) und liegt lokal (z.B. /gitrepo, c:\gitrepo) text_git_repository_note: Repository steht für sich alleine (bare) und liegt lokal (z.B. /gitrepo, c:\gitrepo)
@@ -1078,22 +1079,3 @@ de:
label_attribute_of_assigned_to: "%{name} des Bearbeiters" label_attribute_of_assigned_to: "%{name} des Bearbeiters"
label_attribute_of_fixed_version: "%{name} der Zielversion" label_attribute_of_fixed_version: "%{name} der Zielversion"
label_copy_subtasks: Unteraufgaben kopieren label_copy_subtasks: Unteraufgaben kopieren
label_copied_to: Kopiert nach
label_copied_from: Kopiert von
label_any_issues_in_project: irgendein Ticket im Projekt
label_any_issues_not_in_project: irgendein Ticket nicht im Projekt
field_private_notes: Privater Kommentar
permission_view_private_notes: Private Kommentare sehen
permission_set_notes_private: Kommentar als privat markieren
label_no_issues_in_project: keine Tickets im Projekt
label_any: alle
label_last_n_weeks: letzte %{count} Wochen
setting_cross_project_subtasks: Projektübergreifende Unteraufgaben erlauben
label_cross_project_descendants: Mit Unterprojekten
label_cross_project_tree: Mit Projektbaum
label_cross_project_hierarchy: Mit Projekthierarchie
label_cross_project_system: Mit allen Projekten
button_hide: Verstecken
setting_non_working_week_days: Arbeitsfreie Tage
label_in_the_next_days: in den nächsten
label_in_the_past_days: in den letzten
+2 -20
View File
@@ -137,7 +137,7 @@ el:
general_text_Yes: 'Ναι' general_text_Yes: 'Ναι'
general_text_no: 'όχι' general_text_no: 'όχι'
general_text_yes: 'ναι' general_text_yes: 'ναι'
general_lang_name: 'Greek (Ελληνικά)' general_lang_name: 'Ελληνικά'
general_csv_separator: ',' general_csv_separator: ','
general_csv_decimal_separator: '.' general_csv_decimal_separator: '.'
general_csv_encoding: UTF-8 general_csv_encoding: UTF-8
@@ -920,6 +920,7 @@ el:
project_module_gantt: Gantt project_module_gantt: Gantt
project_module_calendar: Calendar project_module_calendar: Calendar
button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}" button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}"
text_are_you_sure_with_children: Delete issue and all child issues?
field_text: Text field field_text: Text field
label_user_mail_option_only_owner: Only for things I am the owner of label_user_mail_option_only_owner: Only for things I am the owner of
setting_default_notification_option: Default notification option setting_default_notification_option: Default notification option
@@ -1062,22 +1063,3 @@ el:
label_attribute_of_assigned_to: Assignee's %{name} label_attribute_of_assigned_to: Assignee's %{name}
label_attribute_of_fixed_version: Target version's %{name} label_attribute_of_fixed_version: Target version's %{name}
label_copy_subtasks: Copy subtasks label_copy_subtasks: Copy subtasks
label_copied_to: copied to
label_copied_from: copied from
label_any_issues_in_project: any issues in project
label_any_issues_not_in_project: any issues not in project
field_private_notes: Private notes
permission_view_private_notes: View private notes
permission_set_notes_private: Set notes as private
label_no_issues_in_project: no issues in project
label_any: όλα
label_last_n_weeks: last %{count} weeks
setting_cross_project_subtasks: Allow cross-project subtasks
label_cross_project_descendants: With subprojects
label_cross_project_tree: With project tree
label_cross_project_hierarchy: With project hierarchy
label_cross_project_system: With all projects
button_hide: Hide
setting_non_working_week_days: Non-working days
label_in_the_next_days: in the next
label_in_the_past_days: in the past
+1 -19
View File
@@ -873,6 +873,7 @@ en-GB:
text_subprojects_destroy_warning: "Its subproject(s): %{value} will be also deleted." text_subprojects_destroy_warning: "Its subproject(s): %{value} will be also deleted."
text_workflow_edit: Select a role and a tracker to edit the workflow text_workflow_edit: Select a role and a tracker to edit the workflow
text_are_you_sure: Are you sure? text_are_you_sure: Are you sure?
text_are_you_sure_with_children: "Delete issue and all child issues?"
text_journal_changed: "%{label} changed from %{old} to %{new}" text_journal_changed: "%{label} changed from %{old} to %{new}"
text_journal_changed_no_detail: "%{label} updated" text_journal_changed_no_detail: "%{label} updated"
text_journal_set_to: "%{label} set to %{value}" text_journal_set_to: "%{label} set to %{value}"
@@ -1064,22 +1065,3 @@ en-GB:
label_attribute_of_assigned_to: Assignee's %{name} label_attribute_of_assigned_to: Assignee's %{name}
label_attribute_of_fixed_version: Target version's %{name} label_attribute_of_fixed_version: Target version's %{name}
label_copy_subtasks: Copy subtasks label_copy_subtasks: Copy subtasks
label_copied_to: copied to
label_copied_from: copied from
label_any_issues_in_project: any issues in project
label_any_issues_not_in_project: any issues not in project
field_private_notes: Private notes
permission_view_private_notes: View private notes
permission_set_notes_private: Set notes as private
label_no_issues_in_project: no issues in project
label_any: all
label_last_n_weeks: last %{count} weeks
setting_cross_project_subtasks: Allow cross-project subtasks
label_cross_project_descendants: With subprojects
label_cross_project_tree: With project tree
label_cross_project_hierarchy: With project hierarchy
label_cross_project_system: With all projects
button_hide: Hide
setting_non_working_week_days: Non-working days
label_in_the_next_days: in the next
label_in_the_past_days: in the past
+8 -26
View File
@@ -331,7 +331,6 @@ en:
field_core_fields: Standard fields field_core_fields: Standard fields
field_timeout: "Timeout (in seconds)" field_timeout: "Timeout (in seconds)"
field_board_parent: Parent forum field_board_parent: Parent forum
field_private_notes: Private notes
setting_app_title: Application title setting_app_title: Application title
setting_app_subtitle: Application subtitle setting_app_subtitle: Application subtitle
@@ -357,7 +356,6 @@ en:
setting_date_format: Date format setting_date_format: Date format
setting_time_format: Time format setting_time_format: Time format
setting_cross_project_issue_relations: Allow cross-project issue relations setting_cross_project_issue_relations: Allow cross-project issue relations
setting_cross_project_subtasks: Allow cross-project subtasks
setting_issue_list_default_columns: Default columns displayed on the issue list setting_issue_list_default_columns: Default columns displayed on the issue list
setting_repositories_encodings: Attachments and repositories encodings setting_repositories_encodings: Attachments and repositories encodings
setting_emails_header: Emails header setting_emails_header: Emails header
@@ -399,7 +397,6 @@ en:
setting_session_timeout: Session inactivity timeout setting_session_timeout: Session inactivity timeout
setting_thumbnails_enabled: Display attachment thumbnails setting_thumbnails_enabled: Display attachment thumbnails
setting_thumbnails_size: Thumbnails size (in pixels) setting_thumbnails_size: Thumbnails size (in pixels)
setting_non_working_week_days: Non-working days
permission_add_project: Create project permission_add_project: Create project
permission_add_subprojects: Create subprojects permission_add_subprojects: Create subprojects
@@ -419,8 +416,6 @@ en:
permission_add_issue_notes: Add notes permission_add_issue_notes: Add notes
permission_edit_issue_notes: Edit notes permission_edit_issue_notes: Edit notes
permission_edit_own_issue_notes: Edit own notes permission_edit_own_issue_notes: Edit own notes
permission_view_private_notes: View private notes
permission_set_notes_private: Set notes as private
permission_move_issues: Move issues permission_move_issues: Move issues
permission_delete_issues: Delete issues permission_delete_issues: Delete issues
permission_manage_public_queries: Manage public queries permission_manage_public_queries: Manage public queries
@@ -621,7 +616,6 @@ en:
label_current_status: Current status label_current_status: Current status
label_new_statuses_allowed: New statuses allowed label_new_statuses_allowed: New statuses allowed
label_all: all label_all: all
label_any: any
label_none: none label_none: none
label_nobody: nobody label_nobody: nobody
label_next: Next label_next: Next
@@ -656,8 +650,6 @@ en:
label_not_equals: is not label_not_equals: is not
label_in_less_than: in less than label_in_less_than: in less than
label_in_more_than: in more than label_in_more_than: in more than
label_in_the_next_days: in the next
label_in_the_past_days: in the past
label_greater_or_equal: '>=' label_greater_or_equal: '>='
label_less_or_equal: '<=' label_less_or_equal: '<='
label_between: between label_between: between
@@ -667,7 +659,6 @@ en:
label_yesterday: yesterday label_yesterday: yesterday
label_this_week: this week label_this_week: this week
label_last_week: last week label_last_week: last week
label_last_n_weeks: "last %{count} weeks"
label_last_n_days: "last %{count} days" label_last_n_days: "last %{count} days"
label_this_month: this month label_this_month: this month
label_last_month: last month label_last_month: last month
@@ -678,9 +669,6 @@ en:
label_ago: days ago label_ago: days ago
label_contains: contains label_contains: contains
label_not_contains: doesn't contain label_not_contains: doesn't contain
label_any_issues_in_project: any issues in project
label_any_issues_not_in_project: any issues not in project
label_no_issues_in_project: no issues in project
label_day_plural: days label_day_plural: days
label_repository: Repository label_repository: Repository
label_repository_new: New repository label_repository_new: New repository
@@ -749,15 +737,13 @@ en:
label_loading: Loading... label_loading: Loading...
label_relation_new: New relation label_relation_new: New relation
label_relation_delete: Delete relation label_relation_delete: Delete relation
label_relates_to: Related to label_relates_to: related to
label_duplicates: Duplicates label_duplicates: duplicates
label_duplicated_by: Duplicated by label_duplicated_by: duplicated by
label_blocks: Blocks label_blocks: blocks
label_blocked_by: Blocked by label_blocked_by: blocked by
label_precedes: Precedes label_precedes: precedes
label_follows: Follows label_follows: follows
label_copied_to: Copied to
label_copied_from: Copied from
label_end_to_start: end to start label_end_to_start: end to start
label_end_to_end: end to end label_end_to_end: end to end
label_start_to_start: start to start label_start_to_start: start to start
@@ -885,10 +871,6 @@ en:
label_attribute_of_author: "Author's %{name}" label_attribute_of_author: "Author's %{name}"
label_attribute_of_assigned_to: "Assignee's %{name}" label_attribute_of_assigned_to: "Assignee's %{name}"
label_attribute_of_fixed_version: "Target version's %{name}" label_attribute_of_fixed_version: "Target version's %{name}"
label_cross_project_descendants: With subprojects
label_cross_project_tree: With project tree
label_cross_project_hierarchy: With project hierarchy
label_cross_project_system: With all projects
button_login: Login button_login: Login
button_submit: Submit button_submit: Submit
@@ -936,7 +918,6 @@ en:
button_quote: Quote button_quote: Quote
button_duplicate: Duplicate button_duplicate: Duplicate
button_show: Show button_show: Show
button_hide: Hide
button_edit_section: Edit this section button_edit_section: Edit this section
button_export: Export button_export: Export
button_delete_my_account: Delete my account button_delete_my_account: Delete my account
@@ -964,6 +945,7 @@ en:
text_subprojects_destroy_warning: "Its subproject(s): %{value} will be also deleted." text_subprojects_destroy_warning: "Its subproject(s): %{value} will be also deleted."
text_workflow_edit: Select a role and a tracker to edit the workflow text_workflow_edit: Select a role and a tracker to edit the workflow
text_are_you_sure: Are you sure? text_are_you_sure: Are you sure?
text_are_you_sure_with_children: "Delete issue and all child issues?"
text_journal_changed: "%{label} changed from %{old} to %{new}" text_journal_changed: "%{label} changed from %{old} to %{new}"
text_journal_changed_no_detail: "%{label} updated" text_journal_changed_no_detail: "%{label} updated"
text_journal_set_to: "%{label} set to %{value}" text_journal_set_to: "%{label} set to %{value}"
+55 -73
View File
@@ -159,8 +159,8 @@ es:
abbr_day_names: [Dom, Lun, Mar, Mie, Jue, Vie, Sab] abbr_day_names: [Dom, Lun, Mar, Mie, Jue, Vie, Sab]
# Don't forget the nil at the beginning; there's no such thing as a 0th month # Don't forget the nil at the beginning; there's no such thing as a 0th month
month_names: [~, Enero, Febrero, Marzo, Abril, Mayo, Junio, Julio, Agosto, Septiembre, Octubre, Noviembre, Diciembre] month_names: [~, Enero, Febrero, Marzo, Abril, Mayo, Junio, Julio, Agosto, Setiembre, Octubre, Noviembre, Diciembre]
abbr_month_names: [~, Ene, Feb, Mar, Abr, May, Jun, Jul, Ago, Sep, Oct, Nov, Dic] abbr_month_names: [~, Ene, Feb, Mar, Abr, May, Jun, Jul, Ago, Set, Oct, Nov, Dic]
# Used in date_select and datime_select. # Used in date_select and datime_select.
order: order:
- :year - :year
@@ -957,6 +957,7 @@ es:
project_module_gantt: Gantt project_module_gantt: Gantt
project_module_calendar: Calendario project_module_calendar: Calendario
button_edit_associated_wikipage: "Editar paginas Wiki asociadas: %{page_title}" button_edit_associated_wikipage: "Editar paginas Wiki asociadas: %{page_title}"
text_are_you_sure_with_children: ¿Borrar peticiones y todas sus peticiones hijas?
field_text: Campo de texto field_text: Campo de texto
label_user_mail_option_only_owner: Solo para objetos que soy propietario label_user_mail_option_only_owner: Solo para objetos que soy propietario
setting_default_notification_option: Opcion de notificacion por defecto setting_default_notification_option: Opcion de notificacion por defecto
@@ -1037,84 +1038,65 @@ es:
description_selected_columns: Columnas seleccionadas description_selected_columns: Columnas seleccionadas
label_parent_revision: Padre label_parent_revision: Padre
label_child_revision: Hijo label_child_revision: Hijo
setting_default_issue_start_date_to_creation_date: Utilizar la fecha actual como fecha de inicio para nuevas peticiones setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
button_edit_section: Editar esta sección button_edit_section: Edit this section
setting_repositories_encodings: Codificación de adjuntos y repositorios setting_repositories_encodings: Attachments and repositories encodings
description_all_columns: Todas las columnas description_all_columns: Todas las columnas
button_export: Exportar button_export: Exportar
label_export_options: "%{export_format} opciones de exportación" label_export_options: "%{export_format} opciones de exportación"
error_attachment_too_big: Este fichero no se puede adjuntar porque excede el tamaño máximo de fichero (%{max_size}) error_attachment_too_big: Este fichero no se puede adjuntar porque excede el tamaño máximo de fichero (%{max_size})
notice_failed_to_save_time_entries: "Error al guardar %{count} entradas de tiempo de las %{total} seleccionadas: %{ids}." notice_failed_to_save_time_entries: "Error al guarda %{count} entradas de tiempo de las %{total} seleccionadas: %{ids}."
label_x_issues: label_x_issues:
zero: 0 petición zero: 0 petición
one: 1 petición one: 1 petición
other: "%{count} peticiones" other: "%{count} peticiones"
label_repository_new: Nuevo repositorio label_repository_new: New repository
field_repository_is_default: Repositorio principal field_repository_is_default: Main repository
label_copy_attachments: Copiar adjuntos label_copy_attachments: Copy attachments
label_item_position: "%{position}/%{count}" label_item_position: "%{position}/%{count}"
label_completed_versions: Versiones completadas label_completed_versions: Completed versions
text_project_identifier_info: Solo se permiten letras en minúscula (a-z), números, guiones y barras bajas.<br />Una vez guardado, el identificador no se puede cambiar. text_project_identifier_info: Only lower case letters (a-z), numbers, dashes and underscores are allowed.<br />Once saved, the identifier cannot be changed.
field_multiple: Valores múltiples field_multiple: Multiple values
setting_commit_cross_project_ref: Permitir referenciar y resolver peticiones de todos los demás proyectos setting_commit_cross_project_ref: Allow issues of all the other projects to be referenced and fixed
text_issue_conflict_resolution_add_notes: Añadir mis notas y descartar mis otros cambios text_issue_conflict_resolution_add_notes: Add my notes and discard my other changes
text_issue_conflict_resolution_overwrite: Aplicar mis campos de todas formas (las notas anteriores se mantendrán pero algunos cambios podrían ser sobreescritos) text_issue_conflict_resolution_overwrite: Apply my changes anyway (previous notes will be kept but some changes may be overwritten)
notice_issue_update_conflict: La petición ha sido actualizada por otro usuario mientras la editaba. notice_issue_update_conflict: The issue has been updated by an other user while you were editing it.
text_issue_conflict_resolution_cancel: Descartar todos mis cambios y mostrar de nuevo %{link} text_issue_conflict_resolution_cancel: Discard all my changes and redisplay %{link}
permission_manage_related_issues: Gestionar peticiones relacionadas permission_manage_related_issues: Manage related issues
field_auth_source_ldap_filter: Filtro LDAP field_auth_source_ldap_filter: LDAP filter
label_search_for_watchers: Buscar seguidores para añadirlos label_search_for_watchers: Search for watchers to add
notice_account_deleted: Su cuenta ha sido eliminada notice_account_deleted: Your account has been permanently deleted.
setting_unsubscribe: Permitir a los usuarios borrar sus propias cuentas setting_unsubscribe: Allow users to delete their own account
button_delete_my_account: Borrar mi cuenta button_delete_my_account: Delete my account
text_account_destroy_confirmation: |- text_account_destroy_confirmation: |-
¿Está seguro de querer proceder? Are you sure you want to proceed?
Su cuenta quedará borrada permanentemente, sin la posibilidad de reactivarla. Your account will be permanently deleted, with no way to reactivate it.
error_session_expired: Su sesión ha expirado. Por favor, vuelva a identificarse. error_session_expired: Your session has expired. Please login again.
text_session_expiration_settings: "Advertencia: el cambio de estas opciones podría hacer expirar las sesiones activas, incluyendo la suya." text_session_expiration_settings: "Warning: changing these settings may expire the current sessions including yours."
setting_session_lifetime: Tiempo de vida máximo de las sesiones setting_session_lifetime: Session maximum lifetime
setting_session_timeout: Tiempo máximo de inactividad de las sesiones setting_session_timeout: Session inactivity timeout
label_session_expiration: Expiración de sesiones label_session_expiration: Session expiration
permission_close_project: Cerrar / reabrir el proyecto permission_close_project: Close / reopen the project
label_show_closed_projects: Ver proyectos cerrados label_show_closed_projects: View closed projects
button_close: Cerrar button_close: Close
button_reopen: Reabrir button_reopen: Reopen
project_status_active: activo project_status_active: active
project_status_closed: cerrado project_status_closed: closed
project_status_archived: archivado project_status_archived: archived
text_project_closed: Este proyecto está cerrado y es de sólo lectura text_project_closed: This project is closed and read-only.
notice_user_successful_create: Usuario %{id} creado. notice_user_successful_create: User %{id} created.
field_core_fields: Campos básicos field_core_fields: Standard fields
field_timeout: Tiempo de inactividad (en segundos) field_timeout: Timeout (in seconds)
setting_thumbnails_enabled: Mostrar miniaturas de los adjuntos setting_thumbnails_enabled: Display attachment thumbnails
setting_thumbnails_size: Tamaño de las miniaturas (en píxeles) setting_thumbnails_size: Thumbnails size (in pixels)
label_status_transitions: Transiciones de estado label_status_transitions: Status transitions
label_fields_permissions: Permisos sobre los campos label_fields_permissions: Fields permissions
label_readonly: Sólo lectura label_readonly: Read-only
label_required: Requerido label_required: Required
text_repository_identifier_info: Solo se permiten letras en minúscula (a-z), números, guiones y barras bajas.<br />Una vez guardado, el identificador no se puede cambiar. text_repository_identifier_info: Only lower case letters (a-z), numbers, dashes and underscores are allowed.<br />Once saved, the identifier cannot be changed.
field_board_parent: Foro padre field_board_parent: Parent forum
label_attribute_of_project: "%{name} del proyecto" label_attribute_of_project: Project's %{name}
label_attribute_of_author: "%{name} del autor" label_attribute_of_author: Author's %{name}
label_attribute_of_assigned_to: "%{name} de la persona asignada" label_attribute_of_assigned_to: Assignee's %{name}
label_attribute_of_fixed_version: "%{name} de la versión indicada" label_attribute_of_fixed_version: Target version's %{name}
label_copy_subtasks: Copiar subtareas label_copy_subtasks: Copy subtasks
label_copied_to: copiada a
label_copied_from: copiada desde
label_any_issues_in_project: cualquier petición del proyecto
label_any_issues_not_in_project: cualquier petición que no sea del proyecto
field_private_notes: Notas privadas
permission_view_private_notes: Ver notas privadas
permission_set_notes_private: Poner notas como privadas
label_no_issues_in_project: no hay peticiones en el proyecto
label_any: todos
label_last_n_weeks: en las últimas %{count} semanas
setting_cross_project_subtasks: Permitir subtareas cruzadas entre proyectos
label_cross_project_descendants: Con proyectos hijo
label_cross_project_tree: Con el árbol del proyecto
label_cross_project_hierarchy: Con la jerarquía del proyecto
label_cross_project_system: Con todos los proyectos
button_hide: Ocultar
setting_non_working_week_days: Días no laborables
label_in_the_next_days: en los próximos
label_in_the_past_days: en los anteriores
+1 -19
View File
@@ -935,6 +935,7 @@ et:
text_subprojects_destroy_warning: "Alamprojekt(id) - %{value} - kustutatakse samuti." text_subprojects_destroy_warning: "Alamprojekt(id) - %{value} - kustutatakse samuti."
text_workflow_edit: "Töövoo muutmiseks vali roll ja valdkond" text_workflow_edit: "Töövoo muutmiseks vali roll ja valdkond"
text_are_you_sure: "Oled Sa kindel?" text_are_you_sure: "Oled Sa kindel?"
text_are_you_sure_with_children: "Kustutada teema ja kõik tema alamteemad?"
text_journal_changed: "%{label} muudetud %{old} -> %{new}" text_journal_changed: "%{label} muudetud %{old} -> %{new}"
text_journal_changed_no_detail: "%{label} uuendatud" text_journal_changed_no_detail: "%{label} uuendatud"
text_journal_set_to: "%{label} uus väärtus on %{value}" text_journal_set_to: "%{label} uus väärtus on %{value}"
@@ -1075,22 +1076,3 @@ et:
label_attribute_of_assigned_to: Assignee's %{name} label_attribute_of_assigned_to: Assignee's %{name}
label_attribute_of_fixed_version: Target version's %{name} label_attribute_of_fixed_version: Target version's %{name}
label_copy_subtasks: Copy subtasks label_copy_subtasks: Copy subtasks
label_copied_to: copied to
label_copied_from: copied from
label_any_issues_in_project: any issues in project
label_any_issues_not_in_project: any issues not in project
field_private_notes: Private notes
permission_view_private_notes: View private notes
permission_set_notes_private: Set notes as private
label_no_issues_in_project: no issues in project
label_any: "kõik"
label_last_n_weeks: last %{count} weeks
setting_cross_project_subtasks: Allow cross-project subtasks
label_cross_project_descendants: "alamprojektidega"
label_cross_project_tree: "projektipuuga"
label_cross_project_hierarchy: "projektihierarhiaga"
label_cross_project_system: "kõigi projektidega"
button_hide: Hide
setting_non_working_week_days: Non-working days
label_in_the_next_days: in the next
label_in_the_past_days: in the past

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