Compare commits

...

1056 Commits
0.9.1 ... 1.1.2

Author SHA1 Message Date
Jean-Philippe Lang
b33ecc41f9 tagged version 1.1.2
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/tags/1.1.2@5032 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-03-07 19:48:50 +00:00
Jean-Philippe Lang
78a4a995e6 Merged r5030 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5031 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-03-07 19:27:14 +00:00
Jean-Philippe Lang
465534a298 Merged r5021 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5022 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-03-06 16:12:27 +00:00
Jean-Philippe Lang
d5f1bd07b2 Updates for 1.1.2 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5020 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-03-06 13:19:48 +00:00
Jean-Philippe Lang
50863117b8 Translations updates.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5018 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-03-06 13:12:50 +00:00
Jean-Philippe Lang
c6693fc78b Merged r4937 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5017 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-03-06 13:08:02 +00:00
Jean-Philippe Lang
1e01711e3d Merged r4946 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5015 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-03-06 12:57:03 +00:00
Jean-Philippe Lang
1ab7f6f930 Merged r4820 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5014 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-03-06 12:54:59 +00:00
Jean-Philippe Lang
f6f7467cdd Merged r4913, r4914, r4916 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5013 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-03-06 12:51:10 +00:00
Jean-Philippe Lang
4d0a955d3c Merged r5004 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5012 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-03-06 12:48:40 +00:00
Jean-Philippe Lang
c45044f13c Merged r4888 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5011 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-03-06 12:47:05 +00:00
Jean-Philippe Lang
4761e55691 Merged r4951 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5010 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-03-06 12:45:56 +00:00
Jean-Philippe Lang
af7fb657f4 Merged r4889 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4973 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-02-28 20:36:06 +00:00
Jean-Philippe Lang
0805ab943e Merged r4890 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4972 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-02-28 20:34:31 +00:00
Jean-Philippe Lang
9589c0bcad Merged r4811 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4971 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-02-28 20:33:31 +00:00
Jean-Philippe Lang
91295ea6cd Merged r4935 and r4947 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4970 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-02-28 20:32:25 +00:00
Jean-Philippe Lang
d0f4b5aa50 Merged r4968 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4969 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-02-28 20:27:44 +00:00
Jean-Philippe Lang
008e8d4fbf Merged r4965 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4966 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-02-28 16:45:02 +00:00
Toshi MARUYAMA
1e50fea55a Merged r4860 from trunk.
scm: fix diff revision param validation.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4877 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-02-18 13:48:29 +00:00
Toshi MARUYAMA
008d38d6b4 Merged r4816 from trunk.
scm: fix non ASCII filename downloaded from repo is broken on Internet Explorer.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4819 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-02-12 09:51:43 +00:00
Toshi MARUYAMA
c61c9e6471 Merged r4815 from trunk.
scm: cvs: fix most binary files become corrupted on Windows.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4818 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-02-12 09:51:21 +00:00
Jean-Baptiste Barth
e5b5b61d6e Merged r4813 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4814 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-02-12 09:03:36 +00:00
Jean-Baptiste Barth
6a8cdf54b3 Removed .project file and added it to svn:ignore'd files. #7497
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4812 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-02-12 08:56:19 +00:00
Jean-Baptiste Barth
e43d98a6f5 Merged r4799 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4800 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-02-06 00:34:21 +00:00
Jean-Philippe Lang
11b774d39d Merged r4784 from trunk (update for 1.1.1 release).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4785 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-30 14:38:25 +00:00
Jean-Philippe Lang
77c4667dbc Merged r4782 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4783 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-30 09:13:36 +00:00
Jean-Philippe Lang
f05fdd5cfa Merged r4780 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4781 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-30 06:54:51 +00:00
Jean-Philippe Lang
a0bb70ed2d Merged r4765 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4779 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-30 06:45:15 +00:00
Jean-Philippe Lang
1d5c3f7fba Translations updates.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4778 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-30 06:43:27 +00:00
Jean-Philippe Lang
8b83aa1470 Merged r4775 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4776 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-30 06:32:10 +00:00
Jean-Philippe Lang
3a92721af4 Merged r4739 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4774 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-30 06:22:38 +00:00
Jean-Philippe Lang
b3218ba4d4 Merged r4700 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4773 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-30 06:16:06 +00:00
Jean-Philippe Lang
975ee2b522 Merged r4701 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4772 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-30 06:11:51 +00:00
Jean-Philippe Lang
27a319e66d Merged r4727 and r4730 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4771 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-30 06:10:36 +00:00
Jean-Philippe Lang
7be5bf6e4d Merged r4741 and r4764 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4770 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-30 06:08:01 +00:00
Jean-Philippe Lang
c73d4042d1 Merged r4677 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4769 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-30 06:05:54 +00:00
Jean-Philippe Lang
8270ad1e64 Merged r4736 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4768 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-30 06:04:40 +00:00
Jean-Philippe Lang
26016fbf43 Merged r4761 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4767 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-30 06:02:16 +00:00
Jean-Philippe Lang
307e4ceaa2 Merged r4708 and r4709 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4766 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-30 06:00:56 +00:00
Jean-Philippe Lang
03085e85f9 Merged r4720, r4724, r4738 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4763 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-27 17:51:01 +00:00
Jean-Philippe Lang
1f4e0dc10c Merged r4719 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4762 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-27 17:44:40 +00:00
Toshi MARUYAMA
f69c95306d Merged r4749 from trunk.
scm: darcs: fix Darcs adapter recognizes new files as modified files above Darcs 2.4.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4751 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-23 04:21:57 +00:00
Toshi MARUYAMA
634ede3e8b Merged r4748 from trunk.
scm: darcs: add compatible test of Darcs 2.3 and 2.4.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4750 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-23 04:21:34 +00:00
Toshi MARUYAMA
c2a2979189 Merged r4744 from trunk.
scm: darcs: switch '.' or @url at entries() in darcs version.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4747 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-23 04:03:04 +00:00
Toshi MARUYAMA
9800469943 Merged r4743 from trunk.
scm: darcs: change io.gets to io.read and add darcs version unit test.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4746 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-23 04:02:41 +00:00
Toshi MARUYAMA
e47f1d5595 Merged r4742 from trunk.
scm: darcs: add unit lib test.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4745 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-23 04:02:15 +00:00
Jean-Philippe Lang
135fe04d02 Merged r4681 from trunk (missing fixtures breaking tests).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4732 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-16 16:39:19 +00:00
Jean-Philippe Lang
794d7c0959 Merged r4680 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4728 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-16 14:44:38 +00:00
Toshi MARUYAMA
b8f365f2a1 Merged r4713 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4717 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-14 23:37:50 +00:00
Toshi MARUYAMA
6188b9eddb Merged r4710 and r4714 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4716 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-14 23:37:30 +00:00
Toshi MARUYAMA
96c4dc3f1e Merged r4712 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4715 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-14 23:37:10 +00:00
Toshi MARUYAMA
b877215261 Merged r4703 from trunk (scm: fix error on revision page for empty revision).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4707 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-13 12:10:52 +00:00
Toshi MARUYAMA
788b143596 Merged r4665 from trunk.
scm: add compatible functional test fof changing diff revisions label at SCM adapter level.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4706 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-13 12:10:30 +00:00
Toshi MARUYAMA
6a261eb5a0 Merged r4691 from trunk (fix assert_equal parameter order in subversion and git unit test).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4693 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-11 15:28:23 +00:00
Toshi MARUYAMA
007fbc00cf Merged r4687 from trunk (fix setup mercurial test repository).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4689 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-11 08:46:27 +00:00
Toshi MARUYAMA
9b8b4b3bfc Merged r4683 from trunk (test:scm:setup:mercurial task can be simpler).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4685 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-11 04:22:52 +00:00
Toshi MARUYAMA
6734f91a72 Merged r4676 from trunk (change mercurial test repository from tar.gz to bundle).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4684 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-11 04:22:31 +00:00
Jean-Philippe Lang
5266346315 Set the version to stable (#7259).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4678 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-10 18:13:09 +00:00
Toshi MARUYAMA
d426e4452b Merged r4644 and r4674 from trunk (mercurial unit app test).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4675 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-09 23:39:46 +00:00
Jean-Philippe Lang
f8af1bebd7 Merged r4670 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4671 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-09 15:58:15 +00:00
Jean-Philippe Lang
6e695a4d1a Merged r4645 to r4651 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4660 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-08 10:14:00 +00:00
Jean-Philippe Lang
e5b7c94cb4 Merged r4643 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4659 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-08 10:09:04 +00:00
Jean-Philippe Lang
9028e664c4 Merged r4656 and r4657 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4658 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-08 10:04:38 +00:00
Toshi MARUYAMA
6318affd31 Merged r4652 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4653 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-08 00:24:43 +00:00
Jean-Philippe Lang
8794cd3344 Merged r4610 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4649 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-06 21:04:24 +00:00
Toshi MARUYAMA
2fcd4e5271 Merged r4636 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4642 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-05 15:12:08 +00:00
Toshi MARUYAMA
ea60705ca7 Merged r4635 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4641 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-05 15:11:47 +00:00
Toshi MARUYAMA
90bce4e366 Merged r4634 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4640 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-05 15:11:21 +00:00
Toshi MARUYAMA
9f7cc355ad Merged r4633 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4639 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-05 15:10:58 +00:00
Toshi MARUYAMA
6ee4c0bac7 Merged r4632 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4638 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-05 15:10:38 +00:00
Toshi MARUYAMA
b0f0bd1848 Merged r4631 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4637 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-05 15:10:17 +00:00
Toshi MARUYAMA
fe563a8802 Merged r4629 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4630 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-04 16:39:45 +00:00
Toshi MARUYAMA
8ebab00767 Merged r4625 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4628 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-03 10:55:07 +00:00
Toshi MARUYAMA
d97297e45d Merged r4624 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4627 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-03 10:54:29 +00:00
Toshi MARUYAMA
f06500dcce Merged r4623 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4626 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-03 10:53:47 +00:00
Jean-Philippe Lang
b098e2b63f Merged r4621 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4622 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-02 14:37:55 +00:00
Jean-Philippe Lang
151a49b319 Merged r4618 and r4619 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4620 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-02 12:17:39 +00:00
Jean-Philippe Lang
ded234794e Merged r4615 and r4616 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4617 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-02 11:44:19 +00:00
Toshi MARUYAMA
119732c3ee Merged r4613 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4614 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-02 09:52:29 +00:00
Toshi MARUYAMA
730fcef844 Merged r4611 from trunk (Mercurial sorting).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4612 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-02 06:14:59 +00:00
Toshi MARUYAMA
1cb33f3a95 Merged r4608 from trunk (repository: switch darcs cat test if cat supports).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4609 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-01 22:05:48 +00:00
Toshi MARUYAMA
69edd3c53f Merged r4606 from trunk (.hgignore).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4607 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-01 21:20:25 +00:00
Jean-Philippe Lang
ad784e2146 Merged locales updates (r4592 to 4595) from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4596 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-30 15:54:47 +00:00
Jean-Philippe Lang
c783ae4b3d 1.1-stable branch added
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4585 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-30 15:09:18 +00:00
Jean-Philippe Lang
dd87ebef58 Makes the png looks more like the html gantt.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4584 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-30 15:04:08 +00:00
Jean-Philippe Lang
82f528ddc8 Makes issue move form similar to bulk edit form.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4583 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-29 20:48:51 +00:00
Jean-Philippe Lang
ad62060753 Disable project completion display on the gantt (#7127).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4582 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-29 20:29:42 +00:00
Jean-Philippe Lang
f2ae2e9239 Simple issue sort method to make sure subtasks appear under their parent on the gantt (#7128).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4581 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-29 20:21:39 +00:00
Jean-Philippe Lang
d2cc2861de Find visible issues only in ContextMenusController#issues.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4580 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-29 19:55:52 +00:00
Jean-Philippe Lang
e9a0e6dbc8 Takes more parameters into account to determine activity freshness (#7188).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4579 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-29 19:37:42 +00:00
Jean-Philippe Lang
af7006dff6 Fixed: partial toc when text contains pre tags (#7172).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4578 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-29 18:21:22 +00:00
Jean-Philippe Lang
0025a66200 Fixed: cvs diff broken by r4539 (#7176).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4577 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-29 17:46:47 +00:00
Jean-Philippe Lang
03d4ecbbff Fixed: CLI-supplied defaults should not be applied when replying to an issue (#7195).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4576 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-29 17:38:57 +00:00
Jean-Philippe Lang
21c0b868ae Fixed: Tracker reset to default when replying to an issue email (#7197).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4575 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-29 17:36:01 +00:00
Jean-Philippe Lang
df9ea24136 Makes Version#start_date return the minimum start_date of its issues.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4574 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 14:58:52 +00:00
Jean-Philippe Lang
07fe46e9df Makes the API accepts the X-Redmine-API-Key header to hold the API key.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4573 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 14:49:14 +00:00
Jean-Philippe Lang
283d25d823 Makes /projects API accept same pagination parameters as other API collection resources.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4572 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 13:34:44 +00:00
Jean-Philippe Lang
d076c19822 Makes API accept offset/limit or page/limit parameters for retrieving collections.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4571 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 13:33:01 +00:00
Jean-Philippe Lang
1ee7f31f4d Set VERSION to 1.0.5.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4568 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 10:04:08 +00:00
Jean-Philippe Lang
d78a510642 Updated INSTALL for 1.0.5
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4567 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 10:03:32 +00:00
Jean-Philippe Lang
3ef41c3131 Updated CHANGELOG for 1.0.5
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4566 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 10:02:08 +00:00
Jean-Philippe Lang
759048e73a Fixes: Mercurial adapter loses seconds of commit times (#6656).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4561 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 09:56:30 +00:00
Jean-Philippe Lang
3b53ec20e6 Adds themes test for when Redmine is in a sub-uri.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4560 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 09:42:33 +00:00
Jean-Philippe Lang
c8dc7fff08 Avoid theme rescan when no theme is selected.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4559 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-22 21:41:08 +00:00
Jean-Philippe Lang
523febf9c1 Support for Javascript in Themes (#2803).
If javascripts/theme.js is found in the theme directory, it will be automatically loaded on each page.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4558 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-22 21:37:07 +00:00
Jean-Philippe Lang
72f58c4c40 Adds missing not_a_date translation in fr locale (#7160).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4556 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-22 20:52:48 +00:00
Jean-Philippe Lang
0661cf6d87 Restores non english field_start_date translations (#6629, #7016).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4554 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-22 20:40:56 +00:00
Jean-Philippe Lang
703b0ec422 Fixed: 500 error on issue query grouped by a custom field that was deleted (#7144).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4553 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-21 21:46:54 +00:00
Jean-Philippe Lang
3d76a67a2e Force vertical scroll bar to avoid layout jump.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4552 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-21 17:59:38 +00:00
Jean-Philippe Lang
ef288fbff7 Broken links on wiki diff view.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4551 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-20 22:33:44 +00:00
Jean-Philippe Lang
8a8ca40364 Prevents n SQL queries (n = project count) on cross-project issues list.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4550 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-20 20:03:22 +00:00
Jean-Philippe Lang
03397f605c Fixes valid revision regexp.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4549 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-20 18:47:54 +00:00
Jean-Philippe Lang
c42b0ad6b7 Adds missing fixtures when running tests from scratch.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4548 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-20 18:30:36 +00:00
Jean-Philippe Lang
4695754d2a Fixes task description.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4547 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-20 18:24:49 +00:00
Jean-Philippe Lang
339bcc97e0 Adds tasks to run scm tests: test:scm:units, test:scm:functionals and test:scm.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4546 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-20 18:24:24 +00:00
Jean-Philippe Lang
6d4126f17e Adds routing tests for users and xml format.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4545 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-20 18:06:14 +00:00
Jean-Philippe Lang
599bc45073 Adds support for requesting information about current user using /users/current (#7141).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4544 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-20 17:45:09 +00:00
Jean-Philippe Lang
84dd413f22 Restore rev param validation that was removed in r2840.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4542 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-18 18:37:49 +00:00
Jean-Philippe Lang
f7529c94f6 Fixes mercurial adapter.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4541 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-18 18:35:37 +00:00
Jean-Philippe Lang
7d7c67daba Fixed that some arguments where not properly escaped in scm adapters.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4539 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-18 18:12:12 +00:00
Jean-Philippe Lang
a7595ec191 Adds tests for class attribute parsing on pre/code tags.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4537 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-18 17:21:57 +00:00
Jean-Philippe Lang
93847ae337 Fixes a data disclosure issue introduced in r3941.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4535 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-18 17:11:41 +00:00
Jean-Philippe Lang
525656a490 Ported subtasks display with indentation to the new gantt (#7128) and fixed markers alignment.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4534 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-18 16:06:20 +00:00
Jean-Philippe Lang
86e17ce74f Sort versions drop down on the bulk edit form.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4533 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-18 14:14:04 +00:00
Jean-Philippe Lang
0c24f88ce1 Fixed: issue description Quote button lost by r3941 (#7122).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4530 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-18 13:40:50 +00:00
Jean-Philippe Lang
df88dc117f Fixed: r4492 breaks the ability to select issue custom fields available for projects issues (#7121).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4529 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-17 16:10:46 +00:00
Jean-Philippe Lang
620c1e2789 Adds a css class for gantt subjects.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4528 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-17 15:25:07 +00:00
Jean-Philippe Lang
8a86b2d2bc Gantt code cleaning.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4527 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-17 15:21:38 +00:00
Jean-Philippe Lang
084e9e2e57 Gantt code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4526 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-17 14:59:32 +00:00
Jean-Philippe Lang
74a94ed4ad Gantt: make the png looks more like html.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4525 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-17 14:53:30 +00:00
Jean-Philippe Lang
88a802b25e Gantt: make the pdf looks more like html.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4524 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-17 14:37:51 +00:00
Jean-Philippe Lang
edc35d4d5b Gantt code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4523 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-17 14:05:43 +00:00
Jean-Philippe Lang
98c7c179ca Gantt code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4522 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-17 13:40:25 +00:00
Jean-Philippe Lang
8f7da03419 Gantt code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4521 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-17 12:41:54 +00:00
Jean-Philippe Lang
27f76d20ce Gantt: fixes progress width in some cases and start code cleaning.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4520 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-17 12:24:11 +00:00
Jean-Philippe Lang
1c3823cbb0 Adds missing strings in locales.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4519 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-17 09:14:54 +00:00
Jean-Philippe Lang
bc1703d7b6 Converts translations to the new i18n interpolation format (#6495).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4518 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-17 08:50:13 +00:00
Jean-Philippe Lang
4f4a62c6d0 Locales update before changing interpolation format.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4517 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-17 08:40:15 +00:00
Jean-Philippe Lang
3c1576e364 Manually require i18n 0.4.2 before Rails tries to load the most recent gem (#7013).
This workaround makes Redmine work even if i18n 0.5.0 gem is installed.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4516 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-17 08:34:29 +00:00
Jean-Philippe Lang
9f18426ca7 Skip a few SQL queries for cross project gantt.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4515 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-15 21:26:51 +00:00
Jean-Philippe Lang
252dcad3f6 Locales update.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4514 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-15 21:18:28 +00:00
Jean-Philippe Lang
b48291ec63 Adds an application setting to limit the number of items that can be displayed on the gantt chart (#6276).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4513 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-15 21:18:06 +00:00
Jean-Philippe Lang
6a586c39e9 Adds journal created_on attribute to issue API (#7111).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4512 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-14 19:03:17 +00:00
Jean-Philippe Lang
335f8da5e8 Fixed: 404 when entering time with blank issue id (#7099).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4511 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-14 18:29:24 +00:00
Jean-Baptiste Barth
9ac043a096 Set encoding to utf-8 for ruby 1.9 compatibility. #4050
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4510 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-12 23:24:45 +00:00
Jean-Baptiste Barth
17f86d964f Use absolute paths in test/**/* requires for Ruby 1.9.2 compatibility. #4050
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4509 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-12 23:24:34 +00:00
Jean-Baptiste Barth
f9f1bd58d6 Fixed bad html generated in users/memberships partial (multiple </tbody> tags)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4508 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-12 21:21:57 +00:00
Jean-Philippe Lang
e120f2aab4 Fixes tests after fixtures changes.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4507 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-12 19:43:15 +00:00
Jean-Philippe Lang
a8cf13e8c8 Restores the issue description label (#6677).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4506 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-12 17:12:05 +00:00
Jean-Philippe Lang
ac60fc9c1c Refactor and add tests for News #index API (#7072).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4505 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-12 17:00:52 +00:00
Jean-Philippe Lang
aaee26ae22 Use GET instead of POST to retrieve context menu (#6762).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4504 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-12 16:26:24 +00:00
Jean-Philippe Lang
29e0bca43a Make sure there's no nil result in auto_complete.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4503 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-12 16:06:43 +00:00
Jean-Philippe Lang
a8b12bcb52 Autocomplete issue relations on subject (#3170).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4502 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-12 16:04:28 +00:00
Jean-Philippe Lang
758f2f0a4e UsersController tests cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4501 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-12 15:34:35 +00:00
Jean-Philippe Lang
a49c7f95e2 Fixes password sending when creating user.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4500 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-12 15:26:55 +00:00
Jean-Philippe Lang
0a2ec6ef04 Extracts user groups assignment from controller.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4499 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-12 15:13:34 +00:00
Jean-Philippe Lang
87ae744dce Fixes test according to r4493.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4498 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-12 14:43:55 +00:00
Jean-Philippe Lang
2066b2f666 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4497 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-12 14:25:23 +00:00
Jean-Philippe Lang
9e2d401f43 Moves mail_notification param to user hash param so that it can be set using the User API.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4496 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-12 14:19:24 +00:00
Jean-Philippe Lang
1d4f28a54d Removed no longer needed to_sym.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4495 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-12 14:05:19 +00:00
Jean-Philippe Lang
e4f319fe61 Validates user's mail_notification and turn options into strings (the attribute type).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4494 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-12 14:02:39 +00:00
Jean-Philippe Lang
cde02954c8 Moves password param to user hash param so that it can be set using the User API.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4493 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-12 13:39:55 +00:00
Jean-Philippe Lang
a4d7a99c22 Declare safe attributes for User and Projects models.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4492 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-12 13:19:07 +00:00
Jean-Philippe Lang
3409333522 Makes issue safe_attributes extensible (#6000).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4491 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-12 13:11:53 +00:00
Jean-Philippe Lang
8407db9854 Fixes Changeset#text_tag for numeric scmid (#6681).
Contributed by Toshi MARUYAMA.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4490 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-11 14:20:04 +00:00
Jean-Philippe Lang
00d50157d3 Restores object count and adds offset/limit attributes to API responses for paginated collections (#6140).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4489 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-11 13:13:49 +00:00
Jean-Philippe Lang
67f1131a20 Fixes duplicate custom_values fixture that caused failure with Postgresql.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4488 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-11 10:47:00 +00:00
Jean-Philippe Lang
f2b42237d0 Fixes indentation.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4487 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-11 10:24:58 +00:00
Jean-Philippe Lang
5f57bceabb Makes some attributes optional in API response to get faster/lightweight responses.
These attributes are not required for common uses case (eg. updating an object). They can be requested in the reponse using the 'include' parameter. Example GET /issues/1.xml?include=journals. The list of attributes that can be included in responses will be documented in the wiki.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4486 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-11 10:19:11 +00:00
Jean-Philippe Lang
10ba08ce13 Fixes unsafe assertion that may cause failures.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4485 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-10 17:40:33 +00:00
Jean-Philippe Lang
cd71c1cc0a Do not try to copy relations for issues that could not be copied.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4484 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-10 17:37:24 +00:00
Jean-Philippe Lang
ab6a93b029 Changes the representation of journal details in issue API.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4483 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-10 12:22:37 +00:00
Jean-Philippe Lang
252e4983fb Fixes a fixture with valid attributes (start_date < due_date).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4482 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-10 11:06:21 +00:00
Jean-Philippe Lang
3e3315c103 Support for updating custom fields using the received custom_fields array (#6345, #6403).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4481 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-10 10:48:16 +00:00
Jean-Philippe Lang
0e19aa4362 Fixed: error when serializing back objects with custom fields using ActiveResource (#6403).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4480 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-10 10:12:19 +00:00
Jean-Philippe Lang
8524d505c5 Add tests for wiki edit notifications (#7024).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4479 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-07 22:07:55 +00:00
Jean-Philippe Lang
b898200803 Select projects with issue_tracking module for gantt display and remove the nil start/due dates trick.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4477 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-07 19:42:36 +00:00
Jean-Philippe Lang
4715a37937 Gantt: Avoid unnecessary queries before rendering.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4476 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-07 19:29:47 +00:00
Jean-Philippe Lang
318bd10c7f Gantt: fixes position of line in pdf (#6348).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4475 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-07 18:57:46 +00:00
Jean-Philippe Lang
9c6377964c Fixes gantt PDF pagination problem (#6348).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4474 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-07 18:53:15 +00:00
Jean-Philippe Lang
b17e145d75 Gantt: view cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4473 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-07 18:41:50 +00:00
Jean-Philippe Lang
b0a1a04008 Gantt: iterate over all objects only once for html and pdf rendering (#6348).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4472 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-07 18:40:34 +00:00
Jean-Philippe Lang
bfa61c1ec4 Updated locales.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4471 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-05 11:45:39 +00:00
Jean-Philippe Lang
d7c607fd8b Automatic spent time logging from commit messages (#4155).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4470 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-05 11:45:09 +00:00
Jean-Philippe Lang
80e0e1c544 Changes russian CSV separator to ; (#7035).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4469 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-05 10:15:55 +00:00
Jean-Philippe Lang
8841ba97c6 Fixed: class attribute with spaces on pre tags truncated (#7033).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4468 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-05 10:09:18 +00:00
Jean-Philippe Lang
7fb0fe2e91 Safer code in IssueRelation (#7018).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4467 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-04 18:10:02 +00:00
Jean-Philippe Lang
224921460a Adds a pseudo format to api template names and overrides ActionController#default_template so that api templates are chosen automatically.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4466 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-04 17:43:39 +00:00
Jean-Philippe Lang
9157482049 Adds subtasks to GET /issues/:id API (#5338).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4465 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-04 13:02:14 +00:00
Jean-Philippe Lang
88e593ee02 Fixes unhandled case in json builder.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4464 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-04 11:20:20 +00:00
Jean-Philippe Lang
558a951ed6 Fixes unhandled case in json builder.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4463 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-04 10:41:31 +00:00
Jean-Philippe Lang
37ed02553a Fixes test failure in r4461.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4462 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-04 10:40:44 +00:00
Jean-Philippe Lang
f7cf8aa878 Adds REST API for TimeEntries (#6823).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4461 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-04 10:13:15 +00:00
Jean-Philippe Lang
9284a32c9a Moves project attributes default assignments from ProjectsController#new to the model (#6064).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4460 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-03 16:15:16 +00:00
Jean-Philippe Lang
f6c633212a Prevents random failure in XmlTest.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4459 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-03 14:09:33 +00:00
Jean-Philippe Lang
735a83c596 Converts IssuesController to use the new API template system and makes xml/json responses consistent (#6136).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4458 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-03 13:52:07 +00:00
Jean-Philippe Lang
eaf6bb1e9b Projects API tests rewriting.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4457 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-03 12:49:07 +00:00
Jean-Philippe Lang
a1f12e3ade Converts ProjectsController to use the new API template system.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4456 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-03 12:06:14 +00:00
Jean-Philippe Lang
d0a3aab2e7 Adds a reusable method to render API response on validation failure.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4455 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-03 11:51:06 +00:00
Jean-Philippe Lang
ea59d93dc8 Dry Users API responders.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4454 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-03 11:45:55 +00:00
Jean-Philippe Lang
e9775097ec Adds xml/json REST API for Users (#6260).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4453 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-03 11:28:44 +00:00
Jean-Philippe Lang
96ce0f017c Adds a builder-like template system for rendering xml and json API responses.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4452 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-03 11:25:21 +00:00
Jean-Philippe Lang
483133285e Add responders to UsersController.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4451 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-03 09:39:56 +00:00
Jean-Philippe Lang
a52417eca9 Escapes attachment ids in TracMigrate (#6996).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4449 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-29 19:34:19 +00:00
Jean-Philippe Lang
c78f442095 Updates for 1.0.4 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4446 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-28 12:46:54 +00:00
Jean-Philippe Lang
ae3fd4cb59 Accept key auth for ProjectsController#show (#6841).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4444 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-27 16:41:52 +00:00
Jean-Philippe Lang
3d6cb1435c Accept key auth for ProjectsController#destroy (#6841).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4443 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-27 16:37:15 +00:00
Jean-Philippe Lang
054b7d28f1 Accept key auth for ProjectsController#update (#6841).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4442 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-27 16:32:48 +00:00
Jean-Philippe Lang
8458faed11 Accept key auth for ProjectsController#create (#6841).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4441 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-27 16:26:13 +00:00
Jean-Philippe Lang
d24e66370b Prevents validation error when adding a user to a group (#6457).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4437 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-27 14:46:51 +00:00
Jean-Philippe Lang
620c48fbbb Reload themes so that new themes can be used without restarting Redmine (#5712).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4432 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-27 14:06:11 +00:00
Jean-Philippe Lang
4faca3cd4a Fixed: messages attachments/watchers are not deleted when deleting a project or forum (#6966).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4431 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-27 12:42:11 +00:00
Jean-Philippe Lang
97140f6a78 Fixed: Wiki#find_page should not be case sensitive because page title uniqueness is not (#6987).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4430 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-27 11:14:28 +00:00
Jean-Philippe Lang
4a6a551d07 Fixed: Missing template wiki/update.erb error introduced in r4272 (#6987).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4429 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-27 10:34:44 +00:00
Jean-Philippe Lang
d180c833b9 Fixed: Layout problem in workflow overview (#6990).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4428 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-27 09:54:16 +00:00
Azamat Hackimov
6608cda084 Translation update
* es (#6973)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4427 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-25 20:43:22 +00:00
Azamat Hackimov
ec3975ef5b Replaced hardcoded strings (KB) with i18n equivalent (number.human.storage_units.units.kb)
Fixes #6740 finally


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4426 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-25 20:27:44 +00:00
Jean-Philippe Lang
dfc76ce642 Fixed: new gantt chart discloses all private projects names (#6276).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4425 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-21 14:57:53 +00:00
Azamat Hackimov
7087a53f27 Translation update
* bg (#6948)
* da (#6804)
* ja (#6814)
* ru
* sv (#6783)
* zh-TW (#6782)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4424 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-21 14:55:11 +00:00
Jean-Philippe Lang
cfc3ee4f5a Fixed: r4417 breaks MercurialAdapter with ruby 1.8.6 (#5117).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4422 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-21 14:25:26 +00:00
Jean-Philippe Lang
77c6188ec2 Fixed: gantt displays issues by date of creation.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4421 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-21 13:54:26 +00:00
Jean-Philippe Lang
1158716f46 Fixed: Broken pipe error when browsing subversion repository (#6860).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4419 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-20 14:11:56 +00:00
Jean-Philippe Lang
6b72c66893 Fixed: MercurialAdapter.client_version depends on LANG environment variable (#5117).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4417 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-20 14:04:22 +00:00
Jean-Philippe Lang
1f237388bd Add assertions for #6929 in MailHandler tests.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4416 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-20 10:35:05 +00:00
Jean-Philippe Lang
06b0176a3e Fixed: submitting a non numerical parent task input creates a 500 error (#6932).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4414 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-20 10:20:53 +00:00
Jean-Philippe Lang
427ec05c8b Fixed: Migration from boolean to varchar fails on PostgreSQL 8.1 (#6943).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4413 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-20 09:55:06 +00:00
Jean-Philippe Lang
0f55adf31a Fixed: links to edit/delete a news broken by r4214 (#6944).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4412 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-20 09:38:54 +00:00
Jean-Philippe Lang
8bb75da1ac Initialize TimeEntry attributes with params when editing an issue (#5441).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4411 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-17 18:27:38 +00:00
Jean-Philippe Lang
3ba3c540fb Prevents NoMethodError when requesting /time_entries/edit without an id (#6904).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4410 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-16 20:27:45 +00:00
Jean-Philippe Lang
3e95d12b75 Fixed: Log Time link broken in issue context menu (#6904).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4409 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-16 19:49:08 +00:00
Jean-Philippe Lang
ee91e34096 Fixes a NoMethodError in tests with ruby 1.8.6.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4408 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-14 16:51:10 +00:00
Jean-Philippe Lang
2fab7bd9b1 Adds leading slash to all assert_redirected_to arguments (#6887).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4407 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-14 16:45:32 +00:00
Jean-Philippe Lang
2ee45e8cac Use Object#tap instead of #returning (#6887).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4406 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-14 16:24:21 +00:00
Jean-Philippe Lang
c4a218358f Replaced SessionStore :session_key with :key (#6887).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4405 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-14 15:14:19 +00:00
Jean-Philippe Lang
d7cdd58db6 MailHandler: ignore assignee if invalid.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4404 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-14 13:48:01 +00:00
Jean-Philippe Lang
97a9210483 Updates mantis importer for new project name/identifier max lengths (#6446).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4403 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-14 13:16:39 +00:00
Jean-Philippe Lang
8ef06826c3 Raised maximum length of project names and identifiers to 255 and 100 respectively (#6446).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4402 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-14 12:33:14 +00:00
Jean-Philippe Lang
eaab2ede76 Makes projects API return XML description when creating a project (#6874).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4397 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-13 10:05:43 +00:00
Jean-Philippe Lang
abf988ad69 Makes MailHandler ignore invalid keyword values to avoid validation failures.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4396 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-12 13:08:32 +00:00
Jean-Philippe Lang
d4ab2ab4b9 Makes MailHandler accept localized keywords for default or user language (#6112).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4395 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-12 12:29:35 +00:00
Jean-Philippe Lang
e0e8c14c2a Makes MailHandler accept all issue attributes and custom fields that can be set/updated (#4071, #4807, #5622, #6110).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4394 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-12 11:34:53 +00:00
Jean-Philippe Lang
0eb7d8f614 Moved some permission checks for issue update from controller to model.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4393 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-11 16:37:16 +00:00
Jean-Philippe Lang
aa84e6c179 Removes duplicate issue subject on gantt (#6763).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4392 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-11 13:44:23 +00:00
Jean-Philippe Lang
fa3d71bed9 Allow non-unique names for projects (#630).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4391 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-11 13:39:14 +00:00
Jean-Philippe Lang
7482d2f5f4 Fixed: Trunk not working with i18n gem 0.4.2 (#6784).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4389 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-09 19:44:37 +00:00
Jean-Philippe Lang
44c9ad687f Reverted r4381.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4388 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-07 22:38:10 +00:00
Jean-Philippe Lang
63866407f1 Fixed: unchecking status filter on the issue list has no effect (#6844).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4387 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-07 15:38:51 +00:00
Jean-Baptiste Barth
2ca9bb3cca Added missing --no-color option in some git shell-outs. #5324
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4386 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-07 15:19:05 +00:00
Jean-Philippe Lang
635c177eea Fixed: User list not sorted on category form (#6760).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4385 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-07 14:56:12 +00:00
Jean-Philippe Lang
d9e960873c Updated locales.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4384 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-07 14:18:13 +00:00
Jean-Philippe Lang
f8ca6e58aa Make sure existing custom fields visibility is set to true.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4383 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-07 14:17:35 +00:00
Jean-Philippe Lang
475530e59f Adds a "visible" option on User and Project custom fields (#1738).
If set to false, the custom field won't be display on user profile/project overview.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4382 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-07 14:15:01 +00:00
Jean-Philippe Lang
3b01ea9fa4 Removed gantt and calendar menu items that do not fit into the core project menu (#6271).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4381 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-07 12:53:45 +00:00
Jean-Philippe Lang
ae498a06e2 Removed hardcoded links in my/page_layout (#6839).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4380 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-07 12:36:19 +00:00
Jean-Philippe Lang
dca6fb92f9 Render level 4 headings in TOC (#5494).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4379 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-07 12:22:22 +00:00
Jean-Philippe Lang
1c047dfeb8 Fixed: start date being filled with current date even when blank value is submitted (#6575).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4378 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-06 23:23:02 +00:00
Jean-Philippe Lang
7f9d2b0804 Render TOC as nested lists (#1857).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4377 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-06 18:52:07 +00:00
Jean-Philippe Lang
024ff96ee2 Extract headings and TOC parsing from the textile formatter.
Fixes #2038 and #3707 and will allow to support TOC with other text formatters.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4376 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-06 17:47:27 +00:00
Jean-Philippe Lang
666c54e86c Adds a combo to select parent on wiki page rename (#5136).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4375 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-06 14:30:32 +00:00
Jean-Philippe Lang
f79a6f701a Makes textile formatter accept 2 letters acronym (#6591).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4374 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-06 13:29:23 +00:00
Jean-Philippe Lang
338e407a91 Fixed: URLs broken in wiki notifications (#6838).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4373 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-06 13:23:23 +00:00
Jean-Philippe Lang
6f841b7f43 Fixed: 'View difference' broken on wiki page history (#6747).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4372 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-06 13:22:23 +00:00
Jean-Philippe Lang
8a2aa5d217 Disable button to prevent accidental double click on project members (#6826).
Contributed by Andrew Vit.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4371 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-06 12:04:24 +00:00
Jean-Philippe Lang
b67e4a87e4 Clean up member_roles if needed.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4370 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-06 11:49:45 +00:00
Jean-Philippe Lang
c50b611f85 Adds a unique index on members (#6826).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4369 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-06 11:44:38 +00:00
Jean-Philippe Lang
73167fb4f2 Rewrites UpdateMailNotificationValues migration to avoid model validations and failures.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4368 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-06 10:57:56 +00:00
Eric Davis
c55e060bab Allow key authentication when deleting issues (with tests) #6447
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4367 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-05 17:49:25 +00:00
Eric Davis
7d934c984a Allow key authentication when updating issues (with tests) #6447
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4366 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-05 17:49:20 +00:00
Eric Davis
4b1dd334a5 Allow key authentication when creating issues (with tests) #6447
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4365 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-05 16:29:56 +00:00
Eric Davis
c967899b14 Refactor: Convert the tests for Issues#index and #show APIs to shoulda. #6447
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4364 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-04 16:22:47 +00:00
Eric Davis
30dc4fec99 Refactor: convert API key tests using HTTP Basic to a shoulda macro
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4363 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-03 16:48:23 +00:00
Eric Davis
7e359d3d7e Add a Email Header setting. Useful for adding delimiters to every email.
#2852 #6628

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4362 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-02 19:00:36 +00:00
Eric Davis
0395eb99de Remove email quotes (>) when searching for incoming email delimiters. #2852 #6628
Changes the email truncation so a delimiter can be prefixed by email
quotes or spaces ("> Eric said...").  This will let it be combined with
a "--- Reply above ---" delimiter to match "> --- Reply above ---"

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4361 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-02 19:00:31 +00:00
Eric Davis
a04d64881c Refactor: convert username/password http basic auth api tests to shoulda macros #6447
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4360 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-02 15:52:06 +00:00
Jean-Baptiste Barth
27049b848d Use File#expand_path for require's in script/* for Ruby 1.9.2 compatibility. #4050
Since Ruby 1.9.2, LOAD_PATH does not include "." directory anymore, so
we should use absolute paths instead to ensure both 1.8.x and 1.9.x
compatibility. It has been included in railties 2.3.x branch since
july 2009, see 7a427a83ca

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4359 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-02 00:20:21 +00:00
Eric Davis
bed79f523b Refactor: convert api key tests to shoulda macros for reuse. #6447
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4358 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-01 15:45:03 +00:00
Eric Davis
d5fde17bf5 Move all API tests into the ApiTest module to make management easier
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4357 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-01 15:26:05 +00:00
Jean-Philippe Lang
db2ecd3010 Fixed: "Template is missing" error when validation fails on version edit (#6766).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4354 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-01 13:13:32 +00:00
Jean-Philippe Lang
ba56b3f763 Provides a default string for untranslated search types.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4353 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-01 12:55:15 +00:00
Eric Davis
b2eeeb6fa0 Bump version to 1.0.3
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4349 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-01 00:37:19 +00:00
Eric Davis
147a16cbf8 Update changelog for v1.0.3
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4348 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-01 00:37:14 +00:00
Jean-Baptiste Barth
7c5497db65 Add a label for user and group search fields. #6521
Contributed by Felix Schäfer

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4312 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-30 16:47:33 +00:00
Jean-Baptiste Barth
abb83f4c4f Added ability to create issue directly as a subtask of another one. #5484
Contributed by Felix Schäfer

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4311 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-30 16:15:31 +00:00
Jean-Baptiste Barth
f8b79c52cb Force color to be disabled on git shell-outs. #5324
Contributed by Felix Schäfer

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4310 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-29 23:21:57 +00:00
Jean-Baptiste Barth
55cf64393d Fixed Redmine.pm not working with LDAPS on a non-standard port. #4065
Contributed by Fabian Schlenz

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4309 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-29 22:55:50 +00:00
Jean-Baptiste Barth
31b9868fc1 Updated wiki-syntax help page to reflect new link options and last coderay upgrade. #5948
Contributed by Holger Just and Mischa The Evil

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4308 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-29 22:48:59 +00:00
Jean-Baptiste Barth
c91ca4fcd9 Fix wikilinks in project > settings > versions and version view. #6435 #4416
Contributed by Mischa The Evil and Felix Schäfer.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4307 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-29 22:40:02 +00:00
Jean-Baptiste Barth
1cf67b3a1d Fixed: disabling autologin not persisted. #6438
Contributed by Felix Schäfer

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4306 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-29 22:28:24 +00:00
Azamat Hackimov
5d6245b059 Translation update (zh-TW, #6765)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4305 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-28 21:58:58 +00:00
Azamat Hackimov
8dc0d4b916 Translation update (he, #6569)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4304 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-28 21:56:52 +00:00
Eric Davis
c514dd6885 Refactor: convert WikiController to a REST resource
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4303 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-28 21:25:38 +00:00
Jean-Baptiste Barth
e7e7a91b85 Fixed potential nil method errors when trying to access /issues/context_menu with GET. #6750
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4302 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-27 21:01:26 +00:00
Azamat Hackimov
6ffcbaa7b3 Translation update (ru)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4301 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-27 18:58:36 +00:00
Azamat Hackimov
1b1a059d90 Fixing "Kilobyte" prefix to kB (#6740)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4300 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-27 18:46:20 +00:00
Azamat Hackimov
d41391e87a Readding string field_start_date (#6629)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4299 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-27 18:40:45 +00:00
Azamat Hackimov
8ebe03ef86 Translation update (de, #6607)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4298 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-27 18:29:30 +00:00
Azamat Hackimov
cc9d356a55 Translation updates
* bg (#6669)
* ja (#6732)
* mk (#6731)
* sv (#6739)
* zh-TW (#6736)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4297 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-27 18:20:55 +00:00
Eric Davis
e9efa5b981 Refactor: use :id instead of :page when linking to Wiki Pages
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4296 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-27 16:27:06 +00:00
Eric Davis
70bf0706b2 Refactor: convert WikiController#destroy to use HTTP DELETE
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4295 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-26 15:59:20 +00:00
Eric Davis
17eab0f5f9 Fix wiki links on the activity page. #6746
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4294 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-26 15:44:30 +00:00
Eric Davis
c399e76324 Add Redmine::Plugin.installed?(:name) method to check if a plugin is installed
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4293 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-25 23:32:01 +00:00
Eric Davis
397222f198 Allow adding notes when moving issues
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4292 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-25 18:44:46 +00:00
Eric Davis
22e3cba0b7 Allow changing the Priority when moving issues.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4291 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-25 18:44:41 +00:00
Eric Davis
4b045badcf Refactor: Rename WikiController#page_index to #index
index is the action that should list a collection of records, which is
what #page_index does.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4290 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-25 16:16:46 +00:00
Eric Davis
0e951c0716 Fix the tests that were broken by r4286:
"Refactor: merged error rendering methods."

http://ci.finn.de/builds/1-8-7_redmine-trunk_mysql/4286

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4289 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-25 15:42:41 +00:00
Jean-Philippe Lang
d9f2bccf70 Adds a user-agent to reposman (#6735).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4288 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-24 21:00:05 +00:00
Jean-Baptiste Barth
d642964035 Fixed: auto links ending with right angle bracket shouldn't include the bracket in the URL. #5652
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4287 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-23 22:35:02 +00:00
Jean-Philippe Lang
7824eca775 Refactor: merged error rendering methods.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4286 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-23 11:07:04 +00:00
Jean-Philippe Lang
eea456ed84 Improved error message when trying to access an archived project (#2995).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4285 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-23 09:48:01 +00:00
Jean-Philippe Lang
1e4776fa64 Updated french translation.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4284 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-23 09:22:28 +00:00
Jean-Philippe Lang
a29c35e08f Makes zoom buttons on gantt looks like the others.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4283 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-23 09:08:55 +00:00
Eric Davis
0ca74df604 Refactor: move method to model with compatibility wrapper
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4282 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-22 22:38:45 +00:00
Jean-Philippe Lang
a6f891d1b1 Moves a method to the appropriate helper.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4281 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-22 22:29:32 +00:00
Jean-Philippe Lang
ed608cb7d3 Fixed: error on gantt when no issue have a due date (#6350).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4280 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-22 22:13:39 +00:00
Eric Davis
b7e3f80e14 Translation update for en: field_member_of_group and field_assigned_to_role
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4279 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-22 22:10:41 +00:00
Eric Davis
b1921830de Mark the failed test in r4276 as pending. Tests should never fail when committed.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4278 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-22 21:29:02 +00:00
Jean-Philippe Lang
d8ef4b6f4d Fixed: timelog link in the activity broken by recent refactoring (#6702).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4277 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-22 20:40:11 +00:00
Jean-Philippe Lang
d0bbe830ad Adds a failing test for #6350.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4276 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-22 20:32:05 +00:00
Eric Davis
dc26fd215b Add ids to some sections of the issue form
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4275 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-22 18:20:00 +00:00
Jean-Philippe Lang
d9652183f6 Fixed: file upload on wiki page broken by recent refactoring (#6724).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4274 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-22 17:38:05 +00:00
Eric Davis
432b00df40 Add documentation about contributing to Redmine with git
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4273 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-22 17:18:11 +00:00
Eric Davis
cac3b1e538 Refactor: split WikiController#edit into #update
update will handle the saving and should be accessed via POST only.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4272 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-22 16:20:20 +00:00
Jean-Philippe Lang
2fbf7bbcf1 Fixes display of archived child projects on the admin project list.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4271 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-22 15:11:04 +00:00
Jean-Philippe Lang
128c92779c Removes description column from the admin project list to get a better looking project tree.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4270 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-22 14:57:20 +00:00
Jean-Philippe Lang
2589bec48b Removes duplicate helper introduced in r4072.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4269 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-22 14:49:46 +00:00
Jean-Philippe Lang
f17c990a77 Fixed: "Add file" link broken by recent refactoring (#6627).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4268 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-22 14:45:02 +00:00
Jean-Philippe Lang
b26ba17184 Fixed: "Add news" link broken by recent refactoring (#6650).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4267 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-22 14:42:38 +00:00
Eric Davis
c259ada6e1 Refactor: rename WikiController#index to #show, it's a single resource action
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4266 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-21 16:07:28 +00:00
Eric Davis
d3381fb518 Refactor: change :id on WikiController to use :project_id
Using :id to track projects on non-project controllers is confusing and
makes routing with resources difficult.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4265 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-20 21:26:30 +00:00
Jean-Philippe Lang
c058bc2245 Remove the Description label from the issue view and omit the block if empty (#6677).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4264 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-19 19:26:07 +00:00
Jean-Philippe Lang
f3baa5bfee Fixed: precedes/follows relations no longer update start/due dates (#5803).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4263 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-19 19:16:50 +00:00
Eric Davis
297eb6f81b Refactor: remove WikiController#special
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4262 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-19 15:24:16 +00:00
Eric Davis
c06c22cf2e Refactor: extract method from WikiController#special
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4261 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-18 15:27:49 +00:00
Eric Davis
fef21d5aa2 Remember the selected "Member of Role" and "Member of Group" options. #6467
Contributed by Yuki Kita

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4260 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-16 00:00:23 +00:00
Eric Davis
ce99f3247f Use HTTP DELETE when deleting a time entry. #6674
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4259 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-15 23:37:01 +00:00
Eric Davis
c8177b99ed Revert part of r3936.
link_to_if_authorized no longer supports paths for the link options.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4258 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-15 23:11:05 +00:00
Eric Davis
5823a71c18 Revert part of r4064.
Having link_to_if_authorized support urls has too many edge cases and isn't
working with sub-uris.  #6195 #6513

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4257 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-15 23:11:00 +00:00
Eric Davis
06c0e5ad1c Add rake task to send test email. (#6511)
Contributed by Holger Just

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4256 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-15 22:50:33 +00:00
Eric Davis
c20308c7ba Remove the translated string. It's definition changed in en
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4255 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-15 22:41:52 +00:00
Eric Davis
1bfbc012f5 Add rake tasks to add and remove keys in the locales. #6548
Contributed by Felix Schäfer

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4254 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-15 22:41:47 +00:00
Eric Davis
0271105794 Refactor: extract #page_index from WikiController#special
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4253 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-15 15:36:10 +00:00
Eric Davis
bbbfd4ee4c Refactor: extract finder to a utility method
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4252 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-14 18:01:46 +00:00
Eric Davis
e8468b51cc Refactor: extract method in WikiController#special to create a new #export method
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4251 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-13 17:13:50 +00:00
Eric Davis
718816c5d4 Refactor: convert timelogs to a REST resource (:time_entries)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4250 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-12 15:55:21 +00:00
Jean-Baptiste Barth
71dd4b8a7d Fixed bad markup in users/general partial
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4249 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-11 20:34:33 +00:00
Eric Davis
435c90eb47 Refactor: extract TimelogController#edit to #update
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4248 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-11 15:31:42 +00:00
Eric Davis
700c302fca Change Project#notified_users to check for the 'all' notification option. #6541
The previous mail_notification? check would always pass since the
notifications where converted to strings and strings are always true.

Also changed Project#recipients to use #notified_users instead of duplicated
code.

Based on contribution by Felix Schäfer.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4247 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-10 21:42:24 +00:00
Eric Davis
d2986eb98f Correctly update all mail_notification options. #6549
* Need to check for 't' values to support sqlite
* Need to check the membership count for the 'selected' option

Based on patch contributed by Felix Schäfer

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4246 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-10 21:17:15 +00:00
Eric Davis
eabf1ff3e7 Add SortHelper so custom queries will run on the Calendar. #6612
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4245 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-10 21:17:10 +00:00
Eric Davis
4acd990ee2 Refactor: extract TimelogController#create from TimelogController#edit
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4244 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-08 15:39:39 +00:00
Eric Davis
84ebd786d6 Fix the Log Time link on an issue page
Contributed by Philippe Lafoucrière (gravis)

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4243 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-08 15:01:20 +00:00
Jean-Baptiste Barth
156eca4d22 Added ability to edit issues from different project through contextual menu (#5332)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4242 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-08 03:09:51 +00:00
Eric Davis
73f12765a9 Don't duplicate users in Issue#assignable_users. From r4240
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4241 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-07 17:46:37 +00:00
Eric Davis
7798e1b1f7 Allow assigning issues back to the author. #4199
This allows an issue to be reassigned to the author even if they are not
a project member.  Useful when passing back an issue to get more
information from the author.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4240 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-07 17:28:29 +00:00
Eric Davis
068771ea07 Refactor: extract TimelogController#new from #edit
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4239 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-07 15:51:09 +00:00
Azamat Hackimov
e59156b446 Rename task to issue in calendar (#6563)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4238 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-07 15:26:53 +00:00
Azamat Hackimov
5592e77fc6 Translation updates
* de (#6598)
* ja (#6508, #6570)
* he (#6569)
* ru
* pt (#6509)
* pt-BR (#6494, #6596)
* sv (#6536)
* zh-TW (#6525)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4237 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-07 15:19:28 +00:00
Jean-Baptiste Barth
b255b7760a Added ability to delete issues from different projects through contextual menu (#5332)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4236 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-07 05:11:28 +00:00
Eric Davis
2ecca7e4df Refactor: rename TimelogController#details to #index
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4235 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-06 18:23:45 +00:00
Jean-Baptiste Barth
c43ef6e769 Code cleanup: renamed variables in User#allowed_to? with explicit names
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4234 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-06 05:08:38 +00:00
Jean-Baptiste Barth
e59c927ee5 Fixed: User#allowed_to? returning true in any case if array of projects had only one item (#5332)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4233 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-06 05:08:31 +00:00
Eric Davis
e13790c62c Refactor: extract TimelogController#report to a new controller class
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4232 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-05 16:07:17 +00:00
Eric Davis
a034172b24 Refactor: convert UsersController to resource
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4231 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-10-04 15:36:16 +00:00
Eric Davis
86ba692bf5 Refactor: split UsersController#edit into #edit and #update
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4230 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-30 18:22:46 +00:00
Eric Davis
d06a1a7fa4 Refactor: rename UsersController#add to #new
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4229 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-29 16:00:45 +00:00
Jean-Baptiste Barth
4853dd97fd Splitted #find_issues filter in ApplicationController to #find_issues and #check_project_uniqueness (#5332)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4228 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-29 05:22:53 +00:00
Jean-Baptiste Barth
e8f3dd07dd Added ability to specify multiple projects in User#allowed_to? (#5332)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4227 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-29 05:22:45 +00:00
Eric Davis
fda1a0cb3b Fix test failure from Setting changes in other tests
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4226 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-29 03:02:23 +00:00
Eric Davis
a04e1bd582 Locale update
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4225 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-28 22:15:56 +00:00
Eric Davis
747b9ec568 Refactor: move method to model
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4224 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-28 22:13:11 +00:00
Eric Davis
437690119b Allow admins to edit user's email notifications and preferences. #3503
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4223 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-28 22:13:06 +00:00
Eric Davis
26ef9da02b Changed the notifications to use a hierarchy UI
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4222 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-28 21:09:06 +00:00
Eric Davis
4c9f261a24 Added three new notifiable events based on issue attributes
* issue_note_added
* issue_status_updated
* issue_priority_updated

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4221 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-28 20:20:00 +00:00
Eric Davis
244e94de80 Refactor the hardcoded event actions (notifiables) to use a class
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4220 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-28 20:19:55 +00:00
Eric Davis
582ed86d82 Add a default mail notification setting for new users
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4219 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-28 18:36:50 +00:00
Eric Davis
c059300d99 Added User#notify_about? to check when a user should be notified about an event
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4218 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-28 18:22:10 +00:00
Eric Davis
a61ee73e69 Added tests for Issue#recipients
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4217 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-28 18:22:05 +00:00
Eric Davis
0316af7f6b Converted User#mail_notification from a boolean to a string.
The string will now store which type of notification option to use.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4216 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-28 18:22:00 +00:00
Eric Davis
3a3263102a Refactor: split UsersController#add into #add and #create
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4215 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-28 15:28:50 +00:00
Eric Davis
79e30e7087 Refactor: convert News to a REST resource
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4214 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-27 16:51:12 +00:00
Eric Davis
0a224e8bb1 Bump version to 1.0.2
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4210 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-26 22:39:50 +00:00
Eric Davis
d3517e87e5 Update changelog for the 1.0.2 release
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4209 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-26 22:39:44 +00:00
Eric Davis
ffb3d9260a Support leading and trailing spaces in filenames or directories in the git adapter. #6499
Contributed by Felix Schäfer

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4189 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-26 21:54:52 +00:00
Eric Davis
bd51119460 Support spaces in the files or directories in the git adapter. #6054
Contributed by Felix Schäfer

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4188 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-26 19:07:46 +00:00
Eric Davis
319849caf8 Parse the timezone in #last_rev for git to correct display the Age diplay. #6346
Contributed by Felix Schäfer

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4187 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-26 18:41:06 +00:00
Eric Davis
83e0be5d07 Fixes reverting an issue to a status with a done_ratio of 0%. #5170
Contributed by Felix Schäfer

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4186 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-26 18:13:31 +00:00
Eric Davis
76fcf136d0 Redirect to project settings after copying a project. #6443
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4185 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-26 18:04:54 +00:00
Eric Davis
83bfb8cd2b Revert "Fixed: gantt broken when no due date on project issues and versions."
It broke the tests for overdue projects and isn't a good way to fix #6350
because it makes an assumption that a project is due Today and will still
error if issue tracking is disabled.

This reverts commit a7fd592db4. (r4178)

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4184 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-26 17:35:18 +00:00
Eric Davis
768d67f27b Workaround for i18n 0.4.x with the old style syntax. #6428 #5608
This will also silance the whole trace with the deprecation warning.

Contributed by Felix Schäfer

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4183 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-26 17:13:52 +00:00
Eric Davis
1781d87477 Add documentation about setting the log level. #6135
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4182 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-26 16:54:38 +00:00
Jean-Baptiste Barth
2f380a7985 Fixed: don't reset watchers when changing tracker. #6479
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4181 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-26 13:52:25 +00:00
Azamat Hackimov
b3c5bf3f2e Translation updates
* pt-BR (#6490)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4180 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-26 13:47:56 +00:00
Jean-Philippe Lang
7a952136e0 Project tests updated.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4179 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-26 08:24:51 +00:00
Jean-Philippe Lang
a7fd592db4 Fixed: gantt broken when no due date on project issues and versions.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4178 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-26 08:16:05 +00:00
Jean-Philippe Lang
e9686cbbe5 Fixed: error on mail notification when adding an invalid file (#6452).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4177 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-26 08:07:41 +00:00
Jean-Philippe Lang
53b131ba2a Fixes broken filter clearing links on gantt and calendar (#6473).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4176 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-26 07:51:10 +00:00
Azamat Hackimov
02b7423b73 Translation updates
* cs (#6471)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4175 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-24 18:04:32 +00:00
Eric Davis
deed1b949d Refactor: move NewsController#preview to PreviewsController#news
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4174 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-24 16:26:46 +00:00
Azamat Hackimov
20305418f4 Translation updates
* cs (#6471)
* ja (#6465)
* zh-TW (#6466)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4173 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-23 16:23:39 +00:00
Eric Davis
401197a895 Refactor: move #destroy_comment method to CommentsController#destroy
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4172 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-23 15:20:19 +00:00
Azamat Hackimov
4948121af0 Translation update
* ru


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4171 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-22 19:45:30 +00:00
Eric Davis
1f2f953687 Refactor: move NewsController#add_comment to CommentsController#create
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4170 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-22 16:25:09 +00:00
Azamat Hackimov
3b20774c54 New string to translate - field_text. It appear when you trying save empty wiki-page. See #6448 for reference.
Translation updates
* ja (#6441)
* ru
* zh-TW (#6451)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4169 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-22 16:06:29 +00:00
Eric Davis
01d1a02df4 Refactor: split NewsController#edit into #edit and #update
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4168 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-21 15:20:37 +00:00
Eric Davis
51359704a0 Added a users options to the reminders email
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4167 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-20 23:17:51 +00:00
Eric Davis
55fbf6836b Document render_issue_tooltip
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4166 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-20 18:50:14 +00:00
Eric Davis
a09a39402e Add view_issues_form_details_top hook
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4165 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-20 18:50:09 +00:00
Eric Davis
6a76aef375 Add User#allowed_to_globally? which wraps User#allowed_to?
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4164 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-20 16:38:00 +00:00
Eric Davis
3bc29e29e0 Refactor: split NewsController#new into #new and #create methods.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4163 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-20 15:13:48 +00:00
Eric Davis
1a4f5e87f2 Add scm.css to news pages to allow proper syntax highlighting. #6270
Contributed by Holger Just.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4107 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-20 02:55:37 +00:00
Eric Davis
e58513c976 Fix failing test.
See http://ci.finn.de/builds/1-8-7_redmine-trunk_postgres/4100

Contributed by Holger Just

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4106 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-20 02:55:32 +00:00
Eric Davis
f62a1827f3 Updated issue delete confirmation when it has child issues. #6191
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4105 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-20 02:55:26 +00:00
Eric Davis
1809eefe4c Use the correct local variables in the PDF export. #6382
Contributed by Felix Schäfer

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4104 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-20 02:55:20 +00:00
Eric Davis
09331ec432 Turn on ssl Gravatars for all SSL requests. #5929
Contributed by Felix Schäfer

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4103 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-20 02:55:14 +00:00
Azamat Hackimov
944d1a68bd Translations update
* ru
* zh-TW (#6365)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4102 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-19 12:00:23 +00:00
Jean-Baptiste Barth
81a28e9ffa Adds a link to edit the associated wiki page on the version view. #4416
Contributed by Felix Schäfer

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4101 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-18 17:30:22 +00:00
Jean-Baptiste Barth
099761d8fb Fixes switching between inline and side-by-side in diff view with path. #6242
Contributed by Felix Schäfer

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4100 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-18 16:50:08 +00:00
Jean-Baptiste Barth
ffdaead5b7 Removed file mistakenly added in r4092. #6392
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4099 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-17 23:13:26 +00:00
Eric Davis
5fdfe02b3a Refactor: add VersionsController#status_by to the resource.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4098 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-17 16:11:43 +00:00
Eric Davis
bd193a026d Refactor: convert VersionsController to a REST resource.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4097 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-17 15:55:08 +00:00
Eric Davis
eb1f58f962 Added some documentation about the Rails Logger. #6135
Contributed by Felix Schäfer

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4096 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-17 04:31:17 +00:00
Eric Davis
b8ea556616 Fix the new subproject link on project overview. #6388
Contributed by Felix Schäfer.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4095 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-17 04:22:46 +00:00
Eric Davis
ebb445c364 Use the relative_url_root when generating asset_paths. #3935
Contributed by Daniel S from Redmine.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4094 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-17 04:06:40 +00:00
Jean-Baptiste Barth
35dba0f412 Fixed: pressing enter in filters should result to an 'apply', not a 'save'. #2285
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4093 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-16 21:33:49 +00:00
Jean-Baptiste Barth
0d967c0572 Fix links to activity pages broken with r4047. #6392
Contributed by Felix Schafer

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4092 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-16 21:26:30 +00:00
Jean-Baptiste Barth
c4d44af54c Fixed non standard SQL syntax. #6413
Contributed by Juan G

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4091 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-16 19:28:04 +00:00
Eric Davis
969bf2107b Refactor: split VersionsController#new into #new and #create
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4090 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-16 18:27:33 +00:00
Eric Davis
bde8ab84f2 Use the full path to the partials
When trying to use the issue form in a plugin, it would try to use the
relative path to the partials which were incorrect.

  Example: would render 'my_plugin_views/attributes'

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4089 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-15 22:43:30 +00:00
Eric Davis
be6e0927f3 Refactor: Split VersionsController#edit into #edit and #update
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4088 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-15 16:50:25 +00:00
Eric Davis
cdfc57d544 Change the TimelogController's to/from dates based on the project time entries
Instead of looking for the earliest and latest time entry system wide for the
dates in the form, now TimelogController will only look at the time entries
for the current project (and parent/sub projects).

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4087 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-14 19:02:25 +00:00
Eric Davis
8900797ada Refactor: move method to Model.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4086 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-14 19:02:20 +00:00
Eric Davis
1b90703157 Refactor: convert FilesController to a restful resource.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4085 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-14 16:24:07 +00:00
Eric Davis
41c055363e Refactor: split FilesController#new into #new and #create.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4084 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-13 20:35:03 +00:00
Jean-Baptiste Barth
12e10f6956 Fixed "Create and continue" redirection broken by recent changes. #6333
IssuesController#create is no more scoped under project, but
IssuesController#new is, so we need to precise project_id
when redirecting to "New issue" form.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4083 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-11 20:21:27 +00:00
Jean-Baptiste Barth
4b69a895df Fixed broken context_menu on roadmap. #6351
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4082 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-11 14:00:23 +00:00
Azamat Hackimov
f2a8057d63 Translations update
* ja (#6363)
 * ko (#6307)
 * po (#6458)
 * pt-BR (#6316)
 * ru
 * sr and sr-YU (#6339)
 * zh-TW (#6306)
New strings to translate
 * field_member_of_group (r4077)
 * field_assigned_to_role (r4078)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4081 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-11 11:16:56 +00:00
Eric Davis
878bb55522 Refactor: move method to Project#css_classes
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4080 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-10 23:07:10 +00:00
Eric Davis
d36700eeee Refactor: replace chained finders with an inject. Should handle edge cases better.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4079 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-10 19:53:57 +00:00
Eric Davis
41f8d043eb Added a "Member of Role" to the issues filters. #5869
This filter will check an issue's assigned to field for users who have
(or don't have) a specific Role(s).

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4078 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-10 19:44:45 +00:00
Eric Davis
109b42f482 Added a "Member of Group" to the issues filter. #5869
This filter will check an issue's assigned to field for users in (or not in)
specific groups.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4077 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-10 18:46:29 +00:00
Eric Davis
4c656fcffc Define Principal#name so all subclasses will have some sort of name when printed.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4076 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-10 18:46:23 +00:00
Eric Davis
3a2efb4757 Refactor: convert ProjectEnumerations to a resource on a project.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4075 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-10 16:00:49 +00:00
Eric Davis
5e1c295230 Fixed the zoom, previous, and next links on the Gantt chart.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4074 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-10 03:09:18 +00:00
Eric Davis
abf3ee4999 Add project names to the Versions in the Gantt export. #5904
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4073 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-10 03:09:11 +00:00
Eric Davis
bdb3937e0f Rewrite the Gantt chart. #6276
This version of the Gantt chart supports nested charts. So Projects,
Versions, and Issues will be nested underneath their parents correctly.

Additional features:

* Move all Gantt code to Redmine::Helpers::Gantt class instead of having it in
  the Gantt class, controller, and view
* Recursive and nest sub-projects
* Recursive and nest versions
* Recursive and nest issues
* Draw a line showing when a Project is active and it's progress
* Draw a line showing when a Version is active and it's progress
* Show a version's % complete
* Change the color of Projects, Versions, and Issues if they are late or
  behind schedule
* Added Project#start_date and #due_date
* Added Project#completed_percent
* Use a mini-gravatar on the Gantt chart
* Added tests for the Gantt rendering

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4072 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-10 03:09:02 +00:00
Eric Davis
8d52608dba Refactor: convert the Projects routes to resources.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4071 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-09 18:57:21 +00:00
Eric Davis
bf7476af5b Refactor: split method ProjectsController#edit to ProjectsController#update.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4070 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-08 16:01:51 +00:00
Eric Davis
2295b61cb6 Refactor: rename method ProjectsController#add to ProjectsController#new
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4069 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-07 15:00:27 +00:00
Eric Davis
06878e5004 Change project add form to use #create. (From r4067)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4068 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-06 15:09:52 +00:00
Eric Davis
763ab07942 Refactor: split ProjectsController#add into #add (GET) and #create (POST).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4067 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-06 14:53:08 +00:00
Eric Davis
9da4ee5fcc Allow user password changes when changing to Internal authentication. #6267
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4066 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-06 01:02:52 +00:00
Eric Davis
270b559d36 Refocus the related issue field after submitting an issue. #6275
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4065 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-06 00:48:44 +00:00
Eric Davis
d771fa9289 Change link_to_if_authorized to allow url paths. (Fixes #6195)
Both url paths (/issues/1234) and params hashes (:controller => 'issues')
are now supported by link_to_if_authorized.  The authorize_for method
requires a controller/action pair so urls need to be parsed against the
routes to find their controller/action.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4064 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-06 00:26:08 +00:00
Eric Davis
563c879e44 Use the built in Rails ActionView::TestCase for testing helpers.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4063 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-06 00:26:02 +00:00
Eric Davis
db11030410 Refactor: move method to model. (references r4057)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4062 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-05 22:57:20 +00:00
Azamat Hackimov
cbe266079e Added string "cant_link_an_issue_with_a_descendant" to translations missed in #443
Solves #6278


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4061 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-05 13:01:38 +00:00
Azamat Hackimov
c5071cd091 Translation updates:
* ca (#6263)
* nl (#6248)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4060 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-05 11:33:08 +00:00
Jean-Baptiste Barth
a9f5a17c67 Do not display items without valid selection in context menu when on different projects. #4998
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4059 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-03 21:43:07 +00:00
Jean-Baptiste Barth
c799d03ece Added missing tests for User#allowed_to? #6291
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4058 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-03 20:16:00 +00:00
Jean-Baptiste Barth
b6d9f2bddf Add css classes to journals display to facilitate theming
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4057 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-03 20:05:51 +00:00
Jean-Baptiste Barth
4776a5a427 Hide checkboxes in issues list when printing
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4056 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-03 19:59:49 +00:00
Jean-Baptiste Barth
a2ce6e236c Allow mass status update through context menu. #3411
NB: it cannot be done with issues from different projects, same as
other fields. This will be addressed separately, see #5332.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4055 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-03 19:54:24 +00:00
Eric Davis
c1068bf0cd Refactor: move method, ProjectsController#reset_activities to ProjectEnumerationsController#destroy.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4054 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-03 15:04:03 +00:00
Eric Davis
83b4343d2d Refactor: move method, ProjectsController#save_activities to ProjectEnumerationsController#save
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4053 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-02 17:39:56 +00:00
Eric Davis
b5e90972d8 Refactor: move method, ProjectsController#add_file to FilesController#new.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4052 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-09-01 15:17:45 +00:00
Eric Davis
daa8eaa9ae Refactor: move method, ProjectsController#list_files to FilesController#index.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4051 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-31 15:12:58 +00:00
Eric Davis
a188abbe28 Refactor: move method, ProjectsController#roadmap to VersionsController#index.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4050 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-30 15:30:28 +00:00
Azamat Hackimov
1ebb0d9cae Translation updates
* mk (#6208)
* sv (#6210)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4049 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-29 12:54:04 +00:00
Eric Davis
f92dcdf50a Allow setting an issue's notes via params[:issue][:notes]. (XML API)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4048 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-27 20:08:59 +00:00
Eric Davis
b925325ddb Refactor: extract ProjectsController#activity to a new Activities controller.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4047 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-27 14:05:54 +00:00
Eric Davis
5b08b2f33d Refactor: extract common code to link_to_month.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4046 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-26 16:37:26 +00:00
Eric Davis
30821586cb Add the project_id to the Calendar link to month.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4045 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-26 16:37:21 +00:00
Eric Davis
ab1e74d16c Refactor: extract target link generation.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4044 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-26 16:37:16 +00:00
Eric Davis
91380eeaab Refactor: extract ternary operators to temps.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4043 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-26 16:37:11 +00:00
Eric Davis
3eea03d70e Refactor: extract link to previous and next month into helpers.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4042 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-26 16:37:05 +00:00
Eric Davis
56e984b5e7 Refactor: convert many of the custom Issue routes to REST resources.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4041 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-26 16:36:59 +00:00
Eric Davis
daa4272c0e Refactor: extract method in bulk_update.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4040 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-25 14:59:16 +00:00
Azamat Hackimov
bc951a9d4f remove BOM from zh-TW
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4039 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-24 16:15:14 +00:00
Azamat Hackimov
2e2241cf84 Translation updates
* de (#6198)
* ja (#6189)
* zh-TW (#6197)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4038 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-24 15:32:11 +00:00
Eric Davis
80256cf298 Refactor: extract #bulk_update method from IssuesController#bulk_edit.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4037 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-24 15:27:12 +00:00
Azamat Hackimov
95673a9ee4 Adding missed new strings for #6153:
*  project_module_gantt: Gantt
*  project_module_calendar: Calendar


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4036 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-23 19:39:35 +00:00
Eric Davis
dfcb5a666f Updated permissions for r4034
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4035 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-23 15:32:57 +00:00
Eric Davis
b67b3820a1 Refactor: move IssuesController#changes to JournalsController#index.
Since #changes is only an Atom feed of journals of a query, it makes
more sense to have it on the JournalsController resource.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4034 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-23 15:04:36 +00:00
Eric Davis
5b4bd8a59a Sure hope 1.0.1 wasn't released in 201,008 AD...
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4029 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-22 21:17:44 +00:00
Eric Davis
22fa8d9b31 Bump version to 1.0.1
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4028 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-22 21:15:38 +00:00
Eric Davis
dcd2204786 Updated changelog for 1.0.1
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4027 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-22 21:15:33 +00:00
Eric Davis
05527be943 Add RTL support to the context menu. #6012
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4014 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-22 19:12:21 +00:00
Eric Davis
fc9b984cc1 Treat Calendar and Gantt pages as separate modules. #6153
This will let the Issue Tracking, Gantt, and Calendar modules be enabled
or disabled as needed.  A database migration will enable the Gantt and
Calendar for all existing projects that have Issue Tracking enabled.

Contributed by Adam Soltys

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4013 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-22 18:42:00 +00:00
Eric Davis
7e565eed34 Fix the calendar link in the Issues sidebar. #5591
Contributed by Andrew Rudenko

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4012 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-22 18:30:26 +00:00
Eric Davis
dfd1d0c7fe Refactor: merge IssuesController#update_form into IssuesController#new
The #update_form action was only refreshing the issue attributes form,
so it's just a specialized JavaScript version of #new.  This also removed
old code that was extracted in other places (@issue.new_statuses_allowed_to).

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4011 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-20 15:22:19 +00:00
Eric Davis
9696557897 Fix the mock_file test helper.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4010 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-19 22:24:42 +00:00
Eric Davis
e4c5a91d0e Refactor: extract Members Box to partial.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4009 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-19 22:24:37 +00:00
Azamat Hackimov
1c53668bc9 Remove dublicate string
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4008 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-19 19:47:55 +00:00
Azamat Hackimov
af58531482 Localisation updates: New string to fix #5162
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4007 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-19 18:50:18 +00:00
Eric Davis
1fcc1bdc89 Refactor: move IssuesController#context_menu to a new controller.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4006 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-19 18:16:54 +00:00
Azamat Hackimov
57372d9d8e Real RTL-theme support in locales (#6012)
Now each locale-file have direction string (ltr - left-to-right - by default).


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4005 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-19 16:57:44 +00:00
Azamat Hackimov
eefb36177d Fixing #6009
Added to all locales required strings.


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4004 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-19 16:35:51 +00:00
Azamat Hackimov
01788e83e7 Translation updates for upcoming release
* de (#6079)
* es (#6021)
* it (#6093)
* nl (#6025)
* ru
* sr and sr-YU (sr is now serbian cyrillic, #6078)
* sv (#6142)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4003 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-19 15:41:09 +00:00
Eric Davis
782a5f1218 Add Issue Status to the tooltip. #6169
Contributed by Nick Peelman

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3952 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-19 03:43:33 +00:00
Eric Davis
fc6e7f12b7 Small test refactoring, extract method.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3951 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-19 01:28:33 +00:00
Eric Davis
a256e4b1dc Scope the calendar popup CSS so it doesn't conflict with the Calendar menu. #6163
Contributed by Adam Soltys

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3950 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-19 01:13:50 +00:00
Eric Davis
73ba49a715 Use the base layout for all 403, 404, and 500 pages. #6172
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3949 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-19 01:01:35 +00:00
Eric Davis
c090d115e2 Added a rake task to display permissions.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3948 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-18 17:23:23 +00:00
Eric Davis
e6e21046c0 No more IssuesController#preview action.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3947 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-18 15:03:42 +00:00
Eric Davis
1f8d396e3f Refactor: move IssuesController#preview to a new controller.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3946 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-18 15:01:35 +00:00
Eric Davis
3eff27344b Refactor: move IssuesController#auto_complete to a new controller. #4382
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3945 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-17 15:03:58 +00:00
Eric Davis
e63acb70ca Add the Calendar as a project menu item.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3944 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-16 23:56:42 +00:00
Eric Davis
a24f448dc0 Add the Gantt chart as a project menu item
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3943 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-16 23:56:37 +00:00
Eric Davis
d2b0a51848 Allow key authentication for Boards. #6132
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3942 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-16 23:39:27 +00:00
Eric Davis
22c978ad94 Refactor: move IssuesController#reply to JournalsController
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3941 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-16 16:25:04 +00:00
Eric Davis
13fe01a185 Refactor: pull up method to ApplicationController.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3940 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-13 14:59:04 +00:00
Jean-Baptiste Barth
32f6fa5b00 Fixed: changing view style in repository/diff doesn't keep previously selected file. #6045
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3939 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-13 06:09:38 +00:00
Eric Davis
f18b126fba Refactor: Pull up method to ApplicationController.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3938 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-12 13:57:51 +00:00
Eric Davis
a6112ef40d Fix path to test_helper
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3937 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-12 13:57:46 +00:00
Eric Davis
ff43bb1a74 Refactor: Extract a new IssueMovesController from IssuesController.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3936 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-11 14:42:10 +00:00
Jean-Baptiste Barth
e43f9fc21e Show projects depending on their visibility in user's profile. #6100
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3935 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-10 23:07:44 +00:00
Jean-Baptiste Barth
0cfa757db0 Added a warning when a new user or group membership is invalid. #3834
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3934 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-10 22:37:00 +00:00
Jean-Baptiste Barth
83e4cf3dd4 Prevent 500 error on login when there's a typo in OpenID URI scheme, such as http;// or http.//
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3933 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-10 21:12:32 +00:00
Eric Davis
52c624aabd Refactor: extract method from #move and #perform_move.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3932 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-10 15:11:11 +00:00
Eric Davis
424b482579 Refactor TimelogController#report's joins and provide a hook to add more.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3931 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-09 23:15:32 +00:00
Eric Davis
a1b89a53eb Refactor: extract POST version of IssuesController#move to #perform_move.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3930 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-09 14:53:23 +00:00
Jean-Baptiste Barth
e00884bcca Removed hard-coded english error message on failed Member creation
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3929 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-09 05:10:00 +00:00
Jean-Baptiste Barth
56e3953a71 Removed useless debug code. #5978
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3928 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-08 20:26:33 +00:00
Jean-Baptiste Barth
56a6d86af5 Added project filter when viewing all issues. #5084
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3927 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-08 09:01:25 +00:00
Jean-Baptiste Barth
bc5d32d6de Replace Test::Unit::TestCase with ActiveSupport::TestCase. #5477
Contributed by Alexey Palazhchenko

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3926 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-08 07:07:38 +00:00
Jean-Baptiste Barth
780bdccc42 Fixed wrong commit range in git log command. #5628
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3925 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-08 07:07:30 +00:00
Jean-Baptiste Barth
a1b607480a Refactor: added link_to_project helper to handle links to projects
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3924 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-08 07:07:20 +00:00
Eric Davis
5b64f0ff96 Updated changelog format to use lists.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3923 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-07 15:11:31 +00:00
Eric Davis
2e14ba95a0 Add static docs to YARD.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3922 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-07 15:11:26 +00:00
Eric Davis
2c79572e4b Save yard doc to the doc/app directory.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3921 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-07 15:11:21 +00:00
Eric Davis
7a3dcdc67f Exclude test files from YARD.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3920 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-07 15:11:15 +00:00
Eric Davis
ab546a14cb Add a rake task to run CI.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3919 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-06 20:46:44 +00:00
Eric Davis
041bca6ac9 Refactor: start to split IssuesController#move into two separate actions.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3918 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-06 15:47:27 +00:00
Eric Davis
e4af6e77a7 Refactor: extract method in IssuesController#move.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3917 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-05 17:43:07 +00:00
Jean-Baptiste Barth
0bcde4ab28 Fixed: assigned_to and author filters in cross project issues view should be based on user's project visibility. #5760
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3916 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-04 21:01:56 +00:00
Jean-Baptiste Barth
8a2bc5772e Focus on the input when adding a related issue on issues/show page. #4656
Contributed by Edouard Briere

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3915 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-04 20:52:31 +00:00
Jean-Baptiste Barth
491c9d2b55 Display projects as links in users/memberships partial. #5898
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3914 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-04 20:41:32 +00:00
Eric Davis
70973fda64 Fix a few requires for metric_fu's rcov.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3913 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-04 15:04:30 +00:00
Eric Davis
8c79385261 Refactor: extract back_url method to ApplicationController.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3912 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-04 13:37:44 +00:00
Eric Davis
a544a1e57c Added css classes to the HTML body based on the theme, controller, and action. #819
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3911 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-04 01:17:45 +00:00
Eric Davis
07cd12978e Add maxlength attributes to some Project fields. #4896
Contributed by Felix Schäfer

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3910 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-04 00:59:15 +00:00
Eric Davis
6f3ae29522 Use the MenuManager for the Administration menu. #6008
Contributed by Jean-Baptiste Barth.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3909 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-04 00:38:22 +00:00
Eric Davis
6e14b8a6af Remove extra css like the border on revision links in Associated Revisions. #5971
Contributed by Holger Just.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3908 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-04 00:27:41 +00:00
Eric Davis
16969fdbbb Show the number of days in the subject line of Reminder emails.
i18n check needed.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3907 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-03 16:01:53 +00:00
Eric Davis
13234f8552 Refactor: Add methods to User to edit the encapsulate the status field.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3906 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-03 15:26:50 +00:00
Eric Davis
25037b841b Refactor: extract method to shoulda macro.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3905 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-02 15:43:04 +00:00
Eric Davis
d3158e37b9 Added tests for AccountController#register POST with automatic registration.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3904 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-02 15:42:59 +00:00
Eric Davis
5c525f0b41 Added tests for AccountController#register GET.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3903 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-08-02 15:42:54 +00:00
Eric Davis
ca663fcd10 Use image_path for the favicon instead to pick up asset hosts. #3301
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3899 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-30 19:18:33 +00:00
Azamat Hackimov
dbb8645353 RTL theme for reference (thanks to Orgad Shaneh, #5972)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3898 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-29 15:48:53 +00:00
Azamat Hackimov
702b7296a0 Translation updates
* de (#5982)
* zh (#5910) - revert last commit & new string


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3897 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-29 15:47:38 +00:00
Eric Davis
5f20bc240e Add a favicon link with support for suburi. #3301
Contributed by Yuki Kita and Christian Boenning

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3894 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-29 14:58:33 +00:00
Azamat Hackimov
2a85a1cebf Translation updates
* de (#5982)
* he (#5972)
* hu (#5962)
* zh (#5910)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3893 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-28 20:05:06 +00:00
Jean-Philippe Lang
4f2d1b7424 Fixed: Vertical scrollbar always visible in Wiki "code" blocks in Chrome (#5861).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3891 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-25 12:56:57 +00:00
Jean-Philippe Lang
db55ea8cb3 Moves LDAP entry in admin menu.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3889 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-25 12:48:28 +00:00
Jean-Philippe Lang
e1c3415795 Fixes context menu broken by r3869 (#5925).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3887 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-25 11:55:57 +00:00
Jean-Philippe Lang
701d2eaeac Display project names in cross-project gantt PNG (#5904).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3885 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-25 11:43:19 +00:00
Jean-Philippe Lang
bdad35a736 Display project names for versions too on PDF (#5904).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3884 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-25 11:19:44 +00:00
Jean-Philippe Lang
cb5e63d846 Display project names in cross-project gantt PDF (#5904).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3883 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-25 11:10:08 +00:00
Jean-Philippe Lang
c47d23a87b Fixed: Deleting statuses doesn't delete all workflow entries (#5811).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3881 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-25 10:48:27 +00:00
Jean-Philippe Lang
73d9368039 Fixed: Add Another file to ticket doesn't work in IE (broken by r3750).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3879 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-25 10:34:55 +00:00
Jean-Philippe Lang
91f7cbb79d Fixed: Forum message permalinks don't take pagination into account (#5945).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3877 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-25 10:02:12 +00:00
Jean-Philippe Lang
72ce4f3ab9 Harmonize french locale "zero" translation with other locales (#5937).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3875 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-25 09:54:59 +00:00
Jean-Philippe Lang
66820dbed3 Strips user email (#5834).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3873 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-25 09:50:41 +00:00
Jean-Philippe Lang
fd0623bf69 Fixes broken status change in context menu (#5892).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3871 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-25 09:47:37 +00:00
Jean-Philippe Lang
379c0a1dd4 Disable priority (#5925) and progress in context menu for parent issues.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3869 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-25 09:38:54 +00:00
Jean-Philippe Lang
fe63eefc1f Adds missing visibility scope.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3866 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-25 09:29:46 +00:00
Jean-Philippe Lang
a9a4e0d6b8 Moves code to controller.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3865 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-25 09:29:17 +00:00
Azamat Hackimov
7f49f07e9a Try to resolve bug #3922
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3864 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-24 10:26:53 +00:00
Azamat Hackimov
90490c92a0 Translation updates
* fr (#5912)
* ru
* zh (#5910)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3863 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-24 09:47:31 +00:00
Eric Davis
a628b0f186 Force-clear the default value for migration from :string to :text. #5846 #5771
This is a special case for MySQL in Win32.

Contributed by Holger Just.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3860 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-24 00:19:32 +00:00
Eric Davis
4fef8b601d Added icon for the LDAP authentication menu item. #5775
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3859 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-20 23:50:15 +00:00
Eric Davis
fa8d0c5c6f Bump version to 1.0.0
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3854 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-18 16:35:43 +00:00
Eric Davis
854ba2a8d2 Adding 1.0.0's release date.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3853 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-18 16:35:37 +00:00
Eric Davis
c087e6f895 Readme update.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3849 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-18 15:49:24 +00:00
Eric Davis
6807d070da Updated the INSTALL and UPGRADING documents for the 1.0 release.
Contributed by Mischa The Evil

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3848 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-18 15:19:04 +00:00
Azamat Hackimov
09a681db8b Translation updates
* ru 
* sv (#5740)
* pt-BR (#5785)
* zh (#5158, #5883)
* zh-TW (#5735)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3847 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-16 16:47:00 +00:00
Eric Davis
9cfa15917d Added an official favicon.ico
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3845 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-16 03:45:50 +00:00
Eric Davis
d064f27271 Updating CHANGELOG for 1.0
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3843 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-16 03:25:03 +00:00
Eric Davis
c6223864d0 Add :view_issues_index_bottom hook. #5169
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3842 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-16 03:16:29 +00:00
Eric Davis
5e259d41e2 Fix typos in the examples. #5823
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3841 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-16 03:11:35 +00:00
Eric Davis
cd54efa0c9 Force the test RAILS_ENV to help prevent purging data when mistyping. #4572
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3840 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-09 13:46:20 +00:00
Eric Davis
a84ea6866d Version 0.9.6
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3836 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-08 03:53:07 +00:00
Eric Davis
83f1ecb980 Update the CHANGELOG for 0.9.6
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3835 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-08 03:53:01 +00:00
Eric Davis
69af1515d1 Check the browse repository or commit access permissions in Redmine.pm with LDAP. #5797
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3832 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-08 03:46:19 +00:00
Eric Davis
ef5bca0fef Have Redmine.pm respect the Authentication Required setting. #5797
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3831 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-08 03:46:14 +00:00
Eric Davis
0d5fca9ba5 Force the default value of path to be set on the Change model class. #5771
This is needed because MySQL does not support default values on text columns
(Error introduced in r3828)

Contributed by Holger Just

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3830 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-06 21:06:09 +00:00
Eric Davis
ad73f12a6b Include visible subprojects when checking for available Activity event types. #5589
Contributed by Felix Schäfer

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3829 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-06 02:22:03 +00:00
Eric Davis
56af944afa Extend changes.path and changes.from_path to support longer paths. #5771
Long paths to SCM files would be truncated or wouldn't insert into some
databases.  This extends those fields to support arbitrary length filenames.

Contributed by Holger Just

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3828 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-06 02:07:46 +00:00
Eric Davis
48a5460da4 Allow js formatted responses.
Otherwise they return the invalid format error (406) instead of 403, 404, or 500

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3827 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-05 18:00:50 +00:00
Eric Davis
bd1384db7d Added hook :controller_timelog_available_criterias
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3826 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-01 18:49:25 +00:00
Eric Davis
11ff334cd8 Refactor: extract @available_criterias to utility method.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3825 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-07-01 18:47:25 +00:00
Eric Davis
64d2221db5 Set @project so macros will work on the welcome and project list. #5781
Contributed by Holger Just

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3824 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-30 03:32:18 +00:00
Eric Davis
43bbda1966 Don't style lists in a project description as a sub-project. #5752
Contributed by Holger Just

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3823 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-30 03:14:30 +00:00
Eric Davis
5a17b5ba39 Fix Chrome JavaScript bug in the pop up calendar. #5769
Contributed by Holger Just

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3822 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-30 02:54:15 +00:00
Eric Davis
c6201ae15b Recalculate inherited attributes on parents when a child is moved under a new parent. #5524
Contributed by Jean-Baptiste Barth.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3821 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-30 02:45:34 +00:00
Eric Davis
305cab100f Rename code and locale typo: Developper. #5751
Contributed by Holger Just

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3820 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-30 01:55:44 +00:00
Eric Davis
6ef240841c Add some tests for User#try_to_login with LDAP. #5592
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3819 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-30 01:47:47 +00:00
Eric Davis
bc3ad9af38 Bump release to 0.9.5
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3815 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-28 02:46:25 +00:00
Eric Davis
c7d075fb68 Updating documentation for the 0.9.5 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3814 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-28 02:46:20 +00:00
Eric Davis
672852baaf Force string comparison for login search to be case sensitive on MySQL. #2473
Contributed by Holger Just.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3813 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-24 03:08:20 +00:00
Eric Davis
6eea3300f8 Sanitize image links and handle nils in the toc formatter. #5445
Based on contribution by Yuki Kita.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3811 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-24 02:35:24 +00:00
Azamat Hackimov
68bdbbac7d Refresh locales
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3810 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-21 19:58:17 +00:00
Azamat Hackimov
e21b91485e New language en-GB (#5648 thank to Tom Knight for work)
Updates:
* ja (#5715)
* sv (#5671)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3809 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-21 19:54:55 +00:00
Eric Davis
6cb4ff7d89 Refactor and documentation for User#find_by_login.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3808 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-20 21:40:55 +00:00
Eric Davis
de17640489 Change User#login to be case-insensitive. #2473
This change also overrides User#find_by_login to give priority to exact
matches in the login.

Contributed by Greg Mefford

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3807 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-20 21:40:50 +00:00
Eric Davis
7376ef2ad7 Typecast issue ids when searched for.
Older Postgres versions don't allow searching over the id field's max size.

Contributed by Holger Just

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3806 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-20 20:01:32 +00:00
Eric Davis
c98f46d691 Handle unsuccessful destroys in TimelogController. #5700
Contributed by Jan

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3805 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-20 19:30:51 +00:00
Eric Davis
acef1d5352 Exclude fields_for in the TabularFormBuilder, it has a different method
signature. #5416

Contributed by Jean-Baptiste Barth

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3804 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-20 19:03:09 +00:00
Eric Davis
d2f8feb7c5 Optimize the N+1 query in watcher_recipients. #5415
Contributed by Holger Just

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3803 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-20 18:41:30 +00:00
Eric Davis
116c7a7964 Add a link to the cross project time entries page to /projects. #4935
Contributed by Jan.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3802 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-20 16:29:12 +00:00
Eric Davis
4083e7e622 Truncate incoming email subject lines to 255 characters. #5698
Contributed by Jan

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3801 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-20 16:22:36 +00:00
Eric Davis
c5863c0ad0 Support listing directories in svn which include square brackets. #5548
Contributed by Gregor Schmidt

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3800 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-20 16:08:26 +00:00
Eric Davis
70da33fc29 Add some assertions on the default data loading test to debug a Ruby 1.9 failure.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3799 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-20 16:08:20 +00:00
Eric Davis
95c3c63cfe Fix for Ruby 1.9 strings.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3798 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-20 16:08:15 +00:00
Eric Davis
5db9a982cc Added two hooks to the issues report. #5233
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3797 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-20 03:37:42 +00:00
Eric Davis
41ff1b9cb2 Display the correct ISO week number on the project calendar.
http://en.wikipedia.org/wiki/ISO_week

Contributed by Holger Just

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3790 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-19 22:52:15 +00:00
Eric Davis
b2e903ff7c Calculate the correct week number for weeks starting at any date. #4857
http://en.wikipedia.org/wiki/ISO_week

Contributed by Holger Just

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3789 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-19 22:52:09 +00:00
Eric Davis
093853b56a Added tests for #5533's behavior.
Contributed by Gregor Schmidt.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3788 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-19 22:17:34 +00:00
Eric Davis
b10818f4a1 Add a css class to hide content when printing. #5508
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3787 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-19 21:53:58 +00:00
Eric Davis
b53de502ab Display the link name when external links are used in the toc macro. #5445
Contributed by Yuki Kita and Jean-Baptiste Barth

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3786 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-19 21:39:21 +00:00
Eric Davis
69d9600f48 Added documentation about the session_path. #3968
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3785 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-19 20:04:47 +00:00
Eric Davis
2562bc5bcd Hide the role forms when editing or adding Project members. #5452
Contributed by Yuki Kita

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3784 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-19 19:51:43 +00:00
Eric Davis
d48eb2de47 Accept email from anonymous users with an empty from address. #5604
Contributed by Andrew Fenn

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3783 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-19 19:24:17 +00:00
Eric Davis
5f93750c80 Pin shoulda, 2.11.x changes it's API and deprecates a lot of macros.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3782 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-19 19:01:53 +00:00
Eric Davis
921b425b8e Accept key authentication to ProjectsController#index (for feeds). #5317
Contributed by Greg Mefford

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3777 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-19 17:45:04 +00:00
Eric Davis
d635fff3f5 Add ATOM auto discovery link to the Projects list. #5317
Contributed by Greg Mefford

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3776 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-19 17:44:59 +00:00
Eric Davis
43fabab950 Fixed HTML validation errors
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3775 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-19 16:50:13 +00:00
Eric Davis
f08b2a394d Unused variable
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3774 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-19 03:54:28 +00:00
Eric Davis
cf334cee31 Refactor: extract test method
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3773 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-19 03:54:23 +00:00
Eric Davis
c56c0f411c Fix a nil error when a Project cannot save attachments.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3772 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-19 03:54:17 +00:00
Eric Davis
759858d11d Add example url and author url to the plugin generator.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3771 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-17 19:02:13 +00:00
Eric Davis
1a145dca25 Plugin models and controllers must be unloadable.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3770 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-17 19:02:07 +00:00
Eric Davis
28519714e0 Updated the project calendar for greater accessibility.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3769 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-15 23:20:32 +00:00
Eric Davis
f18c0c790b Ignore rubinius .rbc files
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3768 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-09 22:01:21 +00:00
Azamat Hackimov
3096166f09 fr update (#5617)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3767 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-05 07:20:30 +00:00
Eric Davis
345301284a Added JSON support to the issues API. #1214
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3766 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-05 03:52:59 +00:00
Eric Davis
f484fe8556 Convert tests to shoulda
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3765 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-06-05 03:52:53 +00:00
Eric Davis
e94c45d548 Add an "Assigned To" keyword to receiving email. #5594
Will take a user's email address, login, or full name.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3764 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-29 00:05:24 +00:00
Eric Davis
080dc2212e Add 'Start date' and 'End date' keywords for incoming email. #5595
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3763 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-28 23:45:45 +00:00
Azamat Hackimov
af726ea8f0 Locales update
* ja (#5607)
* ru (#5606)
* pt-BR (#5611)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3762 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-28 15:32:01 +00:00
Eric Davis
1a4b23731f Include the Project name when sorting versions.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3761 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-27 17:16:15 +00:00
Eric Davis
f3cc84b343 Show subproject versions on the Roadmap.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3760 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-27 17:16:10 +00:00
Eric Davis
e5ac73b7dc Somehow ran out of unique identifiers.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3759 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-27 17:16:05 +00:00
Azamat Hackimov
b88b15183b Translation updates, Locales sync (rake locales:update)
* ru
* zh-TW (#5602)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3758 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-27 15:34:22 +00:00
Eric Davis
6e529f82a7 Make the Gantt zoom images more accessible
* Correct the alt text
* Add text links next to the images
* Size the images in em to support browser scaling

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3757 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-26 22:48:35 +00:00
Eric Davis
ab4bdc379e Refactor: extract gantt zoom links to a helper.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3756 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-26 22:48:17 +00:00
Eric Davis
c3c3460e0d Add the page title when editing /my/page.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3755 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-26 21:52:03 +00:00
Azamat Hackimov
2b203fe5ab Translation update: Japanese (#5600 get haha, congratulations!)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3754 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-26 20:59:51 +00:00
Azamat Hackimov
ef1c63efbf Translation updates
* de (#5558)
* sv (#5570)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3753 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-26 17:43:50 +00:00
Azamat Hackimov
8c02e4c723 zh-TW update (#5597)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3752 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-26 17:36:25 +00:00
Azamat Hackimov
513c18d2d3 Translation sync
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3751 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-26 17:33:01 +00:00
Eric Davis
e320017cf0 Convert the file attachment's description to a label.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3750 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-25 00:11:58 +00:00
Eric Davis
0d34c3731b Add error messages to MembersController#new so a user is informed of missing data.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3749 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-24 20:21:16 +00:00
Eric Davis
f97ff28628 Invalid HTML
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3748 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-24 20:21:08 +00:00
Eric Davis
52a92b1822 Added a missing label to my/page_layout.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3747 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-24 18:23:42 +00:00
Eric Davis
51d3f51159 Added missing labels to the Issue Calendar.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3746 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-24 18:07:43 +00:00
Eric Davis
908d44519c Allow AuthSources to control if they allow password changes.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3745 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-23 03:16:37 +00:00
Eric Davis
715c9d16ef Refactor AuthSourcesController to support non-LDAP sources. #1131
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3744 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-23 03:16:31 +00:00
Eric Davis
a1f73c8b20 Remove hard coded LDAP log message.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3743 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-23 03:16:25 +00:00
Jean-Philippe Lang
3601c291d0 Typo in french locale.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3741 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-22 08:54:43 +00:00
Jean-Philippe Lang
1933f9ddb2 Typo in french locale.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3740 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-22 08:54:17 +00:00
Azamat Hackimov
e474a6095b Updates (#5409)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3739 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-15 13:12:52 +00:00
Azamat Hackimov
8c93cc4381 sr and sr-CY languages (#5409)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3738 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-15 13:11:26 +00:00
Azamat Hackimov
cd097a5758 New translation (Latvian, #5446, thanks to Dzintars Bergs), translation updates
* ja (#5368)
* he (#5370)
* hu (#5364)
* no (#5471)
* ru (#5476)
* sk (#5432)
* sr (#5409)
* sv (#5467)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3737 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-14 18:15:41 +00:00
Eric Davis
a7b80fcc06 Use present? instead of any?. Handles nil case.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3736 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-14 17:15:09 +00:00
Jean-Philippe Lang
e417d755e9 Eager load group projects to avoid N SQL queries.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3735 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-09 11:31:39 +00:00
Jean-Philippe Lang
0fa124a92c Adds missing thead tags (#5440).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3734 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-09 11:19:15 +00:00
Eric Davis
36c82ecc1f Refactor: move Project Calendar to it's own controller.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3732 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-03 16:02:37 +00:00
Jean-Philippe Lang
469ff0fb4e Fixing indentation.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3731 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-01 14:21:59 +00:00
Jean-Philippe Lang
2fda2c1826 Adds trackers to individual project XML (#5342).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3730 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-01 14:21:41 +00:00
Jean-Philippe Lang
cbd4af236c Adds issue relations to individual issue XML (#5305).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3729 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-01 14:07:36 +00:00
Jean-Philippe Lang
a499efd328 Updates for 0.9.4 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3727 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-01 12:37:20 +00:00
Jean-Philippe Lang
faeae788be Adds auto scroll to context submenus (#2863).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3725 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-01 11:54:40 +00:00
Jean-Philippe Lang
edeb84a070 Fixes strange random test failures with Mysql.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3724 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-01 10:09:20 +00:00
Jean-Philippe Lang
6a8dc735d3 Fixes Bazaar adapter for JRuby/Win32 (#5404).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3723 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-01 09:56:59 +00:00
Jean-Philippe Lang
b897089729 Fixes Mercurial adapter for JRuby (#4494, #5404).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3722 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-01 09:55:40 +00:00
Jean-Philippe Lang
4a4bc93278 Adds jdbc rake tasks (#5404).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3721 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-01 09:37:46 +00:00
Jean-Philippe Lang
1be15816dd Adds test helpers for test repositories.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3720 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-05-01 08:32:21 +00:00
Jean-Philippe Lang
80f7faf10a Fixes EOL style related test failures.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3719 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-30 18:08:40 +00:00
Jean-Philippe Lang
0a47389a65 Fixes some textile issues with JRuby (#5404).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3718 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-30 17:50:34 +00:00
Eric Davis
306ca5e714 Refactor: Pull up #find_optional_project to ApplicationController.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3716 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-30 17:24:11 +00:00
Jean-Philippe Lang
f65c47e3a7 Fixes theme discovery under JRuby on Rails war structure (#632, #5404).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3710 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-30 12:44:19 +00:00
Jean-Philippe Lang
da7c89bfd0 Fixes workflow edit with JRuby (#5404).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3709 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-30 12:19:51 +00:00
Jean-Philippe Lang
663f0c83b2 Fixes time report with JRuby (#5404).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3708 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-30 12:18:11 +00:00
Jean-Philippe Lang
554c047d80 Fixes random test failure.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3707 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-30 11:27:08 +00:00
Jean-Philippe Lang
a4e411c643 Removes broken code in ProjectsController (#5412).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3706 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-30 11:14:57 +00:00
Jean-Philippe Lang
a00d1eabd7 Fixed: watchers selection lost when issue creation fails (#5406). #watched_by? was fixed in order to work with #watcher_user_ids= used in controllers on unsaved objects.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3705 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-30 11:02:27 +00:00
Jean-Philippe Lang
e5180884c1 Fixes selected menu tab when issue creation fails.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3704 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-30 10:48:19 +00:00
Jean-Philippe Lang
51c8f3143c Fixes URLs in atom feeds broken by r3681 (#5403).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3703 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-30 10:45:21 +00:00
Jean-Philippe Lang
0d938dff59 Fixed: 500 internal error when browsing any Redmine page in Epiphany (#5401).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3702 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-30 10:33:25 +00:00
Jean-Philippe Lang
3db77a86a9 Fixes some parameters in functional tests.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3701 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-30 10:06:51 +00:00
Jean-Philippe Lang
b6cd076441 Fixes an object instantiation in tests.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3700 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-30 09:57:58 +00:00
Eric Davis
7ccdaee110 Only copy visible issues.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3698 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-29 23:26:52 +00:00
Eric Davis
488879d9cf Refactor: pull #query_statement_invalid up to ApplicationController.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3696 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-29 15:19:19 +00:00
Eric Davis
e65c3cfc7d Refactor: Move gantts to a separate controller.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3695 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-28 15:54:46 +00:00
Jean-Philippe Lang
a925435b29 Add "Check/Unckeck all" buttons to rows and columns on workflow edit screen (#3201).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3694 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-27 17:54:27 +00:00
Jean-Philippe Lang
eabe8c51cd Adds an assertion on issues deletion when deleting a project (#5381).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3693 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-27 16:52:41 +00:00
Jean-Philippe Lang
c18cb9451d Fixed: private subprojects names may be revealed on issue summary report (#5360).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3692 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-27 16:43:52 +00:00
Eric Davis
df7edb843b Refactor: move method to helper.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3691 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-27 15:28:52 +00:00
Eric Davis
dfc448030d Refactor: extract error to new method with before_filter.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3690 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-26 15:08:06 +00:00
Eric Davis
23f097e344 Refactor: Extract duplicated code to a new method.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3689 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-23 15:05:13 +00:00
Eric Davis
2c898d8a81 Refactor: Split IssuesController#new to #new and #create to match REST pattern.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3688 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-22 15:43:57 +00:00
Eric Davis
9b595c689d Refactor: Move the rest of the routing tests to RoutingTest.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3687 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-21 16:02:55 +00:00
Eric Davis
0fc884cf42 Move more routing tests into the routing integration test.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3686 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-20 15:42:57 +00:00
Eric Davis
657aa624a4 Adding missing setter for Query#available_columns
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3685 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-20 15:42:52 +00:00
Eric Davis
bf33b57aa4 Refactor: Move method to Query model
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3684 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-19 15:08:28 +00:00
Jean-Philippe Lang
0004b52646 Fixes behaviour of move_issues permission for non member role (#5309).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3683 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-18 12:47:41 +00:00
Jean-Philippe Lang
de51e16d2b Removes hard-coded size on user search fields (#5319).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3682 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-18 10:17:27 +00:00
Jean-Philippe Lang
760c933bd7 Fixes acts_as_event with a String as url option (#5343).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3681 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-18 10:00:11 +00:00
Jean-Philippe Lang
3bf6790826 Add "Repository" menu item after repository creation (#5328).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3680 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-17 12:51:46 +00:00
Jean-Philippe Lang
aa4d1fe816 Fixed: API 401 response does not include WWW-Authenticate header (#5322).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3679 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-17 12:45:23 +00:00
Jean-Philippe Lang
2c0ce104e7 Fixed: Error on db:migrate with table prefix set (#5335).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3678 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-17 12:14:52 +00:00
Jean-Philippe Lang
e4e4b0f4b8 Fixes error on subtask XML format (#5336).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3677 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-17 11:49:56 +00:00
Azamat Hackimov
7e9d4811ec Translation updates
* eu (#5291)
* ru (#5296)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3676 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-17 10:37:23 +00:00
Eric Davis
538642c5c4 Let custom field formats control how they are edited.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3675 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-16 15:34:05 +00:00
Eric Davis
4cb943571a Change the case statement into a method call based on the name
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3674 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-16 15:34:00 +00:00
Eric Davis
d22723ed04 Refactor: Move method
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3673 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-16 15:33:55 +00:00
Eric Davis
88db9d0bdc Replace the hardcoded CustomField::FIELD_FORMATS with a class.
Custom Field Formats are now full objects and can be registered with
Redmine::CustomFieldFormat to add new formats.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3672 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-16 15:33:49 +00:00
Jean-Philippe Lang
0e1595ad56 Adds parent issue id in issue REST API (#5304).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3671 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-13 17:51:06 +00:00
Eric Davis
6a6f403978 Added missing Enumeration STI exemplars.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3670 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-13 05:57:27 +00:00
Jean-Philippe Lang
f23aceba86 Fixes a test failure with SQLite3 due to transaction fixtures (#5290).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3669 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-12 17:43:01 +00:00
Jean-Philippe Lang
9ccccb9984 Do not raise an error when destroying a Version with assigned issues.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3668 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-11 17:47:29 +00:00
Jean-Philippe Lang
c76b9edf6c Adds bottom margin to issue watchers avatars (#5264).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3667 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-11 17:14:18 +00:00
Jean-Philippe Lang
734f8324ea Fixed: empty ul tag for issue updates with notes only (#5281).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3666 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-11 17:10:26 +00:00
Jean-Philippe Lang
f77e5d093a Merge issue and time entry validation error messages.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3665 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-11 17:00:13 +00:00
Jean-Philippe Lang
f35921d308 Fixes Issue#save_issue_with_child_records so that time entry do not get saved if issue save fails.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3664 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-11 16:48:46 +00:00
Jean-Philippe Lang
43e5bb75d2 Fixed: issue optimistic locking broken by r3308 (#5280).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3663 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-11 16:27:37 +00:00
Jean-Philippe Lang
2674c6116c Linkify folder names on revision view (#5164).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3659 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-11 15:18:49 +00:00
Jean-Philippe Lang
5163904a3a Fixed: Update of Subversion changesets broken by r3466 under Solaris (#5255).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3635 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-11 13:55:30 +00:00
Jean-Philippe Lang
9306f3ea6c Check that admin LDAP user is untouched after authentication (#5263).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3634 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-11 13:54:42 +00:00
Jean-Philippe Lang
71a4158fd0 Fixes calculation of version estimated hours with subtasks (#5265).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3633 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-11 13:20:02 +00:00
Jean-Philippe Lang
5879273739 Makes the wiki sidebar editable (#5208).
The content of the wiki page named 'Sidebar' is rendered in the sidebar if it exists. Permission to edit protected pages is required to create this page.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3632 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-11 12:56:18 +00:00
Eric Davis
3979224693 Removed count from issues.xml. It causes a typecast error in ActiveResource. (#4745)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3631 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-10 07:30:06 +00:00
Eric Davis
f3e3752759 Added rake task to generate the yardoc (rake yard)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3630 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-09 23:42:41 +00:00
Azamat Hackimov
77795d41ba Translation updates:
* ko (#5245)
* fr (#5247)
* pt-BR (#5244)
* zh


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3629 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-06 16:57:51 +00:00
Azamat Hackimov
db453db497 Translation updates
* ja (#5240)
* zn-TW (#5241)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3628 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-03 18:02:51 +00:00
Azamat Hackimov
eb80b79c26 New strings to localization (#5225)
* error_can_not_delete_custom_field: Unable to delete custom field
* error_unable_to_connect: Unable to connect ({{value}})
* error_can_not_remove_role: This role is in use and can not be deleted.
* error_can_not_delete_tracker: This tracker contains issues and can't be deleted.


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3627 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-03 11:54:24 +00:00
Azamat Hackimov
f2993f6ed0 Translation updates and new Mongolian language (#5237)
* da (#5197)
* pt-BR (#5205 and #5210)
* zh (#5227)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3626 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-04-03 10:49:16 +00:00
Jean-Philippe Lang
8df189ff86 Adds a css class to issue id tds (#5204).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3625 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-29 19:50:00 +00:00
Jean-Philippe Lang
c88a467ddf Fixes an error when running tests coverage.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3624 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-28 15:18:09 +00:00
Jean-Philippe Lang
2bbc948e06 Preview description if it was edited while updating an issue (#741).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3623 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-28 15:17:46 +00:00
Jean-Philippe Lang
c499d2d150 Fixes broken test.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3622 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-28 14:52:33 +00:00
Jean-Philippe Lang
ce549933c8 Applies the same style to the admin project tree than issues.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3621 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-28 13:30:44 +00:00
Jean-Philippe Lang
e8729feba7 Slight optimization in issue tree rendering.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3620 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-28 12:57:39 +00:00
Jean-Philippe Lang
4a51f10074 Fixes diff highlighting (#5199).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3619 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-28 11:30:48 +00:00
Jean-Philippe Lang
4524bc04e9 Issue list improvements for subtasking (#5196):
* makes the parent task column available
* display the issue hierarchy when list is sorted by parent

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3618 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-27 16:55:20 +00:00
Jean-Philippe Lang
06d2c3fd4e Adds parent issue id to the issues CSV export.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3617 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-27 13:38:29 +00:00
Jean-Philippe Lang
b93cf03483 Fixes backslashes escaping when quoting issue description/note (#5129).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3616 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-27 13:33:19 +00:00
Jean-Philippe Lang
883e7b1b94 Width of pre blocks in descriptions changed to auto (#5160).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3615 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-27 13:10:22 +00:00
Jean-Philippe Lang
43a5febf61 Fixed: Links to missing wiki pages not red on project overview page (#5177).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3614 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-27 12:54:13 +00:00
Jean-Philippe Lang
89aa60cbbd Escape revision on repository view (#5153).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3613 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-27 12:46:45 +00:00
Jean-Philippe Lang
8cdcf308be Escape href attribute in auto links (#5179).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3612 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-24 20:26:22 +00:00
Jean-Philippe Lang
84dfff5957 Fixes permission check in QueriesController (#5181).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3611 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-24 20:25:09 +00:00
Azamat Hackimov
d29adc9bb7 Translation updates and fixes
* ja.yml (#5097)
* pt-BR.yml (#5120)
* sl.yml (#5063)
* sv.yml (#5144)
* zh-TW (#5075)



git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3610 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-24 16:55:01 +00:00
Azamat Hackimov
0de66f7f1c Translation ru, fix for #5157
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3609 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-24 02:48:09 +00:00
Jean-Philippe Lang
79b4f68176 Adds an option to enable/disable email notifications during a project copy (#4672).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3608 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-21 11:17:29 +00:00
Jean-Philippe Lang
5225fb70f5 Optimize retrieval of user's projects members.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3607 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-21 09:28:24 +00:00
Jean-Philippe Lang
e584ae220d Cleans up status assignment in IssuesController#new handled by Issue#safe_attributes= now.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3606 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-20 17:37:04 +00:00
Jean-Philippe Lang
533590c29c Fixed: Double-dash results in strikethrough text (#5122).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3605 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-20 12:54:23 +00:00
Eric Davis
6e6e260cea Refactor: Extract Query#sortable_columns from the controller.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3604 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-19 15:42:03 +00:00
Jean-Philippe Lang
5e4eef1a4e Fixed: code syntax highlighting not working in Document page (#3740).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3603 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-18 20:06:16 +00:00
Jean-Philippe Lang
4dd4300147 Adds text formatting to documents index (#202).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3602 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-18 20:02:17 +00:00
Eric Davis
b5d9a1dfbd Refactor: Extract method to the Member model
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3601 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-18 15:49:11 +00:00
Jean-Philippe Lang
f741ca7269 Adds an helper for displaying the 'checked' image.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3600 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-17 20:46:22 +00:00
Jean-Philippe Lang
388805ad58 Do not query the database for project level on projects list.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3599 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-17 20:06:52 +00:00
Jean-Philippe Lang
0e0b8e0799 Accept issue links inside brackets (#4418).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3598 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-17 19:57:32 +00:00
Eric Davis
194dab8e96 Refactor: Change the different find_object filters to share a common method.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3597 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-17 15:41:58 +00:00
Eric Davis
0f999ef0e0 Fixes undefined method 'notes' error from r3594
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3596 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-17 04:13:05 +00:00
Jean-Philippe Lang
2ada68ae62 Removes broken links in wiki syntax help page (#5081).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3595 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-16 21:48:21 +00:00
Jean-Philippe Lang
f4be4d101b Inlines some code to speed up large ticket history rendering.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3594 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-16 21:37:05 +00:00
Jean-Philippe Lang
2f0fbef858 Fixed: Wrong label in issue-journal for subtask-changes (#5090).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3593 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-16 20:37:29 +00:00
Jean-Philippe Lang
0097770626 Upgrade CodeRay to 0.9.2 (#3359).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3592 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-16 20:29:12 +00:00
Eric Davis
e6c8760ad7 Refactor: Split the find_object methods to prep for a larger refactoring.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3591 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-16 15:17:47 +00:00
Jean-Philippe Lang
6b88de1234 Close unclosed pre/code tags (#4265).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3590 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-15 19:57:35 +00:00
Jean-Philippe Lang
7203196212 Do not parse redmine links inside pre/code tags (#1288).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3589 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-15 19:54:50 +00:00
Jean-Philippe Lang
a179f261cd Extract parsing of inline attachments, wiki links and redmine links.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3588 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-15 19:37:01 +00:00
Jean-Philippe Lang
838f3195e7 Fixed closing tags in syntax highlighting tests.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3587 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-15 19:33:05 +00:00
Jean-Philippe Lang
6a1920915c Add background color to line numbers of highlighted code.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3586 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-14 14:53:25 +00:00
Jean-Philippe Lang
ad487bf7cc Replace the hardcoded "CodeRay" css class name for highlighted elements.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3585 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-14 13:22:50 +00:00
Jean-Philippe Lang
c03b4c2e1e Extract CodeRay calls to Redmine::SyntaxHighlighting (#2985).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3584 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-14 12:57:08 +00:00
Jean-Philippe Lang
51d790b2f3 Reverts r1476 that is no longer needed after r3582.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3583 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-14 09:07:21 +00:00
Jean-Philippe Lang
7621463acb Do not html escape code that is going to be highlighted (#2985, #3359).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3582 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-14 09:05:58 +00:00
Jean-Philippe Lang
2f5c17a0f6 Optimization: load attachments when needed.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3581 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-14 08:33:53 +00:00
Jean-Philippe Lang
c3e8d1d512 Enlarges the sidebar.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3580 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-13 17:50:10 +00:00
Jean-Philippe Lang
e605973ec4 Move issue watchers to the sidebar.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3579 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-13 17:45:41 +00:00
Jean-Philippe Lang
8cc519e763 Adds index on issues nested set columns.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3578 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-13 17:17:10 +00:00
Jean-Philippe Lang
508df4a33a Slight UI changes to the subtasks tree.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3577 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-13 15:47:06 +00:00
Jean-Philippe Lang
e016f3ef13 Add subtasking strings to locales.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3576 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-13 15:45:22 +00:00
Jean-Philippe Lang
54eea93def Translations updates.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3575 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-13 15:42:57 +00:00
Jean-Philippe Lang
d550c46160 Makes subtasks rescheduled when a 'precedes' relation is set on a parent task.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3574 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-13 15:29:34 +00:00
Jean-Philippe Lang
8e3d1b694a Adds subtasking (#443) including:
* priority, start/due dates, progress, estimate, spent time roll-up to parent issues
* descendant issues tree displayed on the issue view with context menu support
* issue tree display on the gantt chart
* issue tree copy on project copy
* unlimited nesting

Defining subtasks requires the new permission 'Manage subtasks'.
Subtasks can not belong to a different project than the parent task.

Implementation is based on scoped nested sets for fast reads and updates.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3573 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-13 14:56:49 +00:00
Jean-Philippe Lang
e109c9b6b6 Do not reset session when a non logged in user request the login form (#4958).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3572 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-12 19:22:11 +00:00
Jean-Philippe Lang
7e4aa9ca92 Fixed windows platform detection in reposman.rb (#5039).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3571 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-12 18:15:19 +00:00
Jean-Philippe Lang
c312b8b058 More detailed failures message.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3570 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-12 15:22:05 +00:00
Jean-Philippe Lang
4d19007fd1 Fixes a test failure.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3569 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-12 15:19:49 +00:00
Jean-Philippe Lang
7adf05810a Refactors textile formatting tests.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3568 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-12 15:17:53 +00:00
Jean-Philippe Lang
054ff0db8e Fixed: inline code with less-then/greater-than produces @lt; and @gt; (#1416).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3567 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-12 15:12:23 +00:00
Jean-Philippe Lang
3dc4dbe302 Fixed: error while moving an issue to a project with disabled tracker with SQLite3 (#5049).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3566 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-12 14:51:33 +00:00
Jean-Philippe Lang
63ed494f21 Adds missing flash messages on project settings (#5043).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3565 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-12 13:51:00 +00:00
Jean-Philippe Lang
b028a48c53 Cleanup and warning added in database.yml.database about the test database.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3564 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-12 13:48:14 +00:00
Jean-Philippe Lang
24b4c5f48d Fixed: text formatting breaks when using parenthesis inside bold tags (#1334).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3563 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-12 13:43:11 +00:00
Jean-Philippe Lang
6ea1ef77e5 Fixed: avatar display breaks user profile page layout (#5008).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3562 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-12 13:35:36 +00:00
Jean-Philippe Lang
4e5078ebb0 Fixed: some textile modifiers combinations don't work, eg. bold inside underline (#5045).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3561 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-11 19:55:31 +00:00
Eric Davis
c7c3c0cc7c Refactor: Extract method for setting the flash on bulk issue saves.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3560 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-11 16:34:08 +00:00
Jean-Philippe Lang
3e2e5a0a0c Removes broken javascript on wiki history.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3559 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-10 19:16:03 +00:00
Eric Davis
ddaa95523a Use assert_select to check the download link. (#4204)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3558 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-10 05:38:58 +00:00
Eric Davis
22c1e2b8cf Adds named scopes to replace custom finders.
* Adds watched_by class method in ActsAsWatchable
* Adds Issue#recently_updated, Issue#with_limit and Issue#on_active_project

  #2482

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3557 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-10 05:10:43 +00:00
Azamat Hackimov
cfa1f0c231 Translation updates
* ja (#4946)
* zh-TW (#4947)
* ru



git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3556 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-09 21:02:11 +00:00
Jean-Philippe Lang
b99d4189b7 Fixed: NoMethodError error on roadmap if no trackers are selected and a shared version is available (#5006).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3555 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-08 20:17:09 +00:00
Eric Davis
c07696b578 Refactor: Remove duplicated case statements.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3554 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-08 16:47:57 +00:00
Eric Davis
43e3c43cbd Refactor: Extract similar logic in IssuesHelper#show_detail to a new method.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3553 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-08 16:47:52 +00:00
Eric Davis
1a73f8fa0f Added unit tests for IssuesHelper#show_detail
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3552 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-08 16:47:47 +00:00
Jean-Philippe Lang
fe066e793d Moves scm adapters unit tests to a dedicated folder.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3551 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-06 20:35:38 +00:00
Jean-Philippe Lang
332d88cd67 Makes test pass with Bazaar 2.1.0 (#5002).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3550 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-06 20:14:09 +00:00
Jean-Philippe Lang
6511e94233 Moves ProjectsController#add_issue_category to IssueCategoriesController#new.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3549 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-06 18:43:00 +00:00
Jean-Philippe Lang
729bea1cf3 Moves ProjectsController#add_version to VersionsController#new.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3548 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-06 18:25:01 +00:00
Jean-Philippe Lang
2af97616f7 Adds context menu to the roadmap issue lists.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3547 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-06 18:03:25 +00:00
Eric Davis
d65922afde Remove double negative condition
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3546 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-05 17:25:51 +00:00
Eric Davis
3f3e30c2aa Refactor: Moved the contents of #issue_update into Issue.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3545 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-05 17:11:50 +00:00
Jean-Philippe Lang
69769e1dfd Unselect issues when clicking outside of the list.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3544 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-04 19:09:14 +00:00
Jean-Philippe Lang
e8bc0b2980 Do not disable text selection in the issue list.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3543 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-04 19:06:33 +00:00
Jean-Philippe Lang
bd5fe10c13 Adds an helper for creating the context menu.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3542 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-04 19:02:57 +00:00
Jean-Philippe Lang
12759fd416 Fixed: hard coded English string at the selection of issue watchers (#4982).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3541 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-04 18:49:03 +00:00
Eric Davis
c58dc83e74 Refactor: Replace @journal with @issue.current_journal
This removes an instance variable in #issue_update which will let it be moved
to the Issue model.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3540 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-04 16:18:51 +00:00
Eric Davis
7514e12d33 Hide the main menu div if there isn't any items for it. #3259
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3539 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-04 06:01:05 +00:00
Eric Davis
afdcd770dc Refactor: Extract method
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3538 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-04 05:33:54 +00:00
Eric Davis
bc79caaf69 Refactor: Extract method
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3537 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-04 05:33:49 +00:00
Eric Davis
be20d98040 Refactor: Extract methods to before_filters
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3536 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-04 05:33:45 +00:00
Eric Davis
3a99f18913 Refactor: Split method
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3535 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-04 05:33:40 +00:00
Eric Davis
ca6df3d78d Refactor: Extract method
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3534 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-04 05:33:35 +00:00
Eric Davis
976cba0c5d Refactor: use the existing Issue#new_statuses_allowed_to method.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3533 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-04 00:58:52 +00:00
Jean-Philippe Lang
cdb86d5ef7 Fixed: no email notification on new project/version file added (#4966).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3532 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-03 21:32:41 +00:00
Jean-Philippe Lang
3355ffe029 Fixes git annotate (#3832).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3531 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-03 21:15:46 +00:00
Jean-Philippe Lang
6817a45e7f Adds hooks on login view (#4978).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3530 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-03 20:21:05 +00:00
Jean-Philippe Lang
f2b227547e Filters collapsed by default for saved queries (#4623).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3529 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-03 20:09:39 +00:00
Eric Davis
fe1e3ccd18 Refactor: Decouple failed attachments and the flash messages
Attachment#attach_files will no longer need to return a flash message,
instead it will put unsaved attachments into object#unsaved_attachments
where the calling object can access them.

A utility method #render_attachment_warning_if_needed is included for setting
the standard flash warning.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3528 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-03 17:05:00 +00:00
Eric Davis
44955a519c Added test for when an attachment fails to save.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3527 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-03 17:04:55 +00:00
Eric Davis
f6d8752e44 Fixes destroying attachments if the update fails. (r2875 r3523)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3526 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-03 17:04:50 +00:00
Eric Davis
3c0c0a7677 Allow multiple selected projects in #project_tree_options_for_select
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3525 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-03 03:37:40 +00:00
Jean-Philippe Lang
b09a483aa9 Fixed: error while deleting an in-use enumeration (#4970).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3524 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-02 20:03:09 +00:00
Eric Davis
0fd7e2d696 Refactor: Moved ApplicationController#attach_files to the Attachment model
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3523 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-02 19:26:03 +00:00
Eric Davis
81d617cd5b Fixes failing tests from r3521
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3522 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-02 19:25:58 +00:00
Jean-Philippe Lang
b52e75aad9 Fixes some context menu links broken by r3478.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3521 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-01 20:54:55 +00:00
Eric Davis
f5f5a5f64f Verify issues are updated by HTTP PUT only. Regression from r3486.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3520 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-01 18:29:18 +00:00
Eric Davis
8699e5bbcd Refactor: Decouple building Issue and TimeEntry objects in #issue_update.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3519 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-03-01 17:40:54 +00:00
Jean-Philippe Lang
ae55978515 Adds a test for not sending email without recipient (#4920).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3518 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-28 17:18:21 +00:00
Jean-Philippe Lang
8e57b1f6cd Fixed: Trac importer creates duplicate wiki records (#4743).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3517 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-28 16:51:36 +00:00
Jean-Philippe Lang
c9197769fb Fixes bottom links placement in issue with associated changesets (#4883).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3516 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-28 16:42:31 +00:00
Jean-Philippe Lang
da84474391 Fixed: Plugin generators should display help if no parameter is given (#4609).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3514 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-28 12:15:47 +00:00
Jean-Philippe Lang
9a85d4d063 Fixed: Git blame/annotate fails on moved files (#3832).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3513 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-28 12:09:09 +00:00
Jean-Philippe Lang
d6c299f57d Adds 'Blocked by' (#1755) and 'Duplicated by' relation types to the dropdown menu for new relations.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3512 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-28 11:28:32 +00:00
Jean-Philippe Lang
241f79ac06 Fixed: attachments with the same name at the same time overwrite (#3691).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3511 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-28 11:12:40 +00:00
Jean-Philippe Lang
82310d3162 Allow project custom fields to be searchable (#4945).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3510 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-28 10:55:24 +00:00
Jean-Philippe Lang
1ab77ee015 Fixes a test failure due to a default custom field value.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3509 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-28 10:48:35 +00:00
Jean-Philippe Lang
551993d3c2 Adds missing fixtures.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3508 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-28 10:42:13 +00:00
Jean-Philippe Lang
049f47eaf0 Update for 0.9.3 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3506 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-28 10:20:40 +00:00
Jean-Philippe Lang
c174540a0e Adds attachments upload on wiki edit form (#1223).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3500 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-28 09:43:13 +00:00
Jean-Philippe Lang
02cc0efdd7 Fixed: journal details duplicated when an issue is saved twice (#3690).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3499 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-28 09:21:12 +00:00
Jean-Philippe Lang
26e9a0d919 Fixed: Wrong link when "http" not included in project "Homepage" link (#4944).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3498 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-28 09:10:33 +00:00
Eric Davis
ddec53b9a2 Refactor: Extracted saving logic out of #update and into a utility method
Now #issue_update can be refactored and push to the Model(s) while keeping
the public interface (#update) clean.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3497 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-26 16:16:18 +00:00
Jean-Philippe Lang
113049412b Link to the user from the wiki history view.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3496 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-26 16:03:05 +00:00
Jean-Philippe Lang
bda2aa474c Adds a link to the user profile from the administration user edit screen (#4480).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3495 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-26 16:01:04 +00:00
Jean-Philippe Lang
c7c43d38ce Removes a useless assignment in Trac importer (#4931).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3494 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-26 15:55:02 +00:00
Jean-Philippe Lang
bfed36ac84 Let administrators see locked user profiles.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3493 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-26 15:50:16 +00:00
Jean-Philippe Lang
d6f9e576e8 Makes AuthSource.authenticate return a hash instead of an array.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3492 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-26 09:13:12 +00:00
Jean-Philippe Lang
f1d16bc007 Test that AuthSourceLdap#authenticate returns valid User attributes only.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3491 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-26 08:50:37 +00:00
Jean-Philippe Lang
899f06612a Moves the LDAP test server to a fixture.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3490 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-26 08:42:40 +00:00
Jean-Philippe Lang
7ae53845ac Adds a sample test LDAP configuration file.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3489 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-26 08:31:36 +00:00
Jean-Philippe Lang
aa521c5a5a User model should not have to clean up LDAP attributes.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3488 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-26 08:29:20 +00:00
Eric Davis
3de1f2e157 Refactor: Extracted the duplication from the last commit into a new method
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3487 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-25 17:01:10 +00:00
Eric Davis
c68d853bf4 Refactor: Move the updating of an Issue to the #update method.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3486 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-25 17:01:05 +00:00
Eric Davis
19d4ddf2f2 Fix LDAP on the fly creation. The User object doesn't have a :dn attribute.
Fixes #4918 and adds a test for the behavior.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3485 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-25 16:19:55 +00:00
Eric Davis
39c97279ca Optimize: Only select the name when getting the module names.
This has speed up TimelogController#details by 100% with larger databases
(demo.redmine went from 4077ms down to 2085ms).

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3484 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-25 04:55:33 +00:00
Jean-Philippe Lang
f7bd0801f6 Fixed: changing user/roles of project member not possible without javascript (#4852)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3483 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-24 21:23:20 +00:00
Jean-Philippe Lang
46ecc488a9 Fixed: projects are referenced by id in search results.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3482 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-24 21:08:08 +00:00
Jean-Philippe Lang
b2dee55e72 Makes project identifiers searchable (#4897).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3481 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-24 21:05:14 +00:00
Eric Davis
d581510af6 Refactor: Start to extract IssuesController#edit into #update (REST).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3480 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-24 17:21:41 +00:00
Jean-Philippe Lang
6ed9734cb9 Reduces spacing on the bulk edit form.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3479 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-23 21:44:28 +00:00
Jean-Philippe Lang
e24d6cc223 Bulk edit refactoring.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3478 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-23 21:26:29 +00:00
Jean-Philippe Lang
01dbba72ab Move hardcoded calendar images to css classes.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3477 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-23 21:10:15 +00:00
Jean-Philippe Lang
a2537b3969 Fixed view_issues_move_bottom hook arguments (#4893).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3476 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-23 21:04:12 +00:00
Eric Davis
f838904a7e Refactor: Extract Issue#bulk_edit from the IssuesController
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3475 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-23 17:06:08 +00:00
Eric Davis
89cf2d256d Moved the IssuesController routing tests to an integration test.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3474 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-22 18:15:07 +00:00
Jean-Philippe Lang
0341ac80eb Avoid unnecessary SQL queries when loading changesets.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3473 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-21 14:43:45 +00:00
Jean-Philippe Lang
3d393a5711 Memorize commit authors to speed up changesets loading.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3472 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-21 14:42:45 +00:00
Jean-Philippe Lang
93bcc68017 Adds a missing index (speeds up changesets loading).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3471 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-21 14:40:00 +00:00
Azamat Hackimov
24ec3bb7a9 Translation updates
* sv (#4846)
* ru (#4861)
* de (#4870)



git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3470 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-21 14:38:38 +00:00
Jean-Philippe Lang
103698b371 Wraps changeset creation inside a single transation.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3469 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-21 14:38:34 +00:00
Jean-Philippe Lang
87bc092d7c Removes --find-copies-harder git option to retrieve commits which was way to slow (#4547).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3468 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-21 14:37:09 +00:00
Jean-Philippe Lang
4878d749f4 Do not query git for tags and branches multiple times per request.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3467 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-20 13:35:22 +00:00
Jean-Philippe Lang
46aa855572 Remove invalid utf8 sequences from commit comments and author name (#4773).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3466 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-20 11:24:41 +00:00
Jean-Philippe Lang
6e68921590 Cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3459 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-20 10:11:19 +00:00
Jean-Philippe Lang
dab0c07fba Fixed: Links in Forum mails should redirect to message, not topic (#4884).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3458 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-20 10:07:53 +00:00
Jean-Philippe Lang
08d7c5bfb4 Adds a controller hook before issue move (#4850).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3457 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-19 19:10:50 +00:00
Jean-Philippe Lang
77459f9ae4 Adds a hook on issue move form (#4850).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3456 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-19 19:10:21 +00:00
Jean-Philippe Lang
d46609f4bb Fixes INSTALL and UPGRADING steps order (#4848).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3455 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-19 18:52:34 +00:00
Eric Davis
c6b2f1d606 Refactor: Extract #get_user_dn from AuthSourceLdap.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3454 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-19 17:00:49 +00:00
Eric Davis
d828122009 Refactor: extract AuthSourceLdap#search_attributes
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3453 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-19 16:33:01 +00:00
Jean-Philippe Lang
026fbb99a6 Escaping in html email templates (#4874).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3452 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-18 19:13:38 +00:00
Jean-Philippe Lang
d18fba4ffe Fixed: Pound (#) followed by number with leading zero (0) removes leading zero when rendered in wiki (#4872).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3451 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-18 18:53:30 +00:00
Eric Davis
6f930e9be6 Refactor: Rewrite authenticate_dn to use an implicit return.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3450 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-18 16:55:10 +00:00
Eric Davis
82dd1b2bf2 Refactor: Moved the check for an empty DN to authenticate_dn
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3449 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-18 16:55:05 +00:00
Eric Davis
899fb1079f Added some common example email configs to email.yml.example (#3120)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3448 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-18 05:20:52 +00:00
Eric Davis
28e9bc5d82 Fix an IndexError if all the :last menu items are deleted from a menu. #4718
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3447 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-18 05:01:39 +00:00
Jean-Philippe Lang
0fcc436f22 Fixed: Deselecting textile text formatting causes interning empty string errors (#4867).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3446 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-17 20:47:50 +00:00
Jean-Philippe Lang
74e85953f0 Move SQL LIKE tokens to acts_as_searchable.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3445 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-17 20:20:51 +00:00
Jean-Philippe Lang
c11d30ebc9 Makes search providers extensible (#3936).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3444 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-17 20:05:51 +00:00
Jean-Philippe Lang
b733accfe3 Adds a test for emission address with phrase (#4855).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3443 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-17 19:08:04 +00:00
Jean-Philippe Lang
3dd9687d41 Fixed: unit tests broken by r3438 (#4860).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3442 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-17 18:08:15 +00:00
Eric Davis
4f268c5606 Refactor: extract an #authenticate_dn method in AuthSourceLdap
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3441 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-17 16:35:35 +00:00
Eric Davis
9e22faa640 Converted the REDMINE_SUPPORTED_SCM constant to a class
Now SCMs can be added or removed using a simple API, instead of being
hardcoded:

  Redmine::Scm::Base.add('ScmName')
  Redmine::Scm::Base.delete('ScmName')

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3440 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-16 22:41:59 +00:00
Eric Davis
b3330d3995 Refactor: Extract method from AuthSourceLdap#authenticate
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3439 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-16 17:03:54 +00:00
Eric Davis
7b6b147761 Added some tests for the LDAP authentication.
Includes an export of an LDAP database to use in testing.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3438 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-16 16:40:50 +00:00
Eric Davis
49bfee0535 Refactor: Merged TrackersController#list and #index
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3437 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-15 16:41:27 +00:00
Eric Davis
c5f30fde28 Refactor: Merged AuthSourcesController#list and #index
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3436 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-15 16:41:21 +00:00
Eric Davis
038a88e970 Refactor: Merged RolesController#list and #index
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3435 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-15 16:41:16 +00:00
Azamat Hackimov
cec2b20793 Translation updates
* ja (#4783)
* ru (also fixes #4771)
* sv (#4818)
* zh-TW (#4837)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3434 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-14 19:46:52 +00:00
Jean-Philippe Lang
0772391fe0 Changes 2 png images.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3430 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-14 13:13:16 +00:00
Jean-Philippe Lang
d16c2d4cd3 Adds a few divs to the base layout (#871).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3429 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-14 13:07:51 +00:00
Jean-Philippe Lang
2f8cf7f762 Removes duplicated LOC.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3428 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-14 12:45:48 +00:00
Jean-Philippe Lang
a811f67748 Fixed: SystemStackError (stack level too deep) on Issue#attributes= after model reload (#4838).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3427 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-14 12:43:48 +00:00
Jean-Philippe Lang
0ee17b132c Slight forum styles changes.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3426 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-14 11:52:12 +00:00
Jean-Philippe Lang
00cec500c6 Do not include news author in emphasis tag.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3425 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-14 11:49:29 +00:00
Jean-Philippe Lang
ea3e2b1121 Changes SubversionAdapter to use ActiveSupport::XmlMini API for XML parsing.
This allows easy switching to one of the faster XML parsers supported by XmlMini (eg. libxml-ruby).

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3424 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-13 13:58:05 +00:00
Jean-Philippe Lang
7059d0b500 Adds requirement for ruby version and tells that Rails is included in downloadable releases (#4836).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3423 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-13 13:35:39 +00:00
Jean-Philippe Lang
3168ee3d47 Adds projects links (#4812).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3422 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-13 10:32:06 +00:00
Jean-Philippe Lang
89b9700074 Removes a GIF image.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3420 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-13 09:09:57 +00:00
Jean-Philippe Lang
4fbffd7716 Minifies PNG images (#4819).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3419 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-13 09:08:12 +00:00
Eric Davis
997e8bb0dd Fixing locales, rake locales:update added extra whitespace to the line.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3418 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-12 22:08:08 +00:00
Eric Davis
4a67c211ca Fixed a nil method error in Setting#value=
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3417 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-12 22:08:02 +00:00
Eric Davis
8d086cb7d4 Removed hard coded English string.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3416 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-12 19:15:39 +00:00
Eric Davis
c18f8d34fb Refactor: Merged IssueStatusesController#list and #index.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3415 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-12 19:15:33 +00:00
Jean-Philippe Lang
6ed690eacc Fixed: div.task incorrectly wrapping on Gantt Chart (#4813).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3414 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-12 18:43:39 +00:00
Jean-Philippe Lang
55a3ac764f Issue relation: fixes error with postgres when entering a non-numeric id (#4820) + accept hash (#) before id.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3413 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-12 18:35:31 +00:00
Jean-Philippe Lang
541d830d2a Fixed: Project copy loses wiki pages hierarchy (#4797).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3412 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-11 19:30:53 +00:00
Eric Davis
d2baf5f2a7 Log failed user logins to the Rails logger
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3411 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-11 18:25:38 +00:00
Eric Davis
937d0c70f7 Refactored IssueStatus finder to a before_filter
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3410 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-11 16:42:52 +00:00
Jean-Philippe Lang
2c8a24092b Adds filter for system shared versions on the cross project issue list (#4792).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3409 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-10 21:42:32 +00:00
Jean-Philippe Lang
1fec53cc25 Fixed: roadmap show subprojects issues even if subprojects is unchecked (#4761).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3408 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-10 21:29:31 +00:00
Jean-Philippe Lang
00d7bc3766 Makes use of format_version_name.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3407 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-10 21:21:53 +00:00
Jean-Philippe Lang
0b17e735ea Fixed: Duplicated project name for subproject version on gantt chart (#4775).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3406 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-10 21:13:34 +00:00
Jean-Philippe Lang
d02ddefff4 Fixed: parent project field doesn't include blank value when a member with 'add subproject' permission edits a child project (#4790).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3405 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-10 20:38:23 +00:00
Eric Davis
a8a49a26a1 Refactor: inline the utility methods in ReportsController.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3404 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-10 16:59:58 +00:00
Eric Davis
ed9eb2d684 Switch to using the shoulda gem from gemcutter. #4573
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3403 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-10 00:56:14 +00:00
Jean-Philippe Lang
d04d3f181a Fixed: Repository.fetch_changesets tries to fetch changesets for archived projects (#4782).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3402 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-09 18:42:42 +00:00
Eric Davis
f50feca730 Extracted the rendering from each case statement in issue_reports_details
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3401 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-09 16:47:27 +00:00
Eric Davis
57a4cefd97 Fixed the link to the Issue Details report, broken in r3396
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3400 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-09 16:47:22 +00:00
Azamat Hackimov
349a5d3b55 Translation updates
* de (#4760)
* pt-BR (#4762)
* ru


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3399 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-08 20:59:50 +00:00
Eric Davis
81ee2ff884 Updated object_daddy to a newer version (bugfixes)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3398 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-08 18:53:12 +00:00
Eric Davis
9482831313 Removed the duplicated configuration in the different test environments.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3397 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-08 18:53:07 +00:00
Eric Davis
597725d77c Separated ReportsController#issue_report into two separate actions.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3396 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-08 17:53:58 +00:00
Eric Davis
23c46c68ab Moved a routing test from a functional to an integration test.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3395 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-08 17:53:52 +00:00
Jean-Philippe Lang
3c20a9b0ac Do not parse the entire git log to fetch new commits (takes several minutes for a few thousands commits), but only 1 week before the last known commit (#4547, #4716).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3394 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-07 15:17:21 +00:00
Jean-Philippe Lang
be2004e398 Update for 0.9.2 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3392 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-07 12:47:53 +00:00
Jean-Philippe Lang
446ef61680 Korean support for PDF export (#4639).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3389 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-07 12:30:44 +00:00
Jean-Philippe Lang
f5dfe8b2f5 Translation updates.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3388 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-07 12:23:37 +00:00
Jean-Philippe Lang
eac7fcff6e Fixed: error raised when trying to view the gantt or calendar with a grouped query (#4751).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3379 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-06 18:07:25 +00:00
Jean-Philippe Lang
fece6aa4d0 Fixed: deprecation warning in reposman.rb (#4736).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3378 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-06 14:20:39 +00:00
Jean-Philippe Lang
88339fd98b Do not show 'Quote' links on locked topics (#4749).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3377 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-06 14:15:52 +00:00
Jean-Philippe Lang
5a5d02dfb5 Removes overflow:auto on #content which broke the fix in r2118 (#4724).
Autoscroll divs are added around large tables.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3376 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-06 14:05:13 +00:00
Jean-Philippe Lang
695bffe684 Fixed: error messages displayed twice when creating a group (#4715).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3375 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-06 13:26:29 +00:00
Jean-Philippe Lang
34ee5eccda Renames WikiFormatting.cache to .cache_store
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3374 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-06 13:13:40 +00:00
Jean-Philippe Lang
8fb29d4d21 Adds pagination to forum messages (#4664).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3373 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-06 12:54:13 +00:00
Jean-Philippe Lang
2ad8242ae7 Adds a setting to cache textile rendering (off by default).
* it uses ActionController cache store which is MemoryStore by default and can be configured with config.action_controller.cache_store
* macro processing was moved out of textile rendering so that it doesn't get cached
* no noticeable improvement is expected for small portions of text, so only texts larger than 2KB are cached

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3372 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-06 10:40:21 +00:00
Jean-Philippe Lang
6c8b87fbc8 Adds a permission for exporting wiki pages.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3371 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-06 09:30:53 +00:00
Eric Davis
e5d300af0a Refactor: Pull up several #find_project methods to ApplicationController
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3370 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-05 16:57:02 +00:00
Azamat Hackimov
d7725ecc06 Translations updates
* ja (#4738)
* ru (#4739)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3369 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-04 21:55:15 +00:00
Jean-Philippe Lang
626b71622d Fixed: invalid format parameter returns a DoubleRenderError on issues index (#4737).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3368 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-04 19:58:42 +00:00
Jean-Philippe Lang
ea51dcf911 Reverts r3366.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3367 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-04 19:57:45 +00:00
Jean-Philippe Lang
cfe4d0e7db Fixed: invalid format parameter returns a DoubleRenderError on issues index (#4737).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3366 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-04 19:52:51 +00:00
Eric Davis
5bd912e9a2 Refactor: Extracted the select_all calls to a new private method.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3365 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-04 17:24:33 +00:00
Eric Davis
112fc99311 Added tests for Issue#by_X finders
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3364 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-04 17:24:28 +00:00
Eric Davis
155083ec97 Change Role#anonymous and #non_member so they generate the record as needed.
While creating tests, it was a common occurrence to lost the builtin roles
because they are only created in the migrations.  This makes them behave like
User#anonymous.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3363 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-03 17:47:47 +00:00
Eric Davis
b86b9b898e Refactor: Moved the raw SQL finders from ReportsController to Issue.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3362 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-03 16:49:21 +00:00
Eric Davis
778117fead Converted a test to shoulda and added more checks for it's assignments.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3361 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-03 16:49:16 +00:00
Eric Davis
39c585740d Refactor: Extract method to create a Change from a Changeset.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3360 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-02 17:02:32 +00:00
Eric Davis
358e3194d7 Refactor: Move recipients method into acts_as_event
acts_as_event should be the standard interface to Redmine events, like mail
notifications, so having a standard recipients implementation there makes sense.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3358 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-02-01 18:57:12 +00:00
Jean-Philippe Lang
d43c860448 Allow commits to reference issues of parent projects and subprojects (#4674).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3357 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-31 16:25:06 +00:00
Jean-Philippe Lang
df46d704e8 Log email delivery errors.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3356 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-31 11:41:58 +00:00
Jean-Philippe Lang
a0364deb6a Log info when sending an email notification.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3355 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-31 11:00:40 +00:00
Jean-Philippe Lang
a1a24483b5 Fixed: deleting a project with subprojects breaks the project tree (#4701).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3354 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-31 10:39:42 +00:00
Azamat Hackimov
a5698b3745 Translation updates
* pt-BR (#4692)
* sv (#4666)
* zh-TW (#4654)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3353 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-31 00:54:27 +00:00
Jean-Philippe Lang
7ef20cc169 Fixed: potential security leak on my page calendar (#4691).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3351 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-30 11:23:17 +00:00
Jean-Philippe Lang
2261ec7b95 Updates for 0.9.1 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3348 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-30 10:53:35 +00:00
Jean-Philippe Lang
080ad6a16c Fixed: tests with ruby1.9 broken by r3322 (#4607).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3346 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-30 10:36:20 +00:00
Jean-Philippe Lang
12ea682b66 Fixed: NOT NULL constraint error when adding a group (#4632).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3343 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-30 10:24:23 +00:00
Eric Davis
8420f25109 Added and updated some more object daddy exemplars.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3342 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-28 19:05:21 +00:00
Eric Davis
675f28f895 Use the minimum password length setting when reseting a password. (#4683)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3341 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-28 18:54:51 +00:00
Eric Davis
9044305b93 Added some exemplars for object daddy to generate test data.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3340 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-27 18:29:03 +00:00
Azamat Hackimov
d1c90d9abf New strings to translate (#4647):
* label_board_sticky: Sticky
* label_board_locked: Locked


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3339 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-25 17:26:02 +00:00
Azamat Hackimov
56268d146d New language - Basque/ Euskara (#4650).
Thank Ales Zabala Alava for translation.


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3338 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-25 15:25:46 +00:00
Eric Davis
a8f56aad2c Added User.spawn_with_protected to generate an unsaved User record.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3337 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-23 16:50:41 +00:00
Eric Davis
d4525828b9 Fixed some labels to be more accessible.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3336 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-21 18:21:56 +00:00
Jean-Philippe Lang
1bc00ac022 Accept filters from API calls without set_filter parameter.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3335 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-18 19:53:06 +00:00
Jean-Philippe Lang
320c191f04 Fixed: PDF export of a issue list grouped by a custom field raises an error (#4600).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3333 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-18 18:00:27 +00:00
Jean-Philippe Lang
0ef9bc039d Adds a log message when an API call raises an InvalidAuthenticityToken error.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3332 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-17 20:23:06 +00:00
Jean-Philippe Lang
002f440bcd Move ids in XML views.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3331 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-17 19:45:18 +00:00
Jean-Philippe Lang
3eb815fddd Adds a rake task to receive emails from a POP3 server (#2420).
See:

  rake -D redmine:email:receive_pop3

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3330 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-17 13:53:13 +00:00
Jean-Philippe Lang
b4cf8ea525 Adds a few STDOUT.flush to migration scripts (#3675).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3328 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-17 12:00:45 +00:00
Jean-Philippe Lang
d55e1ec5b0 Adds unit test for #3645.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3327 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-17 11:48:46 +00:00
Jean-Philippe Lang
ed2c98fcae Fixed: Tab's 'border-bottom' not absent when selected (#4495).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3325 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-17 11:15:58 +00:00
Jean-Philippe Lang
f8b52b13a0 Fixed: Issue Summary tables that list by user are not sorted (#4552).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3323 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-17 11:07:53 +00:00
Jean-Philippe Lang
bddf3b5c2c Adds a test with Japanese keywords in a ISO-2002-JP encoded email (#4576).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3322 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-17 11:00:54 +00:00
Jean-Philippe Lang
9c7438aac6 Set native eol svn property on new lang files (#4526).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3320 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-17 10:33:30 +00:00
Eric Davis
1827d609a1 Cleaned up the IssueController redirects to use the back_url like the other actions.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3315 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-14 23:24:55 +00:00
Jean-Philippe Lang
dfe447f841 Fixes project editing route broken by r3313.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3314 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-14 22:19:53 +00:00
Jean-Philippe Lang
68a4cd38f5 XML REST API for Projects (#296).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3313 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-14 20:00:17 +00:00
Azamat Hackimov
64f4b50139 New Croatian language in Redmine! Thanks to Igor P. for translation (#4526)
Also, updates for:
* es.yml (#4578)
* zh.yml (#4545)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3312 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-14 16:10:56 +00:00
Jean-Philippe Lang
da55be4e9a Fixed: error when exporting an issue with target version to PDF (#4556).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3311 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-13 20:17:56 +00:00
Jean-Philippe Lang
3873388f9b XML REST API for issues that provides CRUD operations for Issues (#1214).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3310 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-13 19:29:19 +00:00
Eric Davis
667a7256a7 Escape the parent id parameter.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3309 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-13 17:27:37 +00:00
Jean-Philippe Lang
0a05cc2a37 Set a white list of issue attributes that can be mass-assigned from controllers.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3308 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-12 20:17:20 +00:00
Jean-Philippe Lang
ff77fb6aa9 Fixed: email notifications may affect language of notices on the UI (#4086).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3307 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-10 14:54:02 +00:00
Jean-Philippe Lang
fe7d65922b Fixed: can not search for 2 letters word (#4381).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3306 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-10 14:15:12 +00:00
Jean-Philippe Lang
d7c9886b8c Fixed: attachments get saved on issue update even if validation fails (#4401).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3305 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-10 14:05:42 +00:00
Jean-Philippe Lang
ee64310df4 CHANGELOG updated.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3302 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-10 10:20:46 +00:00
Jean-Philippe Lang
7b99adb3d6 CHANGELOG updated.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3301 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-10 10:19:41 +00:00
Jean-Philippe Lang
1b65a44cfc Fixes Redmine.pm error "closing dbh with active statement handles at /usr/lib/perl5/Apache/Redmine.pm" (#4205).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3300 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-10 10:17:27 +00:00
Jean-Philippe Lang
23508f4f98 Copyright updated (#4542).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3299 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-09 15:01:39 +00:00
Jean-Philippe Lang
a5ceb2f244 Set alignment for inline images in formatted text.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3298 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-09 14:57:33 +00:00
Jean-Philippe Lang
90c0e99d2a CHANGELOG update.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3295 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-09 11:49:45 +00:00
Jean-Philippe Lang
486f705494 Updated doc.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3293 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-09 11:46:53 +00:00
Jean-Philippe Lang
602aa1d7ae Translation updates.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3291 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-09 10:36:32 +00:00
Jean-Philippe Lang
f6ac12ad9d Fixed: undefined method `stringify_keys!' error in Issue#attributes_with_tracker_first= with ruby1.9 (#4540).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3289 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-09 10:27:05 +00:00
Jean-Philippe Lang
75ac722ebc Translation update.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3288 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-09 10:06:18 +00:00
Jean-Philippe Lang
1e30e67043 Adds new Rails strings.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3285 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-07 21:47:51 +00:00
Jean-Philippe Lang
99956ec248 Avatars added in news (#3941) and forums (#4468) + cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3284 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-07 21:28:45 +00:00
Jean-Philippe Lang
518779d96d Fixed: issue summary counts should link to the issue list without subprojects (#4525).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3283 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-07 21:12:16 +00:00
Jean-Philippe Lang
eecc6864a9 Fixed: 'Delete' link on LDAP list has no effect (#4530).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3282 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-07 20:56:41 +00:00
Jean-Philippe Lang
eb3b7d055a Fixed: error when downloading a file with no matching mime type (#3782).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3281 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-05 18:16:03 +00:00
Jean-Philippe Lang
f05b778065 Bulk edit view cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3279 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-03 11:37:04 +00:00
Jean-Philippe Lang
cab99aa5ad Allow bulk edit custom fields of any type (#461).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3278 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-03 11:18:09 +00:00
Jean-Philippe Lang
e9810b5de6 Fixed: bulk edit doesn't include global custom fields (#4505).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3277 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-03 10:50:40 +00:00
Jean-Philippe Lang
4ac99328ff Translation update.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3275 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-03 10:28:22 +00:00
Jean-Philippe Lang
c71cb84a91 Translation update.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3274 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-03 10:26:39 +00:00
Azamat Hackimov
f69a4107f8 Fixed untranslated string (:label_close_versions => Close completed versions)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3273 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-01 16:49:43 +00:00
Azamat Hackimov
357a4eb7b9 de.yml update (#4426)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3272 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-01 16:42:29 +00:00
Azamat Hackimov
88587b5916 Translation updates
* ru
* zh (#4481)
* zh-TW (#4484)


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3271 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-01-01 16:38:58 +00:00
Azamat Hackimov
4702a05ebb Japanese translation update (Patch #4508)
(HNY! :))


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3270 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-12-31 13:19:13 +00:00
Eric Davis
9ffee0a5a4 Added some shoulda macros for testing.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3269 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-12-30 19:42:19 +00:00
Eric Davis
4cf2e7c216 Added a second rake task to generate the session store file.
`rake generate_session_store`

A lot of people think `rake config/initializers/session_store.rb` needs to
have an existing session_store.rb file in order to generate it.  This task
will make it easier to explain and document.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3267 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-12-30 00:46:48 +00:00
Eric Davis
4b3d4553e4 Create and assign the api token so the current instance can access it. (#4497)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3265 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-12-30 00:25:53 +00:00
Eric Davis
c67cc7c285 Added :controller_issues_new_before_save hook. (#4465)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3263 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-12-29 23:31:24 +00:00
Eric Davis
857c7d16ff Fixed the caption on the Files module. (#4406)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3261 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-12-29 17:56:59 +00:00
Jean-Philippe Lang
ac5cfe7a62 Set status of existing versions to 'open' (#4504).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3259 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-12-29 14:53:29 +00:00
Jean-Philippe Lang
ff7eb7b23a Auto-detect attachment content type when blank (#3782).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3258 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-12-29 13:28:30 +00:00
Eric Davis
b7b4e27833 Added :view_settings_general_form plugin hook
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3256 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-12-29 00:43:06 +00:00
Jean-Philippe Lang
44a079fcd7 Enlarge wiki content for MySQL databases (#1071).
Limit changed from 64KB to 16MB.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3254 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-12-27 11:57:13 +00:00
Jean-Philippe Lang
b718d5dec2 Moves attachments parsing after textile parsing so that:
* attachments parsing does not rely on textile syntax
* textile output can be cached (#4482)

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3253 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-12-27 10:52:02 +00:00
Jean-Philippe Lang
a359ffb397 Fixed: Calendar appears on wrong place in IE8 (#3818).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3252 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-12-26 17:17:16 +00:00
Jean-Philippe Lang
b1a92b9b2e Fixed: Subversion password visible in development logs (#4448).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3251 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-12-26 16:20:22 +00:00
Jean-Philippe Lang
04455783a7 Fixed: project copy doesn't copy group memberships (#3975).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3250 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-12-26 16:14:55 +00:00
Jean-Philippe Lang
c36c924714 Ask for confirmation when a non-admin users tries to remove himself from a project (#4402).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3249 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-12-26 15:46:12 +00:00
Jean-Philippe Lang
a4c64ff84b Fixed: subject of incoming emails gets mangled (#3717).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3247 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-12-26 12:15:12 +00:00
Jean-Philippe Lang
de2bef606d Fixes Redmine.pm for SQLite (#4205).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3246 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-12-26 12:02:24 +00:00
902 changed files with 49242 additions and 13557 deletions

3
.gitignore vendored
View File

@@ -1,3 +1,5 @@
/.project
/.loadpath
/config/additional_environment.rb
/config/database.yml
/config/email.yml
@@ -17,3 +19,4 @@
/tmp/sockets/*
/tmp/test/*
/vendor/rails
*.rbc

24
.hgignore Normal file
View File

@@ -0,0 +1,24 @@
syntax: glob
.project
.loadpath
config/additional_environment.rb
config/database.yml
config/email.yml
config/initializers/session_store.rb
coverage
db/*.db
db/*.sqlite3
db/schema.rb
files/*
log/*.log*
log/mongrel_debug
public/dispatch.*
public/plugin_assets
tmp/*
tmp/cache/*
tmp/sessions/*
tmp/sockets/*
tmp/test/*
vendor/rails
*.rbc

View File

@@ -2,4 +2,4 @@
Redmine is a flexible project management web application written using Ruby on Rails framework.
More details can be found at http://www.redmine.org
More details can be found at in the doc directory or on the official website http://www.redmine.org

View File

@@ -25,23 +25,15 @@ class AccountController < ApplicationController
# Login request and validation
def login
if request.get?
# Logout user
self.logged_user = nil
logout_user
else
# Authenticate user
if Setting.openid? && using_open_id?
open_id_authenticate(params[:openid_url])
else
password_authentication
end
authenticate_user
end
end
# Log out current user and redirect to welcome page
def logout
cookies.delete :autologin
Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin']) if User.current.logged?
self.logged_user = nil
logout_user
redirect_to home_url
end
@@ -91,9 +83,9 @@ class AccountController < ApplicationController
else
@user = User.new(params[:user])
@user.admin = false
@user.status = User::STATUS_REGISTERED
@user.register
if session[:auth_source_registration]
@user.status = User::STATUS_ACTIVE
@user.activate
@user.login = session[:auth_source_registration][:login]
@user.auth_source_id = session[:auth_source_registration][:auth_source_id]
if @user.save
@@ -124,8 +116,8 @@ class AccountController < ApplicationController
token = Token.find_by_action_and_value('register', params[:token])
redirect_to(home_url) && return unless token and !token.expired?
user = token.user
redirect_to(home_url) && return unless user.status == User::STATUS_REGISTERED
user.status = User::STATUS_ACTIVE
redirect_to(home_url) && return unless user.registered?
user.activate
if user.save
token.destroy
flash[:notice] = l(:notice_account_activated)
@@ -134,6 +126,22 @@ class AccountController < ApplicationController
end
private
def logout_user
if User.current.logged?
cookies.delete :autologin
Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin'])
self.logged_user = nil
end
end
def authenticate_user
if Setting.openid? && using_open_id?
open_id_authenticate(params[:openid_url])
else
password_authentication
end
end
def password_authentication
user = User.try_to_login(params[:username], params[:password])
@@ -162,7 +170,7 @@ class AccountController < ApplicationController
user.mail = registration['email'] unless registration['email'].nil?
user.firstname, user.lastname = registration['fullname'].split(' ') unless registration['fullname'].nil?
user.random_password
user.status = User::STATUS_REGISTERED
user.register
case Setting.self_registration
when '1'
@@ -210,6 +218,7 @@ class AccountController < ApplicationController
end
def invalid_credentials
logger.warn "Failed login for '#{params[:username]}' from #{request.remote_ip} at #{Time.now.utc}"
flash.now[:error] = l(:notice_account_invalid_creditentials)
end
@@ -232,7 +241,7 @@ class AccountController < ApplicationController
# Pass a block for behavior when a user fails to save
def register_automatically(user, &block)
# Automatic activation
user.status = User::STATUS_ACTIVE
user.activate
user.last_login_on = Time.now
if user.save
self.logged_user = user

View File

@@ -0,0 +1,59 @@
class ActivitiesController < ApplicationController
menu_item :activity
before_filter :find_optional_project
accept_key_auth :index
def index
@days = Setting.activity_days_default.to_i
if params[:from]
begin; @date_to = params[:from].to_date + 1; rescue; end
end
@date_to ||= Date.today + 1
@date_from = @date_to - @days
@with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
@author = (params[:user_id].blank? ? nil : User.active.find(params[:user_id]))
@activity = Redmine::Activity::Fetcher.new(User.current, :project => @project,
:with_subprojects => @with_subprojects,
:author => @author)
@activity.scope_select {|t| !params["show_#{t}"].nil?}
@activity.scope = (@author.nil? ? :default : :all) if @activity.scope.empty?
events = @activity.events(@date_from, @date_to)
if events.empty? || stale?(:etag => [@activity.scope, @date_to, @date_from, @with_subprojects, @author, events.first, User.current, current_language])
respond_to do |format|
format.html {
@events_by_day = events.group_by(&:event_date)
render :layout => false if request.xhr?
}
format.atom {
title = l(:label_activity)
if @author
title = @author.name
elsif @activity.scope.size == 1
title = l("label_#{@activity.scope.first.singularize}_plural")
end
render_feed(events, :title => "#{@project || Setting.app_title}: #{title}")
}
end
end
rescue ActiveRecord::RecordNotFound
render_404
end
private
# TODO: refactor, duplicated in projects_controller
def find_optional_project
return true unless params[:id]
@project = Project.find(params[:id])
authorize
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

@@ -22,6 +22,7 @@ class ApplicationController < ActionController::Base
include Redmine::I18n
layout 'base'
exempt_from_layout 'builder', 'rsb'
# Remove broken cookie after upgrade from 0.8.x (#4292)
# See https://rails.lighthouseapp.com/projects/8994/tickets/3360
@@ -45,7 +46,7 @@ class ApplicationController < ActionController::Base
include Redmine::MenuManager::MenuController
helper Redmine::MenuManager::MenuHelper
REDMINE_SUPPORTED_SCM.each do |scm|
Redmine::Scm::Base.all.each do |scm|
require_dependency "repository/#{scm.underscore}"
end
@@ -70,10 +71,10 @@ class ApplicationController < ActionController::Base
elsif params[:format] == 'atom' && params[:key] && accept_key_auth_actions.include?(params[:action])
# RSS key authentication does not start a session
User.find_by_rss_key(params[:key])
elsif Setting.rest_api_enabled? && ['xml', 'json'].include?(params[:format]) && accept_key_auth_actions.include?(params[:action])
if params[:key].present?
elsif Setting.rest_api_enabled? && api_request?
if (key = api_key_from_request) && accept_key_auth_actions.include?(params[:action])
# Use API key
User.find_by_api_key(params[:key])
User.find_by_api_key(key)
else
# HTTP Basic, either username/password or API key/random
authenticate_with_http_basic do |username, password|
@@ -107,8 +108,9 @@ class ApplicationController < ActionController::Base
lang = find_language(User.current.language)
end
if lang.nil? && request.env['HTTP_ACCEPT_LANGUAGE']
accept_lang = parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first.downcase
accept_lang = parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first
if !accept_lang.blank?
accept_lang = accept_lang.downcase
lang = find_language(accept_lang) || find_language(accept_lang.split('-').first)
end
end
@@ -127,8 +129,9 @@ class ApplicationController < ActionController::Base
respond_to do |format|
format.html { redirect_to :controller => "account", :action => "login", :back_url => url }
format.atom { redirect_to :controller => "account", :action => "login", :back_url => url }
format.xml { head :unauthorized }
format.json { head :unauthorized }
format.xml { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
format.js { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
format.json { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
end
return false
end
@@ -150,14 +153,88 @@ class ApplicationController < ActionController::Base
# Authorize the user for the requested action
def authorize(ctrl = params[:controller], action = params[:action], global = false)
allowed = User.current.allowed_to?({:controller => ctrl, :action => action}, @project, :global => global)
allowed ? true : deny_access
allowed = User.current.allowed_to?({:controller => ctrl, :action => action}, @project || @projects, :global => global)
if allowed
true
else
if @project && @project.archived?
render_403 :message => :notice_not_authorized_archived_project
else
deny_access
end
end
end
# Authorize the user for the requested action outside a project
def authorize_global(ctrl = params[:controller], action = params[:action], global = true)
authorize(ctrl, action, global)
end
# Find project of id params[:id]
def find_project
@project = Project.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
# Find project of id params[:project_id]
def find_project_by_project_id
@project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound
render_404
end
# Find a project based on params[:project_id]
# TODO: some subclasses override this, see about merging their logic
def find_optional_project
@project = Project.find(params[:project_id]) unless params[:project_id].blank?
allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true)
allowed ? true : deny_access
rescue ActiveRecord::RecordNotFound
render_404
end
# Finds and sets @project based on @object.project
def find_project_from_association
render_404 unless @object.present?
@project = @object.project
rescue ActiveRecord::RecordNotFound
render_404
end
def find_model_object
model = self.class.read_inheritable_attribute('model_object')
if model
@object = model.find(params[:id])
self.instance_variable_set('@' + controller_name.singularize, @object) if @object
end
rescue ActiveRecord::RecordNotFound
render_404
end
def self.model_object(model)
write_inheritable_attribute('model_object', model)
end
# Filter for bulk issue operations
def find_issues
@issues = Issue.find_all_by_id(params[:id] || params[:ids])
raise ActiveRecord::RecordNotFound if @issues.empty?
@projects = @issues.collect(&:project).compact.uniq
@project = @projects.first if @projects.size == 1
rescue ActiveRecord::RecordNotFound
render_404
end
# Check if project is unique before bulk operations
def check_project_uniqueness
unless @project
# TODO: let users bulk edit/move/destroy issues from different projects
render_error 'Can not bulk edit/move/destroy issues from different projects'
return false
end
end
# make sure that the user is a member of the project (or admin) if project is private
# used as a before_filter for actions that do not require any particular permission on the project
@@ -175,6 +252,10 @@ class ApplicationController < ActionController::Base
end
end
def back_url
params[:back_url] || request.env['HTTP_REFERER']
end
def redirect_back_or_default(default)
back_url = CGI.unescape(params[:back_url].to_s)
if !back_url.blank?
@@ -192,23 +273,47 @@ class ApplicationController < ActionController::Base
redirect_to default
end
def render_403
def render_403(options={})
@project = nil
render :template => "common/403", :layout => (request.xhr? ? false : 'base'), :status => 403
render_error({:message => :notice_not_authorized, :status => 403}.merge(options))
return false
end
def render_404
render :template => "common/404", :layout => !request.xhr?, :status => 404
def render_404(options={})
render_error({:message => :notice_file_not_found, :status => 404}.merge(options))
return false
end
def render_error(msg)
flash.now[:error] = msg
render :text => '', :layout => !request.xhr?, :status => 500
# Renders an error response
def render_error(arg)
arg = {:message => arg} unless arg.is_a?(Hash)
@message = arg[:message]
@message = l(@message) if @message.is_a?(Symbol)
@status = arg[:status] || 500
respond_to do |format|
format.html {
render :template => 'common/error', :layout => use_layout, :status => @status
}
format.atom { head @status }
format.xml { head @status }
format.js { head @status }
format.json { head @status }
end
end
# Picks which layout to use based on the request
#
# @return [boolean, string] name of the layout to use or false for no layout
def use_layout
request.xhr? ? false : 'base'
end
def invalid_authenticity_token
if api_request?
logger.error "Form authenticity token is missing or is invalid. API calls must include a proper Content-type header (text/xml or text/json)."
end
render_error "Invalid form authenticity token."
end
@@ -229,27 +334,6 @@ class ApplicationController < ActionController::Base
self.class.read_inheritable_attribute('accept_key_auth_actions') || []
end
# TODO: move to model
def attach_files(obj, attachments)
attached = []
unsaved = []
if attachments && attachments.is_a?(Hash)
attachments.each_value do |attachment|
file = attachment['file']
next unless file && file.size > 0
a = Attachment.create(:container => obj,
:file => file,
:description => attachment['description'].to_s.strip,
:author => User.current)
a.new_record? ? (unsaved << a) : (attached << a)
end
if unsaved.any?
flash[:warning] = l(:warning_attachments_not_saved, unsaved.size)
end
end
attached
end
# Returns the number of objects that should be displayed
# on the paginated list
def per_page_option
@@ -265,6 +349,30 @@ class ApplicationController < ActionController::Base
per_page
end
# Returns offset and limit used to retrieve objects
# for an API response based on offset, limit and page parameters
def api_offset_and_limit(options=params)
if options[:offset].present?
offset = options[:offset].to_i
if offset < 0
offset = 0
end
end
limit = options[:limit].to_i
if limit < 1
limit = 25
elsif limit > 100
limit = 100
end
if offset.nil? && options[:page].present?
offset = (options[:page].to_i - 1) * limit
offset = 0 if offset < 0
end
offset ||= 0
[offset, limit]
end
# qvalues http header parser
# code taken from webrick
def parse_qvalues(value)
@@ -290,4 +398,85 @@ class ApplicationController < ActionController::Base
def filename_for_content_disposition(name)
request.env['HTTP_USER_AGENT'] =~ %r{MSIE} ? ERB::Util.url_encode(name) : name
end
def api_request?
%w(xml json).include? params[:format]
end
# Returns the API key present in the request
def api_key_from_request
if params[:key].present?
params[:key]
elsif request.headers["X-Redmine-API-Key"].present?
request.headers["X-Redmine-API-Key"]
end
end
# Renders a warning flash if obj has unsaved attachments
def render_attachment_warning_if_needed(obj)
flash[:warning] = l(:warning_attachments_not_saved, obj.unsaved_attachments.size) if obj.unsaved_attachments.present?
end
# Sets the `flash` notice or error based the number of issues that did not save
#
# @param [Array, Issue] issues all of the saved and unsaved Issues
# @param [Array, Integer] unsaved_issue_ids the issue ids that were not saved
def set_flash_from_bulk_issue_save(issues, unsaved_issue_ids)
if unsaved_issue_ids.empty?
flash[:notice] = l(:notice_successful_update) unless issues.empty?
else
flash[:error] = l(:notice_failed_to_save_issues,
:count => unsaved_issue_ids.size,
:total => issues.size,
:ids => '#' + unsaved_issue_ids.join(', #'))
end
end
# Rescues an invalid query statement. Just in case...
def query_statement_invalid(exception)
logger.error "Query::StatementInvalid: #{exception.message}" if logger
session.delete(:query)
sort_clear if respond_to?(:sort_clear)
render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
end
# Converts the errors on an ActiveRecord object into a common JSON format
def object_errors_to_json(object)
object.errors.collect do |attribute, error|
{ attribute => error }
end.to_json
end
# Renders API response on validation failure
def render_validation_errors(object)
options = { :status => :unprocessable_entity, :layout => false }
options.merge!(case params[:format]
when 'xml'; { :xml => object.errors }
when 'json'; { :json => {'errors' => object.errors} } # ActiveResource client compliance
else
raise "Unknown format #{params[:format]} in #render_validation_errors"
end
)
render options
end
# Overrides #default_template so that the api template
# is used automatically if it exists
def default_template(action_name = self.action_name)
if api_request?
begin
return self.view_paths.find_template(default_template_name(action_name), 'api')
rescue ::ActionView::MissingTemplate
# the api template was not found
# fallback to the default behaviour
end
end
super
end
# Overrides #pick_layout so that #render with no arguments
# doesn't use the layout for api requests
def pick_layout(*args)
api_request? ? nil : super
end
end

View File

@@ -41,7 +41,7 @@ class AttachmentsController < ApplicationController
# images are sent inline
send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
:type => @attachment.content_type,
:type => detect_content_type(@attachment),
:disposition => (@attachment.image? ? 'inline' : 'attachment')
end
@@ -76,4 +76,12 @@ private
def delete_authorize
@attachment.deletable? ? true : deny_access
end
def detect_content_type(attachment)
content_type = attachment.content_type
if content_type.blank?
content_type = Redmine::MimeType.of(attachment.filename)
end
content_type.to_s
end
end

View File

@@ -20,45 +20,42 @@ class AuthSourcesController < ApplicationController
before_filter :require_admin
def index
list
render :action => 'list' unless request.xhr?
end
# GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
verify :method => :post, :only => [ :destroy, :create, :update ],
:redirect_to => { :action => :list }
:redirect_to => { :template => :index }
def list
@auth_source_pages, @auth_sources = paginate :auth_sources, :per_page => 10
render :action => "list", :layout => false if request.xhr?
def index
@auth_source_pages, @auth_sources = paginate auth_source_class.name.tableize, :per_page => 10
render "auth_sources/index"
end
def new
@auth_source = AuthSourceLdap.new
@auth_source = auth_source_class.new
render 'auth_sources/new'
end
def create
@auth_source = AuthSourceLdap.new(params[:auth_source])
@auth_source = auth_source_class.new(params[:auth_source])
if @auth_source.save
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'list'
redirect_to :action => 'index'
else
render :action => 'new'
render 'auth_sources/new'
end
end
def edit
@auth_source = AuthSource.find(params[:id])
render 'auth_sources/edit'
end
def update
@auth_source = AuthSource.find(params[:id])
if @auth_source.update_attributes(params[:auth_source])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'list'
redirect_to :action => 'index'
else
render :action => 'edit'
render 'auth_sources/edit'
end
end
@@ -68,9 +65,9 @@ class AuthSourcesController < ApplicationController
@auth_method.test_connection
flash[:notice] = l(:notice_successful_connection)
rescue => text
flash[:error] = "Unable to connect (#{text})"
flash[:error] = l(:error_unable_to_connect, text.message)
end
redirect_to :action => 'list'
redirect_to :action => 'index'
end
def destroy
@@ -79,6 +76,12 @@ class AuthSourcesController < ApplicationController
@auth_source.destroy
flash[:notice] = l(:notice_successful_delete)
end
redirect_to :action => 'list'
redirect_to :action => 'index'
end
protected
def auth_source_class
AuthSource
end
end

View File

@@ -0,0 +1,27 @@
class AutoCompletesController < ApplicationController
before_filter :find_project
def issues
@issues = []
q = params[:q].to_s
query = (params[:scope] == "all" && Setting.cross_project_issue_relations?) ? Issue : @project.issues
if q.match(/^\d+$/)
@issues << query.visible.find_by_id(q.to_i)
end
unless q.blank?
@issues += query.visible.find(:all, :conditions => ["LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%"], :limit => 10)
end
@issues.compact!
render :layout => false
end
private
def find_project
project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id]
@project = Project.find(project_id)
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

@@ -17,7 +17,8 @@
class BoardsController < ApplicationController
default_search_scope :messages
before_filter :find_project, :authorize
before_filter :find_project, :find_board_if_available, :authorize
accept_key_auth :index, :show
helper :messages
include MessagesHelper
@@ -68,24 +69,33 @@ class BoardsController < ApplicationController
@board.project = @project
if request.post? && @board.save
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'boards'
redirect_to_settings_in_projects
end
end
def edit
if request.post? && @board.update_attributes(params[:board])
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'boards'
redirect_to_settings_in_projects
end
end
def destroy
@board.destroy
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'boards'
redirect_to_settings_in_projects
end
private
def redirect_to_settings_in_projects
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'boards'
end
def find_project
@project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound
render_404
end
def find_board_if_available
@board = @project.boards.find(params[:id]) if params[:id]
rescue ActiveRecord::RecordNotFound
render_404

View File

@@ -0,0 +1,44 @@
class CalendarsController < ApplicationController
menu_item :calendar
before_filter :find_optional_project
rescue_from Query::StatementInvalid, :with => :query_statement_invalid
helper :issues
helper :projects
helper :queries
include QueriesHelper
helper :sort
include SortHelper
def show
if params[:year] and params[:year].to_i > 1900
@year = params[:year].to_i
if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
@month = params[:month].to_i
end
end
@year ||= Date.today.year
@month ||= Date.today.month
@calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
retrieve_query
@query.group_by = nil
if @query.valid?
events = []
events += @query.issues(:include => [:tracker, :assigned_to, :priority],
:conditions => ["((start_date BETWEEN ? AND ?) OR (due_date BETWEEN ? AND ?))", @calendar.startdt, @calendar.enddt, @calendar.startdt, @calendar.enddt]
)
events += @query.versions(:conditions => ["effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
@calendar.events = events
end
render :action => 'show', :layout => false if request.xhr?
end
def update
show
end
end

View File

@@ -0,0 +1,36 @@
class CommentsController < ApplicationController
default_search_scope :news
model_object News
before_filter :find_model_object
before_filter :find_project_from_association
before_filter :authorize
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
def create
@comment = Comment.new(params[:comment])
@comment.author = User.current
if @news.comments << @comment
flash[:notice] = l(:label_comment_added)
end
redirect_to :controller => 'news', :action => 'show', :id => @news
end
verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
def destroy
@news.comments.find(params[:comment_id]).destroy
redirect_to :controller => 'news', :action => 'show', :id => @news
end
private
# ApplicationController's find_model_object sets it based on the controller
# name so it needs to be overriden and set to @news instead
def find_model_object
super
@news = @object
@comment = nil
@news
end
end

View File

@@ -0,0 +1,44 @@
class ContextMenusController < ApplicationController
helper :watchers
def issues
@issues = Issue.visible.all(:conditions => {:id => params[:ids]}, :include => :project)
if (@issues.size == 1)
@issue = @issues.first
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
else
@allowed_statuses = @issues.map do |i|
i.new_statuses_allowed_to(User.current)
end.inject do |memo,s|
memo & s
end
end
@projects = @issues.collect(&:project).compact.uniq
@project = @projects.first if @projects.size == 1
@can = {:edit => User.current.allowed_to?(:edit_issues, @projects),
:log_time => (@project && User.current.allowed_to?(:log_time, @project)),
:update => (User.current.allowed_to?(:edit_issues, @projects) || (User.current.allowed_to?(:change_status, @projects) && !@allowed_statuses.blank?)),
:move => (@project && User.current.allowed_to?(:move_issues, @project)),
:copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
:delete => User.current.allowed_to?(:delete_issues, @projects)
}
if @project
@assignables = @project.assignable_users
@assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
@trackers = @project.trackers
else
#when multiple projects, we only keep the intersection of each set
@assignables = @projects.map(&:assignable_users).inject{|memo,a| memo & a}
@trackers = @projects.map(&:trackers).inject{|memo,t| memo & t}
end
@priorities = IssuePriority.all.reverse
@statuses = IssueStatus.find(:all, :order => 'position')
@back = back_url
render :layout => false
end
end

View File

@@ -56,7 +56,7 @@ class CustomFieldsController < ApplicationController
@custom_field = CustomField.find(params[:id]).destroy
redirect_to :action => 'index', :tab => @custom_field.class.name
rescue
flash[:error] = "Unable to delete custom field"
flash[:error] = l(:error_can_not_delete_custom_field)
redirect_to :action => 'index'
end
end

View File

@@ -17,8 +17,10 @@
class DocumentsController < ApplicationController
default_search_scope :documents
model_object Document
before_filter :find_project, :only => [:index, :new]
before_filter :find_document, :except => [:index, :new]
before_filter :find_model_object, :except => [:index, :new]
before_filter :find_project_from_association, :except => [:index, :new]
before_filter :authorize
helper :attachments
@@ -47,7 +49,8 @@ class DocumentsController < ApplicationController
def new
@document = @project.documents.build(params[:document])
if request.post? and @document.save
attach_files(@document, params[:attachments])
attachments = Attachment.attach_files(@document, params[:attachments])
render_attachment_warning_if_needed(@document)
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'index', :project_id => @project
end
@@ -67,8 +70,10 @@ class DocumentsController < ApplicationController
end
def add_attachment
attachments = attach_files(@document, params[:attachments])
Mailer.deliver_attachments_added(attachments) if !attachments.empty? && Setting.notified_events.include?('document_added')
attachments = Attachment.attach_files(@document, params[:attachments])
render_attachment_warning_if_needed(@document)
Mailer.deliver_attachments_added(attachments[:files]) if attachments.present? && attachments[:files].present? && Setting.notified_events.include?('document_added')
redirect_to :action => 'show', :id => @document
end
@@ -78,11 +83,4 @@ private
rescue ActiveRecord::RecordNotFound
render_404
end
def find_document
@document = Document.find(params[:id])
@project = @document.project
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

@@ -76,12 +76,12 @@ class EnumerationsController < ApplicationController
@enumeration.destroy
redirect_to :action => 'index'
elsif params[:reassign_to_id]
if reassign_to = Enumeration.find_by_type_and_id(@enumeration.type, params[:reassign_to_id])
if reassign_to = @enumeration.class.find_by_id(params[:reassign_to_id])
@enumeration.destroy(reassign_to)
redirect_to :action => 'index'
end
end
@enumerations = Enumeration.find(:all, :conditions => ['type = (?)', @enumeration.type]) - [@enumeration]
@enumerations = @enumeration.class.find(:all) - [@enumeration]
#rescue
# flash[:error] = 'Unable to delete enumeration'
# redirect_to :action => 'index'

View File

@@ -0,0 +1,36 @@
class FilesController < ApplicationController
menu_item :files
before_filter :find_project_by_project_id
before_filter :authorize
helper :sort
include SortHelper
def index
sort_init 'filename', 'asc'
sort_update 'filename' => "#{Attachment.table_name}.filename",
'created_on' => "#{Attachment.table_name}.created_on",
'size' => "#{Attachment.table_name}.filesize",
'downloads' => "#{Attachment.table_name}.downloads"
@containers = [ Project.find(@project.id, :include => :attachments, :order => sort_clause)]
@containers += @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse
render :layout => !request.xhr?
end
def new
@versions = @project.versions.sort
end
def create
container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id]))
attachments = Attachment.attach_files(container, params[:attachments])
render_attachment_warning_if_needed(container)
if !attachments.empty? && !attachments[:files].blank? && Setting.notified_events.include?('file_added')
Mailer.deliver_attachments_added(attachments[:files])
end
redirect_to project_files_path(@project)
end
end

View File

@@ -0,0 +1,36 @@
class GanttsController < ApplicationController
menu_item :gantt
before_filter :find_optional_project
rescue_from Query::StatementInvalid, :with => :query_statement_invalid
helper :gantt
helper :issues
helper :projects
helper :queries
include QueriesHelper
helper :sort
include SortHelper
include Redmine::Export::PDF
def show
@gantt = Redmine::Helpers::Gantt.new(params)
@gantt.project = @project
retrieve_query
@query.group_by = nil
@gantt.query = @query if @query.valid?
basename = (@project ? "#{@project.identifier}-" : '') + 'gantt'
respond_to do |format|
format.html { render :action => "show", :layout => !request.xhr? }
format.png { send_data(@gantt.to_image, :disposition => 'inline', :type => 'image/png', :filename => "#{basename}.png") } if @gantt.respond_to?('to_image')
format.pdf { send_data(@gantt.to_pdf, :type => 'application/pdf', :filename => "#{basename}.pdf") }
end
end
def update
show
end
end

View File

@@ -57,7 +57,7 @@ class GroupsController < ApplicationController
# GET /groups/1/edit
def edit
@group = Group.find(params[:id])
@group = Group.find(params[:id], :include => :projects)
end
# POST /groups
@@ -138,18 +138,25 @@ class GroupsController < ApplicationController
def edit_membership
@group = Group.find(params[:id])
@membership = params[:membership_id] ? Member.find(params[:membership_id]) : Member.new(:principal => @group)
@membership.attributes = params[:membership]
@membership = Member.edit_membership(params[:membership_id], params[:membership], @group)
@membership.save if request.post?
respond_to do |format|
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
format.js {
render(:update) {|page|
page.replace_html "tab-content-memberships", :partial => 'groups/memberships'
page.visual_effect(:highlight, "member-#{@membership.id}")
}
}
end
if @membership.valid?
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
format.js {
render(:update) {|page|
page.replace_html "tab-content-memberships", :partial => 'groups/memberships'
page.visual_effect(:highlight, "member-#{@membership.id}")
}
}
else
format.js {
render(:update) {|page|
page.alert(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', ')))
}
}
end
end
end
def destroy_membership

View File

@@ -17,10 +17,41 @@
class IssueCategoriesController < ApplicationController
menu_item :settings
before_filter :find_project, :authorize
model_object IssueCategory
before_filter :find_model_object, :except => :new
before_filter :find_project_from_association, :except => :new
before_filter :find_project, :only => :new
before_filter :authorize
verify :method => :post, :only => :destroy
def new
@category = @project.issue_categories.build(params[:category])
if request.post?
if @category.save
respond_to do |format|
format.html do
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project
end
format.js do
# IE doesn't support the replace_html rjs method for select box options
render(:update) {|page| page.replace "issue_category_id",
content_tag('select', '<option></option>' + options_from_collection_for_select(@project.issue_categories, 'id', 'name', @category.id), :id => 'issue_category_id', :name => 'issue[category_id]')
}
end
end
else
respond_to do |format|
format.html
format.js do
render(:update) {|page| page.alert(@category.errors.full_messages.join('\n')) }
end
end
end
end
end
def edit
if request.post? and @category.update_attributes(params[:category])
flash[:notice] = l(:notice_successful_update)
@@ -43,10 +74,16 @@ class IssueCategoriesController < ApplicationController
end
private
# Wrap ApplicationController's find_model_object method to set
# @category instead of just @issue_category
def find_model_object
super
@category = @object
end
def find_project
@category = IssueCategory.find(params[:id])
@project = @category.project
@project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound
render_404
end
end
end

View File

@@ -0,0 +1,68 @@
class IssueMovesController < ApplicationController
default_search_scope :issues
before_filter :find_issues, :check_project_uniqueness
before_filter :authorize
def new
prepare_for_issue_move
render :layout => false if request.xhr?
end
def create
prepare_for_issue_move
if request.post?
new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id])
unsaved_issue_ids = []
moved_issues = []
@issues.each do |issue|
issue.reload
issue.init_journal(User.current)
issue.current_journal.notes = @notes if @notes.present?
call_hook(:controller_issues_move_before_save, { :params => params, :issue => issue, :target_project => @target_project, :copy => !!@copy })
if r = issue.move_to_project(@target_project, new_tracker, {:copy => @copy, :attributes => extract_changed_attributes_for_move(params)})
moved_issues << r
else
unsaved_issue_ids << issue.id
end
end
set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
if params[:follow]
if @issues.size == 1 && moved_issues.size == 1
redirect_to :controller => 'issues', :action => 'show', :id => moved_issues.first
else
redirect_to :controller => 'issues', :action => 'index', :project_id => (@target_project || @project)
end
else
redirect_to :controller => 'issues', :action => 'index', :project_id => @project
end
return
end
end
private
def prepare_for_issue_move
@issues.sort!
@copy = params[:copy_options] && params[:copy_options][:copy]
@allowed_projects = Issue.allowed_target_projects_on_move
@target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id]
@target_project ||= @project
@trackers = @target_project.trackers
@available_statuses = Workflow.available_statuses(@project)
@notes = params[:notes]
@notes ||= ''
end
def extract_changed_attributes_for_move(params)
changed_attributes = {}
[:assigned_to_id, :status_id, :start_date, :due_date, :priority_id].each do |valid_attribute|
unless params[valid_attribute].blank?
changed_attributes[valid_attribute] = (params[valid_attribute] == 'none' ? nil : params[valid_attribute])
end
end
changed_attributes
end
end

View File

@@ -16,18 +16,19 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class IssueRelationsController < ApplicationController
before_filter :find_project, :authorize
before_filter :find_issue, :find_project_from_association, :authorize
def new
@relation = IssueRelation.new(params[:relation])
@relation.issue_from = @issue
if params[:relation] && !params[:relation][:issue_to_id].blank?
@relation.issue_to = Issue.visible.find_by_id(params[:relation][:issue_to_id])
if params[:relation] && m = params[:relation][:issue_to_id].to_s.match(/^#?(\d+)$/)
@relation.issue_to = Issue.visible.find_by_id(m[1].to_i)
end
@relation.save if request.post?
respond_to do |format|
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @issue }
format.js do
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
render :update do |page|
page.replace_html "relations", :partial => 'issues/relations'
if @relation.errors.empty?
@@ -47,14 +48,16 @@ class IssueRelationsController < ApplicationController
end
respond_to do |format|
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @issue }
format.js { render(:update) {|page| page.replace_html "relations", :partial => 'issues/relations'} }
format.js {
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
render(:update) {|page| page.replace_html "relations", :partial => 'issues/relations'}
}
end
end
private
def find_project
@issue = Issue.find(params[:issue_id])
@project = @issue.project
def find_issue
@issue = @object = Issue.find(params[:issue_id])
rescue ActiveRecord::RecordNotFound
render_404
end

View File

@@ -21,16 +21,11 @@ class IssueStatusesController < ApplicationController
before_filter :require_admin
verify :method => :post, :only => [ :destroy, :create, :update, :move, :update_issue_done_ratio ],
:redirect_to => { :action => :list }
:redirect_to => { :action => :index }
def index
list
render :action => 'list' unless request.xhr?
end
def list
@issue_status_pages, @issue_statuses = paginate :issue_statuses, :per_page => 25, :order => "position"
render :action => "list", :layout => false if request.xhr?
render :action => "index", :layout => false if request.xhr?
end
def new
@@ -41,7 +36,7 @@ class IssueStatusesController < ApplicationController
@issue_status = IssueStatus.new(params[:issue_status])
if @issue_status.save
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'list'
redirect_to :action => 'index'
else
render :action => 'new'
end
@@ -55,7 +50,7 @@ class IssueStatusesController < ApplicationController
@issue_status = IssueStatus.find(params[:id])
if @issue_status.update_attributes(params[:issue_status])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'list'
redirect_to :action => 'index'
else
render :action => 'edit'
end
@@ -63,10 +58,10 @@ class IssueStatusesController < ApplicationController
def destroy
IssueStatus.find(params[:id]).destroy
redirect_to :action => 'list'
redirect_to :action => 'index'
rescue
flash[:error] = "Unable to delete issue status"
redirect_to :action => 'list'
flash[:error] = l(:error_unable_delete_issue_status)
redirect_to :action => 'index'
end
def update_issue_done_ratio
@@ -75,6 +70,6 @@ class IssueStatusesController < ApplicationController
else
flash[:error] = l(:error_issue_done_ratios_not_updated)
end
redirect_to :action => 'list'
redirect_to :action => 'index'
end
end

View File

@@ -16,15 +16,18 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class IssuesController < ApplicationController
menu_item :new_issue, :only => :new
menu_item :new_issue, :only => [:new, :create]
default_search_scope :issues
before_filter :find_issue, :only => [:show, :edit, :reply]
before_filter :find_issues, :only => [:bulk_edit, :move, :destroy]
before_filter :find_project, :only => [:new, :update_form, :preview]
before_filter :authorize, :except => [:index, :changes, :gantt, :calendar, :preview, :context_menu]
before_filter :find_optional_project, :only => [:index, :changes, :gantt, :calendar]
accept_key_auth :index, :show, :changes
before_filter :find_issue, :only => [:show, :edit, :update]
before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :move, :perform_move, :destroy]
before_filter :check_project_uniqueness, :only => [:move, :perform_move]
before_filter :find_project, :only => [:new, :create]
before_filter :authorize, :except => [:index]
before_filter :find_optional_project, :only => [:index]
before_filter :check_for_default_issue_status, :only => [:new, :create]
before_filter :build_new_issue_from_params, :only => [:new, :create]
accept_key_auth :index, :show, :create, :update, :destroy
rescue_from Query::StatementInvalid, :with => :query_statement_invalid
@@ -40,40 +43,53 @@ class IssuesController < ApplicationController
helper :attachments
include AttachmentsHelper
helper :queries
include QueriesHelper
helper :repositories
include RepositoriesHelper
helper :sort
include SortHelper
include IssuesHelper
helper :timelog
helper :gantt
include Redmine::Export::PDF
verify :method => :post,
verify :method => [:post, :delete],
:only => :destroy,
:render => { :nothing => true, :status => :method_not_allowed }
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
verify :method => :post, :only => :bulk_update, :render => {:nothing => true, :status => :method_not_allowed }
verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
def index
retrieve_query
sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.available_columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
sort_update(@query.sortable_columns)
if @query.valid?
limit = per_page_option
respond_to do |format|
format.html { }
format.atom { limit = Setting.feeds_limit.to_i }
format.csv { limit = Setting.issues_export_limit.to_i }
format.pdf { limit = Setting.issues_export_limit.to_i }
case params[:format]
when 'csv', 'pdf'
@limit = Setting.issues_export_limit.to_i
when 'atom'
@limit = Setting.feeds_limit.to_i
when 'xml', 'json'
@offset, @limit = api_offset_and_limit
else
@limit = per_page_option
end
@issue_count = @query.issue_count
@issue_pages = Paginator.new self, @issue_count, limit, params['page']
@issue_pages = Paginator.new self, @issue_count, @limit, params['page']
@offset ||= @issue_pages.current.offset
@issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
:order => sort_clause,
:offset => @issue_pages.current.offset,
:limit => limit)
:offset => @offset,
:limit => @limit)
@issue_count_by_group = @query.issue_count_by_group
respond_to do |format|
format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
format.api
format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
format.csv { send_data(issues_to_csv(@issues, @project), :type => 'text/csv; header=present', :filename => 'export.csv') }
format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
@@ -86,34 +102,21 @@ class IssuesController < ApplicationController
render_404
end
def changes
retrieve_query
sort_init 'id', 'desc'
sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.available_columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
if @query.valid?
@journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC",
:limit => 25)
end
@title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
render :layout => false, :content_type => 'application/atom+xml'
rescue ActiveRecord::RecordNotFound
render_404
end
def show
@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.reverse! if User.current.wants_comments_in_reverse_order?
@changesets = @issue.changesets
@changesets = @issue.changesets.visible.all
@changesets.reverse! if User.current.wants_comments_in_reverse_order?
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
@edit_allowed = User.current.allowed_to?(:edit_issues, @project)
@priorities = IssuePriority.all
@time_entry = TimeEntry.new
respond_to do |format|
format.html { render :template => 'issues/show.rhtml' }
format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' }
format.api
format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
end
end
@@ -121,211 +124,95 @@ class IssuesController < ApplicationController
# Add a new issue
# The new issue will be created from an existing one if copy_from parameter is given
def new
@issue = Issue.new
@issue.copy_from(params[:copy_from]) if params[:copy_from]
@issue.project = @project
# Tracker must be set before custom field values
@issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
if @issue.tracker.nil?
render_error l(:error_no_tracker_in_project)
return
respond_to do |format|
format.html { render :action => 'new', :layout => !request.xhr? }
format.js { render :partial => 'attributes' }
end
if params[:issue].is_a?(Hash)
@issue.attributes = params[:issue]
@issue.watcher_user_ids = params[:issue]['watcher_user_ids'] if User.current.allowed_to?(:add_issue_watchers, @project)
end
@issue.author = User.current
default_status = IssueStatus.default
unless default_status
render_error l(:error_no_default_issue_status)
return
end
@issue.status = default_status
@allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(User.current.roles_for_project(@project), @issue.tracker)).uniq
if request.get? || request.xhr?
@issue.start_date ||= Date.today
else
requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
# Check that the user is allowed to apply the requested status
@issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
if @issue.save
attach_files(@issue, params[:attachments])
flash[:notice] = l(:notice_successful_create)
call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
redirect_to(params[:continue] ? { :action => 'new', :tracker_id => @issue.tracker } :
{ :action => 'show', :id => @issue })
return
end
end
@priorities = IssuePriority.all
render :layout => !request.xhr?
end
# Attributes that can be updated on workflow transition (without :edit permission)
# TODO: make it configurable (at least per role)
UPDATABLE_ATTRS_ON_TRANSITION = %w(status_id assigned_to_id fixed_version_id done_ratio) unless const_defined?(:UPDATABLE_ATTRS_ON_TRANSITION)
def edit
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
@priorities = IssuePriority.all
@edit_allowed = User.current.allowed_to?(:edit_issues, @project)
@time_entry = TimeEntry.new
@notes = params[:notes]
journal = @issue.init_journal(User.current, @notes)
# User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue]
attrs = params[:issue].dup
attrs.delete_if {|k,v| !UPDATABLE_ATTRS_ON_TRANSITION.include?(k) } unless @edit_allowed
attrs.delete(:status_id) unless @allowed_statuses.detect {|s| s.id.to_s == attrs[:status_id].to_s}
@issue.attributes = attrs
end
if request.post?
@time_entry = TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
@time_entry.attributes = params[:time_entry]
attachments = attach_files(@issue, params[:attachments])
attachments.each {|a| journal.details << JournalDetail.new(:property => 'attachment', :prop_key => a.id, :value => a.filename)}
call_hook(:controller_issues_edit_before_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => journal})
if (@time_entry.hours.nil? || @time_entry.valid?) && @issue.save
# Log spend time
if User.current.allowed_to?(:log_time, @project)
@time_entry.save
end
if !journal.new_record?
# Only send notification if something was actually changed
flash[:notice] = l(:notice_successful_update)
end
call_hook(:controller_issues_edit_after_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => journal})
redirect_to(params[:back_to] || {:action => 'show', :id => @issue})
def create
call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
if @issue.save
attachments = Attachment.attach_files(@issue, params[:attachments])
render_attachment_warning_if_needed(@issue)
flash[:notice] = l(:notice_successful_create)
call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
respond_to do |format|
format.html {
redirect_to(params[:continue] ? { :action => 'new', :project_id => @project, :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
{ :action => 'show', :id => @issue })
}
format.api { render :action => 'show', :status => :created, :location => issue_url(@issue) }
end
return
else
respond_to do |format|
format.html { render :action => 'new' }
format.api { render_validation_errors(@issue) }
end
end
rescue ActiveRecord::StaleObjectError
# Optimistic locking exception
flash.now[:error] = l(:notice_locking_conflict)
# Remove the previously added attachments if issue was not updated
attachments.each(&:destroy)
end
def edit
update_issue_from_params
@journal = @issue.current_journal
respond_to do |format|
format.html { }
format.xml { }
end
end
def reply
journal = Journal.find(params[:journal_id]) if params[:journal_id]
if journal
user = journal.user
text = journal.notes
def update
update_issue_from_params
if @issue.save_issue_with_child_records(params, @time_entry)
render_attachment_warning_if_needed(@issue)
flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
respond_to do |format|
format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
format.api { head :ok }
end
else
user = @issue.author
text = @issue.description
render_attachment_warning_if_needed(@issue)
flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
@journal = @issue.current_journal
respond_to do |format|
format.html { render :action => 'edit' }
format.api { render_validation_errors(@issue) }
end
end
content = "#{ll(Setting.default_language, :text_user_wrote, user)}\\n> "
content << text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]').gsub('"', '\"').gsub(/(\r?\n|\r\n?)/, "\\n> ") + "\\n\\n"
render(:update) { |page|
page.<< "$('notes').value = \"#{content}\";"
page.show 'update'
page << "Form.Element.focus('notes');"
page << "Element.scrollTo('update');"
page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;"
}
end
# Bulk edit a set of issues
def bulk_edit
if request.post?
tracker = params[:tracker_id].blank? ? nil : @project.trackers.find_by_id(params[:tracker_id])
status = params[:status_id].blank? ? nil : IssueStatus.find_by_id(params[:status_id])
priority = params[:priority_id].blank? ? nil : IssuePriority.find_by_id(params[:priority_id])
assigned_to = (params[:assigned_to_id].blank? || params[:assigned_to_id] == 'none') ? nil : User.find_by_id(params[:assigned_to_id])
category = (params[:category_id].blank? || params[:category_id] == 'none') ? nil : @project.issue_categories.find_by_id(params[:category_id])
fixed_version = (params[:fixed_version_id].blank? || params[:fixed_version_id] == 'none') ? nil : @project.shared_versions.find_by_id(params[:fixed_version_id])
custom_field_values = params[:custom_field_values] ? params[:custom_field_values].reject {|k,v| v.blank?} : nil
unsaved_issue_ids = []
@issues.each do |issue|
journal = issue.init_journal(User.current, params[:notes])
issue.tracker = tracker if tracker
issue.priority = priority if priority
issue.assigned_to = assigned_to if assigned_to || params[:assigned_to_id] == 'none'
issue.category = category if category || params[:category_id] == 'none'
issue.fixed_version = fixed_version if fixed_version || params[:fixed_version_id] == 'none'
issue.start_date = params[:start_date] unless params[:start_date].blank?
issue.due_date = params[:due_date] unless params[:due_date].blank?
issue.done_ratio = params[:done_ratio] unless params[:done_ratio].blank?
issue.custom_field_values = custom_field_values if custom_field_values && !custom_field_values.empty?
call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
# Don't save any change to the issue if the user is not authorized to apply the requested status
unless (status.nil? || (issue.new_statuses_allowed_to(User.current).include?(status) && issue.status = status)) && issue.save
# Keep unsaved issue ids to display them in flash error
unsaved_issue_ids << issue.id
end
end
if unsaved_issue_ids.empty?
flash[:notice] = l(:notice_successful_update) unless @issues.empty?
else
flash[:error] = l(:notice_failed_to_save_issues, :count => unsaved_issue_ids.size,
:total => @issues.size,
:ids => '#' + unsaved_issue_ids.join(', #'))
end
redirect_to(params[:back_to] || {:controller => 'issues', :action => 'index', :project_id => @project})
return
end
@available_statuses = Workflow.available_statuses(@project)
@custom_fields = @project.issue_custom_fields.select {|f| f.field_format == 'list'}
@issues.sort!
@available_statuses = @projects.map{|p|Workflow.available_statuses(p)}.inject{|memo,w|memo & w}
@custom_fields = @projects.map{|p|p.all_issue_custom_fields}.inject{|memo,c|memo & c}
@assignables = @projects.map(&:assignable_users).inject{|memo,a| memo & a}
@trackers = @projects.map(&:trackers).inject{|memo,t| memo & t}
end
def move
@copy = params[:copy_options] && params[:copy_options][:copy]
@allowed_projects = []
# find projects to which the user is allowed to move the issue
if User.current.admin?
# admin is allowed to move issues to any active (visible) project
@allowed_projects = Project.find(:all, :conditions => Project.visible_by(User.current))
else
User.current.memberships.each {|m| @allowed_projects << m.project if m.roles.detect {|r| r.allowed_to?(:move_issues)}}
def bulk_update
@issues.sort!
attributes = parse_params_for_bulk_issue_attributes(params)
unsaved_issue_ids = []
@issues.each do |issue|
issue.reload
journal = issue.init_journal(User.current, params[:notes])
issue.safe_attributes = attributes
call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
unless issue.save
# Keep unsaved issue ids to display them in flash error
unsaved_issue_ids << issue.id
end
end
@target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id]
@target_project ||= @project
@trackers = @target_project.trackers
@available_statuses = Workflow.available_statuses(@project)
if request.post?
new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id])
unsaved_issue_ids = []
moved_issues = []
@issues.each do |issue|
changed_attributes = {}
[:assigned_to_id, :status_id, :start_date, :due_date].each do |valid_attribute|
unless params[valid_attribute].blank?
changed_attributes[valid_attribute] = (params[valid_attribute] == 'none' ? nil : params[valid_attribute])
end
end
issue.init_journal(User.current)
if r = issue.move_to(@target_project, new_tracker, {:copy => @copy, :attributes => changed_attributes})
moved_issues << r
else
unsaved_issue_ids << issue.id
end
end
if unsaved_issue_ids.empty?
flash[:notice] = l(:notice_successful_update) unless @issues.empty?
else
flash[:error] = l(:notice_failed_to_save_issues, :count => unsaved_issue_ids.size,
:total => @issues.size,
:ids => '#' + unsaved_issue_ids.join(', #'))
end
if params[:follow]
if @issues.size == 1 && moved_issues.size == 1
redirect_to :controller => 'issues', :action => 'show', :id => moved_issues.first
else
redirect_to :controller => 'issues', :action => 'index', :project_id => (@target_project || @project)
end
else
redirect_to :controller => 'issues', :action => 'index', :project_id => @project
end
return
end
render :layout => false if request.xhr?
set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project})
end
def destroy
@@ -345,119 +232,17 @@ class IssuesController < ApplicationController
TimeEntry.update_all("issue_id = #{reassign_to.id}", ['issue_id IN (?)', @issues])
end
else
# display the destroy form
return
# display the destroy form if it's a user request
return unless api_request?
end
end
@issues.each(&:destroy)
redirect_to :action => 'index', :project_id => @project
end
def gantt
@gantt = Redmine::Helpers::Gantt.new(params)
retrieve_query
if @query.valid?
events = []
# Issues that have start and due dates
events += @query.issues(:include => [:tracker, :assigned_to, :priority],
:order => "start_date, due_date",
:conditions => ["(((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?) or (start_date<? and due_date>?)) and start_date is not null and due_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
)
# Issues that don't have a due date but that are assigned to a version with a date
events += @query.issues(:include => [:tracker, :assigned_to, :priority, :fixed_version],
:order => "start_date, effective_date",
:conditions => ["(((start_date>=? and start_date<=?) or (effective_date>=? and effective_date<=?) or (start_date<? and effective_date>?)) and start_date is not null and due_date is null and effective_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
)
# Versions
events += @query.versions(:conditions => ["effective_date BETWEEN ? AND ?", @gantt.date_from, @gantt.date_to])
@gantt.events = events
end
basename = (@project ? "#{@project.identifier}-" : '') + 'gantt'
respond_to do |format|
format.html { render :template => "issues/gantt.rhtml", :layout => !request.xhr? }
format.png { send_data(@gantt.to_image, :disposition => 'inline', :type => 'image/png', :filename => "#{basename}.png") } if @gantt.respond_to?('to_image')
format.pdf { send_data(gantt_to_pdf(@gantt, @project), :type => 'application/pdf', :filename => "#{basename}.pdf") }
format.html { redirect_back_or_default(:action => 'index', :project_id => @project) }
format.api { head :ok }
end
end
def calendar
if params[:year] and params[:year].to_i > 1900
@year = params[:year].to_i
if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
@month = params[:month].to_i
end
end
@year ||= Date.today.year
@month ||= Date.today.month
@calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
retrieve_query
if @query.valid?
events = []
events += @query.issues(:include => [:tracker, :assigned_to, :priority],
:conditions => ["((start_date BETWEEN ? AND ?) OR (due_date BETWEEN ? AND ?))", @calendar.startdt, @calendar.enddt, @calendar.startdt, @calendar.enddt]
)
events += @query.versions(:conditions => ["effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
@calendar.events = events
end
render :layout => false if request.xhr?
end
def context_menu
@issues = Issue.find_all_by_id(params[:ids], :include => :project)
if (@issues.size == 1)
@issue = @issues.first
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
end
projects = @issues.collect(&:project).compact.uniq
@project = projects.first if projects.size == 1
@can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)),
:log_time => (@project && User.current.allowed_to?(:log_time, @project)),
:update => (@project && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && @allowed_statuses && !@allowed_statuses.empty?))),
:move => (@project && User.current.allowed_to?(:move_issues, @project)),
:copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
:delete => (@project && User.current.allowed_to?(:delete_issues, @project))
}
if @project
@assignables = @project.assignable_users
@assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
@trackers = @project.trackers
end
@priorities = IssuePriority.all.reverse
@statuses = IssueStatus.find(:all, :order => 'position')
@back = params[:back_url] || request.env['HTTP_REFERER']
render :layout => false
end
def update_form
if params[:id].blank?
@issue = Issue.new
@issue.project = @project
else
@issue = @project.issues.visible.find(params[:id])
end
@issue.attributes = params[:issue]
@allowed_statuses = ([@issue.status] + @issue.status.find_new_statuses_allowed_to(User.current.roles_for_project(@project), @issue.tracker)).uniq
@priorities = IssuePriority.all
render :partial => 'attributes'
end
def preview
@issue = @project.issues.find_by_id(params[:id]) unless params[:id].blank?
@attachements = @issue.attachments if @issue
@text = params[:notes] || (params[:issue] ? params[:issue][:description] : nil)
render :partial => 'common/preview'
end
private
def find_issue
@issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
@@ -466,75 +251,69 @@ private
render_404
end
# Filter for bulk operations
def find_issues
@issues = Issue.find_all_by_id(params[:id] || params[:ids])
raise ActiveRecord::RecordNotFound if @issues.empty?
projects = @issues.collect(&:project).compact.uniq
if projects.size == 1
@project = projects.first
def find_project
project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id]
@project = Project.find(project_id)
rescue ActiveRecord::RecordNotFound
render_404
end
# Used by #edit and #update to set some common instance variables
# from the params
# TODO: Refactor, not everything in here is needed by #edit
def update_issue_from_params
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
@priorities = IssuePriority.all
@edit_allowed = User.current.allowed_to?(:edit_issues, @project)
@time_entry = TimeEntry.new
@time_entry.attributes = params[:time_entry]
@notes = params[:notes] || (params[:issue].present? ? params[:issue][:notes] : nil)
@issue.init_journal(User.current, @notes)
@issue.safe_attributes = params[:issue]
end
# TODO: Refactor, lots of extra code in here
# TODO: Changing tracker on an existing issue should not trigger this
def build_new_issue_from_params
if params[:id].blank?
@issue = Issue.new
@issue.copy_from(params[:copy_from]) if params[:copy_from]
@issue.project = @project
else
# TODO: let users bulk edit/move/destroy issues from different projects
render_error 'Can not bulk edit/move/destroy issues from different projects'
@issue = @project.issues.visible.find(params[:id])
end
@issue.project = @project
# Tracker must be set before custom field values
@issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
if @issue.tracker.nil?
render_error l(:error_no_tracker_in_project)
return false
end
rescue ActiveRecord::RecordNotFound
render_404
end
def find_project
@project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound
render_404
end
def find_optional_project
@project = Project.find(params[:project_id]) unless params[:project_id].blank?
allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true)
allowed ? true : deny_access
rescue ActiveRecord::RecordNotFound
render_404
end
# Retrieve query from session or build a new query
def retrieve_query
if !params[:query_id].blank?
cond = "project_id IS NULL"
cond << " OR project_id = #{@project.id}" if @project
@query = Query.find(params[:query_id], :conditions => cond)
@query.project = @project
session[:query] = {:id => @query.id, :project_id => @query.project_id}
sort_clear
else
if params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
# Give it a name, required to be valid
@query = Query.new(:name => "_")
@query.project = @project
if params[:fields] and params[:fields].is_a? Array
params[:fields].each do |field|
@query.add_filter(field, params[:operators][field], params[:values][field])
end
else
@query.available_filters.keys.each do |field|
@query.add_short_filter(field, params[field]) if params[field]
end
end
@query.group_by = params[:group_by]
@query.column_names = params[:query] && params[:query][:column_names]
session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
else
@query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
@query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
@query.project = @project
@issue.start_date ||= Date.today
if params[:issue].is_a?(Hash)
@issue.safe_attributes = params[:issue]
if User.current.allowed_to?(:add_issue_watchers, @project) && @issue.new_record?
@issue.watcher_user_ids = params[:issue]['watcher_user_ids']
end
end
@issue.author = User.current
@priorities = IssuePriority.all
@allowed_statuses = @issue.new_statuses_allowed_to(User.current, true)
end
# Rescues an invalid query statement. Just in case...
def query_statement_invalid(exception)
logger.error "Query::StatementInvalid: #{exception.message}" if logger
session.delete(:query)
sort_clear
render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
def check_for_default_issue_status
if IssueStatus.default.nil?
render_error l(:error_no_default_issue_status)
return false
end
end
def parse_params_for_bulk_issue_attributes(params)
attributes = (params[:issue] || {}).reject {|k,v| v.blank?}
attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
attributes
end
end

View File

@@ -16,7 +16,56 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class JournalsController < ApplicationController
before_filter :find_journal
before_filter :find_journal, :only => [:edit]
before_filter :find_issue, :only => [:new]
before_filter :find_optional_project, :only => [:index]
before_filter :authorize, :only => [:new, :edit]
accept_key_auth :index
helper :issues
helper :custom_fields
helper :queries
include QueriesHelper
helper :sort
include SortHelper
def index
retrieve_query
sort_init 'id', 'desc'
sort_update(@query.sortable_columns)
if @query.valid?
@journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC",
:limit => 25)
end
@title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
render :layout => false, :content_type => 'application/atom+xml'
rescue ActiveRecord::RecordNotFound
render_404
end
def new
journal = Journal.find(params[:journal_id]) if params[:journal_id]
if journal
user = journal.user
text = journal.notes
else
user = @issue.author
text = @issue.description
end
# Replaces pre blocks with [...]
text = text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]')
content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> "
content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
render(:update) { |page|
page.<< "$('notes').value = \"#{escape_javascript content}\";"
page.show 'update'
page << "Form.Element.focus('notes');"
page << "Element.scrollTo('update');"
page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;"
}
end
def edit
if request.post?
@@ -27,6 +76,14 @@ class JournalsController < ApplicationController
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @journal.journalized_id }
format.js { render :action => 'update' }
end
else
respond_to do |format|
format.html {
# TODO: implement non-JS journal update
render :nothing => true
}
format.js
end
end
end
@@ -38,4 +95,12 @@ private
rescue ActiveRecord::RecordNotFound
render_404
end
# TODO: duplicated in IssuesController
def find_issue
@issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
@project = @issue.project
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

@@ -0,0 +1,25 @@
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class LdapAuthSourcesController < AuthSourcesController
protected
def auth_source_class
AuthSourceLdap
end
end

View File

@@ -16,7 +16,9 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class MembersController < ApplicationController
before_filter :find_member, :except => [:new, :autocomplete_for_member]
model_object Member
before_filter :find_model_object, :except => [:new, :autocomplete_for_member]
before_filter :find_project_from_association, :except => [:new, :autocomplete_for_member]
before_filter :find_project, :only => [:new, :autocomplete_for_member]
before_filter :authorize
@@ -34,13 +36,30 @@ class MembersController < ApplicationController
@project.members << members
end
respond_to do |format|
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
format.js {
render(:update) {|page|
page.replace_html "tab-content-members", :partial => 'projects/settings/members'
members.each {|member| page.visual_effect(:highlight, "member-#{member.id}") }
if members.present? && members.all? {|m| m.valid? }
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
format.js {
render(:update) {|page|
page.replace_html "tab-content-members", :partial => 'projects/settings/members'
page << 'hideOnLoad()'
members.each {|member| page.visual_effect(:highlight, "member-#{member.id}") }
}
}
}
else
format.js {
render(:update) {|page|
errors = members.collect {|m|
m.errors.full_messages
}.flatten.uniq
page.alert(l(:notice_failed_to_save_members, :errors => errors.join(', ')))
}
}
end
end
end
@@ -51,6 +70,7 @@ class MembersController < ApplicationController
format.js {
render(:update) {|page|
page.replace_html "tab-content-members", :partial => 'projects/settings/members'
page << 'hideOnLoad()'
page.visual_effect(:highlight, "member-#{@member.id}")
}
}
@@ -64,7 +84,11 @@ class MembersController < ApplicationController
end
respond_to do |format|
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
format.js { render(:update) {|page| page.replace_html "tab-content-members", :partial => 'projects/settings/members'} }
format.js { render(:update) {|page|
page.replace_html "tab-content-members", :partial => 'projects/settings/members'
page << 'hideOnLoad()'
}
}
end
end
@@ -73,17 +97,4 @@ class MembersController < ApplicationController
render :layout => false
end
private
def find_project
@project = Project.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
def find_member
@member = Member.find(params[:id])
@project = @member.project
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

@@ -29,10 +29,24 @@ class MessagesController < ApplicationController
helper :attachments
include AttachmentsHelper
REPLIES_PER_PAGE = 25 unless const_defined?(:REPLIES_PER_PAGE)
# Show a topic and its replies
def show
@replies = @topic.children.find(:all, :include => [:author, :attachments, {:board => :project}])
@replies.reverse! if User.current.wants_comments_in_reverse_order?
page = params[:page]
# Find the page of the requested reply
if params[:r] && page.nil?
offset = @topic.children.count(:conditions => ["#{Message.table_name}.id < ?", params[:r].to_i])
page = 1 + offset / REPLIES_PER_PAGE
end
@reply_count = @topic.children.count
@reply_pages = Paginator.new self, @reply_count, REPLIES_PER_PAGE, page
@replies = @topic.children.find(:all, :include => [:author, :attachments, {:board => :project}],
:order => "#{Message.table_name}.created_on ASC",
:limit => @reply_pages.items_per_page,
:offset => @reply_pages.current.offset)
@reply = Message.new(:subject => "RE: #{@message.subject}")
render :action => "show", :layout => false if request.xhr?
end
@@ -48,7 +62,8 @@ class MessagesController < ApplicationController
end
if request.post? && @message.save
call_hook(:controller_messages_new_after_save, { :params => params, :message => @message})
attach_files(@message, params[:attachments])
attachments = Attachment.attach_files(@message, params[:attachments])
render_attachment_warning_if_needed(@message)
redirect_to :action => 'show', :id => @message
end
end
@@ -61,9 +76,10 @@ class MessagesController < ApplicationController
@topic.children << @reply
if !@reply.new_record?
call_hook(:controller_messages_reply_after_save, { :params => params, :message => @reply})
attach_files(@reply, params[:attachments])
attachments = Attachment.attach_files(@reply, params[:attachments])
render_attachment_warning_if_needed(@reply)
end
redirect_to :action => 'show', :id => @topic
redirect_to :action => 'show', :id => @topic, :r => @reply
end
# Edit a message
@@ -74,10 +90,11 @@ class MessagesController < ApplicationController
@message.sticky = params[:message]['sticky']
end
if request.post? && @message.update_attributes(params[:message])
attach_files(@message, params[:attachments])
attachments = Attachment.attach_files(@message, params[:attachments])
render_attachment_warning_if_needed(@message)
flash[:notice] = l(:notice_successful_update)
@message.reload
redirect_to :action => 'show', :board_id => @message.board, :id => @message.root
redirect_to :action => 'show', :board_id => @message.board, :id => @message.root, :r => (@message.parent_id && @message.id)
end
end
@@ -87,7 +104,7 @@ class MessagesController < ApplicationController
@message.destroy
redirect_to @message.parent.nil? ?
{ :controller => 'boards', :action => 'show', :project_id => @project, :id => @board } :
{ :action => 'show', :id => @message.parent }
{ :action => 'show', :id => @message.parent, :r => @message }
end
def quote

View File

@@ -19,6 +19,7 @@ class MyController < ApplicationController
before_filter :require_login
helper :issues
helper :users
helper :custom_fields
BLOCKS = { 'issuesassignedtome' => :label_assigned_to_me_issues,
@@ -53,31 +54,24 @@ class MyController < ApplicationController
@user = User.current
@pref = @user.pref
if request.post?
@user.attributes = params[:user]
@user.mail_notification = (params[:notification_option] == 'all')
@user.safe_attributes = params[:user]
@user.pref.attributes = params[:pref]
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
if @user.save
@user.pref.save
@user.notified_project_ids = (params[:notification_option] == 'selected' ? params[:notified_project_ids] : [])
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
set_language_if_valid @user.language
flash[:notice] = l(:notice_account_updated)
redirect_to :action => 'account'
return
end
end
@notification_options = [[l(:label_user_mail_option_all), 'all'],
[l(:label_user_mail_option_none), 'none']]
# Only users that belong to more than 1 project can select projects for which they are notified
# Note that @user.membership.size would fail since AR ignores :include association option when doing a count
@notification_options.insert 1, [l(:label_user_mail_option_selected), 'selected'] if @user.memberships.length > 1
@notification_option = @user.mail_notification? ? 'all' : (@user.notified_projects_ids.empty? ? 'none' : 'selected')
end
# Manage user's password
def password
@user = User.current
if @user.auth_source_id
unless @user.change_password_allowed?
flash[:error] = l(:notice_can_t_change_password)
redirect_to :action => 'account'
return

View File

@@ -17,22 +17,35 @@
class NewsController < ApplicationController
default_search_scope :news
before_filter :find_news, :except => [:new, :index, :preview]
before_filter :find_project, :only => [:new, :preview]
before_filter :authorize, :except => [:index, :preview]
model_object News
before_filter :find_model_object, :except => [:new, :create, :index]
before_filter :find_project_from_association, :except => [:new, :create, :index]
before_filter :find_project, :only => [:new, :create]
before_filter :authorize, :except => [:index]
before_filter :find_optional_project, :only => :index
accept_key_auth :index
def index
@news_pages, @newss = paginate :news,
:per_page => 10,
:conditions => Project.allowed_to_condition(User.current, :view_news, :project => @project),
:include => [:author, :project],
:order => "#{News.table_name}.created_on DESC"
case params[:format]
when 'xml', 'json'
@offset, @limit = api_offset_and_limit
else
@limit = 10
end
scope = @project ? @project.news.visible : News.visible
@news_count = scope.count
@news_pages = Paginator.new self, @news_count, @limit, params['page']
@offset ||= @news_pages.current.offset
@newss = scope.all(:include => [:author, :project],
:order => "#{News.table_name}.created_on DESC",
:offset => @offset,
:limit => @limit)
respond_to do |format|
format.html { render :layout => false if request.xhr? }
format.xml { render :xml => @newss.to_xml }
format.json { render :json => @newss.to_json }
format.api
format.atom { render_feed(@newss, :title => (@project ? @project.name : Setting.app_title) + ": #{l(:label_news_plural)}") }
end
end
@@ -44,57 +57,39 @@ class NewsController < ApplicationController
def new
@news = News.new(:project => @project, :author => User.current)
end
def create
@news = News.new(:project => @project, :author => User.current)
if request.post?
@news.attributes = params[:news]
if @news.save
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'news', :action => 'index', :project_id => @project
else
render :action => 'new'
end
end
end
def edit
if request.post? and @news.update_attributes(params[:news])
end
def update
if request.put? and @news.update_attributes(params[:news])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'show', :id => @news
end
end
def add_comment
@comment = Comment.new(params[:comment])
@comment.author = User.current
if @news.comments << @comment
flash[:notice] = l(:label_comment_added)
redirect_to :action => 'show', :id => @news
else
show
render :action => 'show'
render :action => 'edit'
end
end
def destroy_comment
@news.comments.find(params[:comment_id]).destroy
redirect_to :action => 'show', :id => @news
end
def destroy
@news.destroy
redirect_to :action => 'index', :project_id => @project
end
def preview
@text = (params[:news] ? params[:news][:description] : nil)
render :partial => 'common/preview'
end
private
def find_news
@news = News.find(params[:id])
@project = @news.project
rescue ActiveRecord::RecordNotFound
render_404
end
def find_project
@project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound

View File

@@ -0,0 +1,33 @@
class PreviewsController < ApplicationController
before_filter :find_project
def issue
@issue = @project.issues.find_by_id(params[:id]) unless params[:id].blank?
if @issue
@attachements = @issue.attachments
@description = params[:issue] && params[:issue][:description]
if @description && @description.gsub(/(\r?\n|\n\r?)/, "\n") == @issue.description.to_s.gsub(/(\r?\n|\n\r?)/, "\n")
@description = nil
end
@notes = params[:notes]
else
@description = (params[:issue] ? params[:issue][:description] : nil)
end
render :layout => false
end
def news
@text = (params[:news] ? params[:news][:description] : nil)
render :partial => 'common/preview'
end
private
def find_project
project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id]
@project = Project.find(project_id)
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

@@ -0,0 +1,26 @@
class ProjectEnumerationsController < ApplicationController
before_filter :find_project_by_project_id
before_filter :authorize
def update
if request.put? && params[:enumerations]
Project.transaction do
params[:enumerations].each do |id, activity|
@project.update_or_create_time_entry_activity(id, activity)
end
end
flash[:notice] = l(:notice_successful_update)
end
redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project
end
def destroy
@project.time_entry_activities.each do |time_entry_activity|
time_entry_activity.destroy(time_entry_activity.parent)
end
flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project
end
end

View File

@@ -17,30 +17,26 @@
class ProjectsController < ApplicationController
menu_item :overview
menu_item :activity, :only => :activity
menu_item :roadmap, :only => :roadmap
menu_item :files, :only => [:list_files, :add_file]
menu_item :settings, :only => :settings
before_filter :find_project, :except => [ :index, :list, :add, :copy, :activity ]
before_filter :find_optional_project, :only => :activity
before_filter :authorize, :except => [ :index, :list, :add, :copy, :archive, :unarchive, :destroy, :activity ]
before_filter :authorize_global, :only => :add
before_filter :find_project, :except => [ :index, :list, :new, :create, :copy ]
before_filter :authorize, :except => [ :index, :list, :new, :create, :copy, :archive, :unarchive, :destroy]
before_filter :authorize_global, :only => [:new, :create]
before_filter :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ]
accept_key_auth :activity
after_filter :only => [:add, :edit, :archive, :unarchive, :destroy] do |controller|
accept_key_auth :index, :show, :create, :update, :destroy
after_filter :only => [:create, :edit, :update, :archive, :unarchive, :destroy] do |controller|
if controller.request.post?
controller.send :expire_action, :controller => 'welcome', :action => 'robots.txt'
end
end
helper :sort
include SortHelper
helper :custom_fields
include CustomFieldsHelper
helper :issues
helper IssuesHelper
helper :queries
include QueriesHelper
helper :repositories
@@ -53,6 +49,11 @@ class ProjectsController < ApplicationController
format.html {
@projects = Project.visible.find(:all, :order => 'lft')
}
format.api {
@offset, @limit = api_offset_and_limit
@project_count = Project.visible.count
@projects = Project.visible.all(:offset => @offset, :limit => @limit, :order => 'lft')
}
format.atom {
projects = Project.visible.find(:all, :order => 'created_on DESC',
:limit => Setting.feeds_limit.to_i)
@@ -61,30 +62,41 @@ class ProjectsController < ApplicationController
end
end
# Add a new project
def add
def new
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.all
@project = Project.new(params[:project])
if request.get?
@project.identifier = Project.next_identifier if Setting.sequential_project_identifiers?
@project.trackers = Tracker.all
@project.is_public = Setting.default_projects_public?
@project.enabled_module_names = Setting.default_projects_modules
else
@project.enabled_module_names = params[:enabled_modules]
if validate_parent_id && @project.save
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
# Add current user as a project member if he is not admin
unless User.current.admin?
r = Role.givable.find_by_id(Setting.new_project_user_role_id.to_i) || Role.givable.first
m = Member.new(:user => User.current, :roles => [r])
@project.members << m
end
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'projects', :action => 'settings', :id => @project
end
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
def create
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.all
@project = Project.new
@project.safe_attributes = params[:project]
if validate_parent_id && @project.save
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
# Add current user as a project member if he is not admin
unless User.current.admin?
r = Role.givable.find_by_id(Setting.new_project_user_role_id.to_i) || Role.givable.first
m = Member.new(:user => User.current, :roles => [r])
@project.members << m
end
end
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'projects', :action => 'settings', :id => @project
}
format.api { render :action => 'show', :status => :created, :location => url_for(:controller => 'projects', :action => 'show', :id => @project.id) }
end
else
respond_to do |format|
format.html { render :action => 'new' }
format.api { render_validation_errors(@project) }
end
end
end
def copy
@@ -102,18 +114,21 @@ class ProjectsController < ApplicationController
redirect_to :controller => 'admin', :action => 'projects'
end
else
@project = Project.new(params[:project])
@project.enabled_module_names = params[:enabled_modules]
if validate_parent_id && @project.copy(@source_project, :only => params[:only])
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'admin', :action => 'projects'
elsif !@project.new_record?
# Project was created
# But some objects were not copied due to validation failures
# (eg. issues from disabled trackers)
# TODO: inform about that
redirect_to :controller => 'admin', :action => 'projects'
Mailer.with_deliveries(params[:notifications] == '1') do
@project = Project.new
@project.safe_attributes = params[:project]
@project.enabled_module_names = params[:enabled_modules]
if validate_parent_id && @project.copy(@source_project, :only => params[:only])
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'projects', :action => 'settings', :id => @project
elsif !@project.new_record?
# Project was created
# But some objects were not copied due to validation failures
# (eg. issues from disabled trackers)
# TODO: inform about that
redirect_to :controller => 'projects', :action => 'settings', :id => @project
end
end
end
rescue ActiveRecord::RecordNotFound
@@ -147,6 +162,11 @@ class ProjectsController < ApplicationController
:conditions => cond).to_f
end
@key = User.current.rss_key
respond_to do |format|
format.html
format.api
end
end
def settings
@@ -158,23 +178,37 @@ class ProjectsController < ApplicationController
@wiki ||= @project.wiki
end
# Edit @project
def edit
if request.post?
@project.attributes = params[:project]
if validate_parent_id && @project.save
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'settings', :id => @project
else
settings
render :action => 'settings'
end
# TODO: convert to PUT only
verify :method => [:post, :put], :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
def update
@project.safe_attributes = params[:project]
if validate_parent_id && @project.save
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'settings', :id => @project
}
format.api { head :ok }
end
else
respond_to do |format|
format.html {
settings
render :action => 'settings'
}
format.api { render_validation_errors(@project) }
end
end
end
verify :method => :post, :only => :modules, :render => {:nothing => true, :status => :method_not_allowed }
def modules
@project.enabled_module_names = params[:enabled_modules]
@project.enabled_module_names = params[:enabled_module_names]
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'settings', :id => @project, :tab => 'modules'
end
@@ -195,197 +229,22 @@ class ProjectsController < ApplicationController
# Delete @project
def destroy
@project_to_destroy = @project
if request.post? and params[:confirm]
@project_to_destroy.destroy
redirect_to :controller => 'admin', :action => 'projects'
if request.get?
# display confirmation view
else
if api_request? || params[:confirm]
@project_to_destroy.destroy
respond_to do |format|
format.html { redirect_to :controller => 'admin', :action => 'projects' }
format.api { head :ok }
end
end
end
# hide project in layout
@project = nil
end
# Add a new issue category to @project
def add_issue_category
@category = @project.issue_categories.build(params[:category])
if request.post?
if @category.save
respond_to do |format|
format.html do
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'settings', :tab => 'categories', :id => @project
end
format.js do
# IE doesn't support the replace_html rjs method for select box options
render(:update) {|page| page.replace "issue_category_id",
content_tag('select', '<option></option>' + options_from_collection_for_select(@project.issue_categories, 'id', 'name', @category.id), :id => 'issue_category_id', :name => 'issue[category_id]')
}
end
end
else
respond_to do |format|
format.html
format.js do
render(:update) {|page| page.alert(@category.errors.full_messages.join('\n')) }
end
end
end
end
end
# Add a new version to @project
def add_version
@version = @project.versions.build
if params[:version]
attributes = params[:version].dup
attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing'])
@version.attributes = attributes
end
if request.post?
if @version.save
respond_to do |format|
format.html do
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'settings', :tab => 'versions', :id => @project
end
format.js do
# IE doesn't support the replace_html rjs method for select box options
render(:update) {|page| page.replace "issue_fixed_version_id",
content_tag('select', '<option></option>' + version_options_for_select(@project.shared_versions.open, @version), :id => 'issue_fixed_version_id', :name => 'issue[fixed_version_id]')
}
end
end
else
respond_to do |format|
format.html
format.js do
render(:update) {|page| page.alert(@version.errors.full_messages.join('\n')) }
end
end
end
end
end
def add_file
if request.post?
container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id]))
attachments = attach_files(container, params[:attachments])
if !attachments.empty? && Setting.notified_events.include?('file_added')
Mailer.deliver_attachments_added(attachments)
end
redirect_to :controller => 'projects', :action => 'list_files', :id => @project
return
end
@versions = @project.versions.sort
end
def save_activities
if request.post? && params[:enumerations]
Project.transaction do
params[:enumerations].each do |id, activity|
@project.update_or_create_time_entry_activity(id, activity)
end
end
end
redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project
end
def reset_activities
@project.time_entry_activities.each do |time_entry_activity|
time_entry_activity.destroy(time_entry_activity.parent)
end
redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project
end
def list_files
sort_init 'filename', 'asc'
sort_update 'filename' => "#{Attachment.table_name}.filename",
'created_on' => "#{Attachment.table_name}.created_on",
'size' => "#{Attachment.table_name}.filesize",
'downloads' => "#{Attachment.table_name}.downloads"
@containers = [ Project.find(@project.id, :include => :attachments, :order => sort_clause)]
@containers += @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse
render :layout => !request.xhr?
end
def roadmap
@trackers = @project.trackers.find(:all, :order => 'position')
retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?})
@with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
@versions = @project.shared_versions.sort
@versions.reject! {|version| version.closed? || version.completed? } unless params[:completed]
@issues_by_version = {}
unless @selected_tracker_ids.empty?
@versions.each do |version|
conditions = {:tracker_id => @selected_tracker_ids}
if !@project.versions.include?(version)
conditions.merge!(:project_id => project_ids)
end
issues = version.fixed_issues.visible.find(:all,
:include => [:project, :status, :tracker, :priority],
:conditions => conditions,
:order => "#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id")
@issues_by_version[version] = issues
end
end
@versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].empty?}
end
def activity
@days = Setting.activity_days_default.to_i
if params[:from]
begin; @date_to = params[:from].to_date + 1; rescue; end
end
@date_to ||= Date.today + 1
@date_from = @date_to - @days
@with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
@author = (params[:user_id].blank? ? nil : User.active.find(params[:user_id]))
@activity = Redmine::Activity::Fetcher.new(User.current, :project => @project,
:with_subprojects => @with_subprojects,
:author => @author)
@activity.scope_select {|t| !params["show_#{t}"].nil?}
@activity.scope = (@author.nil? ? :default : :all) if @activity.scope.empty?
events = @activity.events(@date_from, @date_to)
if events.empty? || stale?(:etag => [events.first, User.current])
respond_to do |format|
format.html {
@events_by_day = events.group_by(&:event_date)
render :layout => false if request.xhr?
}
format.atom {
title = l(:label_activity)
if @author
title = @author.name
elsif @activity.scope.size == 1
title = l("label_#{@activity.scope.first.singularize}_plural")
end
render_feed(events, :title => "#{@project || Setting.app_title}: #{title}")
}
end
end
rescue ActiveRecord::RecordNotFound
render_404
end
private
# Find project of id params[:id]
# if not found, redirect to project list
# Used as a before_filter
def find_project
@project = Project.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
def find_optional_project
return true unless params[:id]
@project = Project.find(params[:id])
@@ -394,14 +253,6 @@ private
render_404
end
def retrieve_selected_tracker_ids(selectable_trackers, default_trackers=nil)
if ids = params[:tracker_ids]
@selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
else
@selected_tracker_ids = (default_trackers || selectable_trackers).collect {|t| t.id.to_s }
end
end
# Validates parent_id param according to user's permissions
# TODO: move it to Project model in a validation that depends on User.current
def validate_parent_id

View File

@@ -27,9 +27,7 @@ class QueriesController < ApplicationController
@query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
@query.column_names = nil if params[:default_columns]
params[:fields].each do |field|
@query.add_filter(field, params[:operators][field], params[:values][field])
end if params[:fields]
@query.add_filters(params[:fields], params[:operators], params[:values]) if params[:fields]
@query.group_by ||= params[:group_by]
if request.post? && params[:confirm] && @query.save
@@ -43,9 +41,7 @@ class QueriesController < ApplicationController
def edit
if request.post?
@query.filters = {}
params[:fields].each do |field|
@query.add_filter(field, params[:operators][field], params[:values][field])
end if params[:fields]
@query.add_filters(params[:fields], params[:operators], params[:values]) if params[:fields]
@query.attributes = params[:query]
@query.project = nil if params[:query_is_for_all]
@query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
@@ -74,7 +70,7 @@ private
def find_optional_project
@project = Project.find(params[:project_id]) if params[:project_id]
User.current.allowed_to?(:save_queries, @project, :global => true)
render_403 unless User.current.allowed_to?(:save_queries, @project, :global => true)
rescue ActiveRecord::RecordNotFound
render_404
end

View File

@@ -17,184 +17,79 @@
class ReportsController < ApplicationController
menu_item :issues
before_filter :find_project, :authorize
before_filter :find_project, :authorize, :find_issue_statuses
def issue_report
@statuses = IssueStatus.find(:all, :order => 'position')
@trackers = @project.trackers
@versions = @project.shared_versions.sort
@priorities = IssuePriority.all
@categories = @project.issue_categories
@assignees = @project.members.collect { |m| m.user }.sort
@authors = @project.members.collect { |m| m.user }.sort
@subprojects = @project.descendants.visible
@issues_by_tracker = Issue.by_tracker(@project)
@issues_by_version = Issue.by_version(@project)
@issues_by_priority = Issue.by_priority(@project)
@issues_by_category = Issue.by_category(@project)
@issues_by_assigned_to = Issue.by_assigned_to(@project)
@issues_by_author = Issue.by_author(@project)
@issues_by_subproject = Issue.by_subproject(@project) || []
render :template => "reports/issue_report"
end
def issue_report_details
case params[:detail]
when "tracker"
@field = "tracker_id"
@rows = @project.trackers
@data = issues_by_tracker
@data = Issue.by_tracker(@project)
@report_title = l(:field_tracker)
render :template => "reports/issue_report_details"
when "version"
@field = "fixed_version_id"
@rows = @project.shared_versions.sort
@data = issues_by_version
@data = Issue.by_version(@project)
@report_title = l(:field_version)
render :template => "reports/issue_report_details"
when "priority"
@field = "priority_id"
@rows = IssuePriority.all
@data = issues_by_priority
@data = Issue.by_priority(@project)
@report_title = l(:field_priority)
render :template => "reports/issue_report_details"
when "category"
@field = "category_id"
@rows = @project.issue_categories
@data = issues_by_category
@data = Issue.by_category(@project)
@report_title = l(:field_category)
render :template => "reports/issue_report_details"
when "assigned_to"
@field = "assigned_to_id"
@rows = @project.members.collect { |m| m.user }
@data = issues_by_assigned_to
@rows = @project.members.collect { |m| m.user }.sort
@data = Issue.by_assigned_to(@project)
@report_title = l(:field_assigned_to)
render :template => "reports/issue_report_details"
when "author"
@field = "author_id"
@rows = @project.members.collect { |m| m.user }
@data = issues_by_author
@rows = @project.members.collect { |m| m.user }.sort
@data = Issue.by_author(@project)
@report_title = l(:field_author)
render :template => "reports/issue_report_details"
when "subproject"
@field = "project_id"
@rows = @project.descendants.active
@data = issues_by_subproject
@rows = @project.descendants.visible
@data = Issue.by_subproject(@project) || []
@report_title = l(:field_subproject)
render :template => "reports/issue_report_details"
else
@trackers = @project.trackers
@versions = @project.shared_versions.sort
@priorities = IssuePriority.all
@categories = @project.issue_categories
@assignees = @project.members.collect { |m| m.user }
@authors = @project.members.collect { |m| m.user }
@subprojects = @project.descendants.active
issues_by_tracker
issues_by_version
issues_by_priority
issues_by_category
issues_by_assigned_to
issues_by_author
issues_by_subproject
render :template => "reports/issue_report"
end
end
private
# Find project of id params[:id]
def find_project
@project = Project.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
respond_to do |format|
if @field
format.html {}
else
format.html { redirect_to :action => 'issue_report', :id => @project }
end
end
end
def issues_by_tracker
@issues_by_tracker ||=
ActiveRecord::Base.connection.select_all("select s.id as status_id,
s.is_closed as closed,
t.id as tracker_id,
count(i.id) as total
from
#{Issue.table_name} i, #{IssueStatus.table_name} s, #{Tracker.table_name} t
where
i.status_id=s.id
and i.tracker_id=t.id
and i.project_id=#{@project.id}
group by s.id, s.is_closed, t.id")
end
private
def issues_by_version
@issues_by_version ||=
ActiveRecord::Base.connection.select_all("select s.id as status_id,
s.is_closed as closed,
v.id as fixed_version_id,
count(i.id) as total
from
#{Issue.table_name} i, #{IssueStatus.table_name} s, #{Version.table_name} v
where
i.status_id=s.id
and i.fixed_version_id=v.id
and i.project_id=#{@project.id}
group by s.id, s.is_closed, v.id")
end
def issues_by_priority
@issues_by_priority ||=
ActiveRecord::Base.connection.select_all("select s.id as status_id,
s.is_closed as closed,
p.id as priority_id,
count(i.id) as total
from
#{Issue.table_name} i, #{IssueStatus.table_name} s, #{IssuePriority.table_name} p
where
i.status_id=s.id
and i.priority_id=p.id
and i.project_id=#{@project.id}
group by s.id, s.is_closed, p.id")
end
def issues_by_category
@issues_by_category ||=
ActiveRecord::Base.connection.select_all("select s.id as status_id,
s.is_closed as closed,
c.id as category_id,
count(i.id) as total
from
#{Issue.table_name} i, #{IssueStatus.table_name} s, #{IssueCategory.table_name} c
where
i.status_id=s.id
and i.category_id=c.id
and i.project_id=#{@project.id}
group by s.id, s.is_closed, c.id")
end
def issues_by_assigned_to
@issues_by_assigned_to ||=
ActiveRecord::Base.connection.select_all("select s.id as status_id,
s.is_closed as closed,
a.id as assigned_to_id,
count(i.id) as total
from
#{Issue.table_name} i, #{IssueStatus.table_name} s, #{User.table_name} a
where
i.status_id=s.id
and i.assigned_to_id=a.id
and i.project_id=#{@project.id}
group by s.id, s.is_closed, a.id")
end
def issues_by_author
@issues_by_author ||=
ActiveRecord::Base.connection.select_all("select s.id as status_id,
s.is_closed as closed,
a.id as author_id,
count(i.id) as total
from
#{Issue.table_name} i, #{IssueStatus.table_name} s, #{User.table_name} a
where
i.status_id=s.id
and i.author_id=a.id
and i.project_id=#{@project.id}
group by s.id, s.is_closed, a.id")
end
def issues_by_subproject
@issues_by_subproject ||=
ActiveRecord::Base.connection.select_all("select s.id as status_id,
s.is_closed as closed,
i.project_id as project_id,
count(i.id) as total
from
#{Issue.table_name} i, #{IssueStatus.table_name} s
where
i.status_id=s.id
and i.project_id IN (#{@project.descendants.active.collect{|p| p.id}.join(',')})
group by s.id, s.is_closed, i.project_id") if @project.descendants.active.any?
@issues_by_subproject ||= []
def find_issue_statuses
@statuses = IssueStatus.find(:all, :order => 'position')
end
end

View File

@@ -24,6 +24,7 @@ class InvalidRevisionParam < Exception; end
class RepositoriesController < ApplicationController
menu_item :repository
menu_item :settings, :only => :edit
default_search_scope :changesets
before_filter :find_repository, :except => :edit
@@ -43,7 +44,13 @@ class RepositoriesController < ApplicationController
@repository.attributes = params[:repository]
@repository.save
end
render(:update) {|page| page.replace_html "tab-content-repository", :partial => 'projects/settings/repository'}
render(:update) do |page|
page.replace_html "tab-content-repository", :partial => 'projects/settings/repository'
if @repository && !@project.repository
@project.reload #needed to reload association
page.replace_html "main-menu", render_main_menu(@project)
end
end
end
def committers
@@ -116,7 +123,7 @@ class RepositoriesController < ApplicationController
(show_error_not_found; return) unless @content
if 'raw' == params[:format] || @content.is_binary_data? || (@entry.size && @entry.size > Setting.file_max_size_displayed.to_i.kilobyte)
# Force the download
send_data @content, :filename => @path.split('/').last
send_data @content, :filename => filename_for_content_disposition(@path.split('/').last)
else
# Prevent empty lines when displaying a file with Windows style eol
@content.gsub!("\r\n", "\n")
@@ -132,6 +139,7 @@ class RepositoriesController < ApplicationController
end
def revision
raise ChangesetNotFound if @rev.nil? || @rev.empty?
@changeset = @repository.find_changeset_by_name(@rev)
raise ChangesetNotFound unless @changeset
@@ -167,6 +175,9 @@ class RepositoriesController < ApplicationController
@diff = @repository.diff(@path, @rev, @rev_to)
show_error_not_found unless @diff
end
@changeset = @repository.find_changeset_by_name(@rev)
@changeset_to = @rev_to ? @repository.find_changeset_by_name(@rev_to) : nil
end
end
@@ -189,13 +200,10 @@ class RepositoriesController < ApplicationController
end
end
private
def find_project
@project = Project.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
private
REV_PARAM_RE = %r{\A[a-f0-9]*\Z}i
def find_repository
@project = Project.find(params[:id])
@repository = @project.repository
@@ -204,6 +212,12 @@ private
@path ||= ''
@rev = params[:rev].blank? ? @repository.default_branch : params[:rev].strip
@rev_to = params[:rev_to]
unless @rev.to_s.match(REV_PARAM_RE) && @rev_to.to_s.match(REV_PARAM_RE)
if @repository.branches.blank?
raise InvalidRevisionParam
end
end
rescue ActiveRecord::RecordNotFound
render_404
rescue InvalidRevisionParam
@@ -211,7 +225,7 @@ private
end
def show_error_not_found
render_error l(:error_scm_not_found)
render_error :message => l(:error_scm_not_found), :status => 404
end
# Handler for Redmine::Scm::Adapters::CommandFailed exception

View File

@@ -21,16 +21,11 @@ class RolesController < ApplicationController
before_filter :require_admin
verify :method => :post, :only => [ :destroy, :move ],
:redirect_to => { :action => :list }
:redirect_to => { :action => :index }
def index
list
render :action => 'list' unless request.xhr?
end
def list
@role_pages, @roles = paginate :roles, :per_page => 25, :order => 'builtin, position'
render :action => "list", :layout => false if request.xhr?
render :action => "index", :layout => false if request.xhr?
end
def new
@@ -62,7 +57,7 @@ class RolesController < ApplicationController
@role.destroy
redirect_to :action => 'index'
rescue
flash[:error] = 'This role is in use and can not be deleted.'
flash[:error] = l(:error_can_not_remove_role)
redirect_to :action => 'index'
end

View File

@@ -43,12 +43,12 @@ class SearchController < ApplicationController
begin; offset = params[:offset].to_time if params[:offset]; rescue; end
# quick jump to an issue
if @question.match(/^#?(\d+)$/) && Issue.visible.find_by_id($1)
if @question.match(/^#?(\d+)$/) && Issue.visible.find_by_id($1.to_i)
redirect_to :controller => "issues", :action => "show", :id => $1
return
end
@object_types = %w(issues news documents changesets wiki_pages messages projects)
@object_types = Redmine::Search.available_search_types.dup
if projects_to_search.is_a? Project
# don't search projects
@object_types.delete('projects')
@@ -62,21 +62,19 @@ class SearchController < ApplicationController
# extract tokens from the question
# eg. hello "bye bye" => ["hello", "bye bye"]
@tokens = @question.scan(%r{((\s|^)"[\s\w]+"(\s|$)|\S+)}).collect {|m| m.first.gsub(%r{(^\s*"\s*|\s*"\s*$)}, '')}
# tokens must be at least 3 character long
@tokens = @tokens.uniq.select {|w| w.length > 2 }
# tokens must be at least 2 characters long
@tokens = @tokens.uniq.select {|w| w.length > 1 }
if !@tokens.empty?
# no more than 5 tokens to search for
@tokens.slice! 5..-1 if @tokens.size > 5
# strings used in sql like statement
like_tokens = @tokens.collect {|w| "%#{w.downcase}%"}
@tokens.slice! 5..-1 if @tokens.size > 5
@results = []
@results_by_type = Hash.new {|h,k| h[k] = 0}
limit = 10
@scope.each do |s|
r, c = s.singularize.camelcase.constantize.search(like_tokens, projects_to_search,
r, c = s.singularize.camelcase.constantize.search(@tokens, projects_to_search,
:all_words => @all_words,
:titles_only => @titles_only,
:limit => (limit+1),

View File

@@ -26,7 +26,7 @@ class SettingsController < ApplicationController
end
def edit
@notifiables = %w(issue_added issue_updated news_added document_added file_added message_posted wiki_content_added wiki_content_updated)
@notifiables = Redmine::Notifiable.all
if request.post? && params[:settings] && params[:settings].is_a?(Hash)
settings = (params[:settings] || {}).dup.symbolize_keys
settings.each do |name, value|
@@ -44,6 +44,8 @@ class SettingsController < ApplicationController
@guessed_host_and_path = request.host_with_port.dup
@guessed_host_and_path << ('/'+ Redmine::Utils.relative_url_root.gsub(%r{^\/}, '')) unless Redmine::Utils.relative_url_root.blank?
Redmine::Themes.rescan
end
def plugin

View File

@@ -0,0 +1,209 @@
class TimeEntryReportsController < ApplicationController
menu_item :issues
before_filter :find_optional_project
before_filter :load_available_criterias
helper :sort
include SortHelper
helper :issues
helper :timelog
include TimelogHelper
helper :custom_fields
include CustomFieldsHelper
def report
@criterias = params[:criterias] || []
@criterias = @criterias.select{|criteria| @available_criterias.has_key? criteria}
@criterias.uniq!
@criterias = @criterias[0,3]
@columns = (params[:columns] && %w(year month week day).include?(params[:columns])) ? params[:columns] : 'month'
retrieve_date_range
unless @criterias.empty?
sql_select = @criterias.collect{|criteria| @available_criterias[criteria][:sql] + " AS " + criteria}.join(', ')
sql_group_by = @criterias.collect{|criteria| @available_criterias[criteria][:sql]}.join(', ')
sql_condition = ''
if @project.nil?
sql_condition = Project.allowed_to_condition(User.current, :view_time_entries)
elsif @issue.nil?
sql_condition = @project.project_condition(Setting.display_subprojects_issues?)
else
sql_condition = "#{Issue.table_name}.root_id = #{@issue.root_id} AND #{Issue.table_name}.lft >= #{@issue.lft} AND #{Issue.table_name}.rgt <= #{@issue.rgt}"
end
sql = "SELECT #{sql_select}, tyear, tmonth, tweek, spent_on, SUM(hours) AS hours"
sql << " FROM #{TimeEntry.table_name}"
sql << time_report_joins
sql << " WHERE"
sql << " (%s) AND" % sql_condition
sql << " (spent_on BETWEEN '%s' AND '%s')" % [ActiveRecord::Base.connection.quoted_date(@from), ActiveRecord::Base.connection.quoted_date(@to)]
sql << " GROUP BY #{sql_group_by}, tyear, tmonth, tweek, spent_on"
@hours = ActiveRecord::Base.connection.select_all(sql)
@hours.each do |row|
case @columns
when 'year'
row['year'] = row['tyear']
when 'month'
row['month'] = "#{row['tyear']}-#{row['tmonth']}"
when 'week'
row['week'] = "#{row['tyear']}-#{row['tweek']}"
when 'day'
row['day'] = "#{row['spent_on']}"
end
end
@total_hours = @hours.inject(0) {|s,k| s = s + k['hours'].to_f}
@periods = []
# Date#at_beginning_of_ not supported in Rails 1.2.x
date_from = @from.to_time
# 100 columns max
while date_from <= @to.to_time && @periods.length < 100
case @columns
when 'year'
@periods << "#{date_from.year}"
date_from = (date_from + 1.year).at_beginning_of_year
when 'month'
@periods << "#{date_from.year}-#{date_from.month}"
date_from = (date_from + 1.month).at_beginning_of_month
when 'week'
@periods << "#{date_from.year}-#{date_from.to_date.cweek}"
date_from = (date_from + 7.day).at_beginning_of_week
when 'day'
@periods << "#{date_from.to_date}"
date_from = date_from + 1.day
end
end
end
respond_to do |format|
format.html { render :layout => !request.xhr? }
format.csv { send_data(report_to_csv(@criterias, @periods, @hours), :type => 'text/csv; header=present', :filename => 'timelog.csv') }
end
end
private
# TODO: duplicated in TimelogController
def find_optional_project
if !params[:issue_id].blank?
@issue = Issue.find(params[:issue_id])
@project = @issue.project
elsif !params[:project_id].blank?
@project = Project.find(params[:project_id])
end
deny_access unless User.current.allowed_to?(:view_time_entries, @project, :global => true)
end
# Retrieves the date range based on predefined ranges or specific from/to param dates
# TODO: duplicated in TimelogController
def retrieve_date_range
@free_period = false
@from, @to = nil, nil
if params[:period_type] == '1' || (params[:period_type].nil? && !params[:period].nil?)
case params[:period].to_s
when 'today'
@from = @to = Date.today
when 'yesterday'
@from = @to = Date.today - 1
when 'current_week'
@from = Date.today - (Date.today.cwday - 1)%7
@to = @from + 6
when 'last_week'
@from = Date.today - 7 - (Date.today.cwday - 1)%7
@to = @from + 6
when '7_days'
@from = Date.today - 7
@to = Date.today
when 'current_month'
@from = Date.civil(Date.today.year, Date.today.month, 1)
@to = (@from >> 1) - 1
when 'last_month'
@from = Date.civil(Date.today.year, Date.today.month, 1) << 1
@to = (@from >> 1) - 1
when '30_days'
@from = Date.today - 30
@to = Date.today
when 'current_year'
@from = Date.civil(Date.today.year, 1, 1)
@to = Date.civil(Date.today.year, 12, 31)
end
elsif params[:period_type] == '2' || (params[:period_type].nil? && (!params[:from].nil? || !params[:to].nil?))
begin; @from = params[:from].to_s.to_date unless params[:from].blank?; rescue; end
begin; @to = params[:to].to_s.to_date unless params[:to].blank?; rescue; end
@free_period = true
else
# default
end
@from, @to = @to, @from if @from && @to && @from > @to
@from ||= (TimeEntry.earilest_date_for_project(@project) || Date.today)
@to ||= (TimeEntry.latest_date_for_project(@project) || Date.today)
end
def load_available_criterias
@available_criterias = { 'project' => {:sql => "#{TimeEntry.table_name}.project_id",
:klass => Project,
:label => :label_project},
'version' => {:sql => "#{Issue.table_name}.fixed_version_id",
:klass => Version,
:label => :label_version},
'category' => {:sql => "#{Issue.table_name}.category_id",
:klass => IssueCategory,
:label => :field_category},
'member' => {:sql => "#{TimeEntry.table_name}.user_id",
:klass => User,
:label => :label_member},
'tracker' => {:sql => "#{Issue.table_name}.tracker_id",
:klass => Tracker,
:label => :label_tracker},
'activity' => {:sql => "#{TimeEntry.table_name}.activity_id",
:klass => TimeEntryActivity,
:label => :label_activity},
'issue' => {:sql => "#{TimeEntry.table_name}.issue_id",
:klass => Issue,
:label => :label_issue}
}
# Add list and boolean custom fields as available criterias
custom_fields = (@project.nil? ? IssueCustomField.for_all : @project.all_issue_custom_fields)
custom_fields.select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
@available_criterias["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'Issue' AND c.customized_id = #{Issue.table_name}.id)",
:format => cf.field_format,
:label => cf.name}
end if @project
# Add list and boolean time entry custom fields
TimeEntryCustomField.find(:all).select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
@available_criterias["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'TimeEntry' AND c.customized_id = #{TimeEntry.table_name}.id)",
:format => cf.field_format,
:label => cf.name}
end
# Add list and boolean time entry activity custom fields
TimeEntryActivityCustomField.find(:all).select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
@available_criterias["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'Enumeration' AND c.customized_id = #{TimeEntry.table_name}.activity_id)",
:format => cf.field_format,
:label => cf.name}
end
call_hook(:controller_timelog_available_criterias, { :available_criterias => @available_criterias, :project => @project })
@available_criterias
end
def time_report_joins
sql = ''
sql << " LEFT JOIN #{Issue.table_name} ON #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id"
sql << " LEFT JOIN #{Project.table_name} ON #{TimeEntry.table_name}.project_id = #{Project.table_name}.id"
# TODO: rename hook
call_hook(:controller_timelog_time_report_joins, {:sql => sql} )
sql
end
end

View File

@@ -1,5 +1,5 @@
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
# Redmine - project management software
# Copyright (C) 2006-2010 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,10 +17,11 @@
class TimelogController < ApplicationController
menu_item :issues
before_filter :find_project, :authorize, :only => [:edit, :destroy]
before_filter :find_optional_project, :only => [:report, :details]
verify :method => :post, :only => :destroy, :redirect_to => { :action => :details }
before_filter :find_project, :only => [:new, :create]
before_filter :find_time_entry, :only => [:show, :edit, :update, :destroy]
before_filter :authorize, :except => [:index]
before_filter :find_optional_project, :only => [:index]
accept_key_auth :index, :show, :create, :update, :destroy
helper :sort
include SortHelper
@@ -29,129 +30,7 @@ class TimelogController < ApplicationController
helper :custom_fields
include CustomFieldsHelper
def report
@available_criterias = { 'project' => {:sql => "#{TimeEntry.table_name}.project_id",
:klass => Project,
:label => :label_project},
'version' => {:sql => "#{Issue.table_name}.fixed_version_id",
:klass => Version,
:label => :label_version},
'category' => {:sql => "#{Issue.table_name}.category_id",
:klass => IssueCategory,
:label => :field_category},
'member' => {:sql => "#{TimeEntry.table_name}.user_id",
:klass => User,
:label => :label_member},
'tracker' => {:sql => "#{Issue.table_name}.tracker_id",
:klass => Tracker,
:label => :label_tracker},
'activity' => {:sql => "#{TimeEntry.table_name}.activity_id",
:klass => TimeEntryActivity,
:label => :label_activity},
'issue' => {:sql => "#{TimeEntry.table_name}.issue_id",
:klass => Issue,
:label => :label_issue}
}
# Add list and boolean custom fields as available criterias
custom_fields = (@project.nil? ? IssueCustomField.for_all : @project.all_issue_custom_fields)
custom_fields.select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
@available_criterias["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'Issue' AND c.customized_id = #{Issue.table_name}.id)",
:format => cf.field_format,
:label => cf.name}
end if @project
# Add list and boolean time entry custom fields
TimeEntryCustomField.find(:all).select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
@available_criterias["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'TimeEntry' AND c.customized_id = #{TimeEntry.table_name}.id)",
:format => cf.field_format,
:label => cf.name}
end
# Add list and boolean time entry activity custom fields
TimeEntryActivityCustomField.find(:all).select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
@available_criterias["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'Enumeration' AND c.customized_id = #{TimeEntry.table_name}.activity_id)",
:format => cf.field_format,
:label => cf.name}
end
@criterias = params[:criterias] || []
@criterias = @criterias.select{|criteria| @available_criterias.has_key? criteria}
@criterias.uniq!
@criterias = @criterias[0,3]
@columns = (params[:columns] && %w(year month week day).include?(params[:columns])) ? params[:columns] : 'month'
retrieve_date_range
unless @criterias.empty?
sql_select = @criterias.collect{|criteria| @available_criterias[criteria][:sql] + " AS " + criteria}.join(', ')
sql_group_by = @criterias.collect{|criteria| @available_criterias[criteria][:sql]}.join(', ')
sql_condition = ''
if @project.nil?
sql_condition = Project.allowed_to_condition(User.current, :view_time_entries)
elsif @issue.nil?
sql_condition = @project.project_condition(Setting.display_subprojects_issues?)
else
sql_condition = "#{TimeEntry.table_name}.issue_id = #{@issue.id}"
end
sql = "SELECT #{sql_select}, tyear, tmonth, tweek, spent_on, SUM(hours) AS hours"
sql << " FROM #{TimeEntry.table_name}"
sql << " LEFT JOIN #{Issue.table_name} ON #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id"
sql << " LEFT JOIN #{Project.table_name} ON #{TimeEntry.table_name}.project_id = #{Project.table_name}.id"
sql << " WHERE"
sql << " (%s) AND" % sql_condition
sql << " (spent_on BETWEEN '%s' AND '%s')" % [ActiveRecord::Base.connection.quoted_date(@from), ActiveRecord::Base.connection.quoted_date(@to)]
sql << " GROUP BY #{sql_group_by}, tyear, tmonth, tweek, spent_on"
@hours = ActiveRecord::Base.connection.select_all(sql)
@hours.each do |row|
case @columns
when 'year'
row['year'] = row['tyear']
when 'month'
row['month'] = "#{row['tyear']}-#{row['tmonth']}"
when 'week'
row['week'] = "#{row['tyear']}-#{row['tweek']}"
when 'day'
row['day'] = "#{row['spent_on']}"
end
end
@total_hours = @hours.inject(0) {|s,k| s = s + k['hours'].to_f}
@periods = []
# Date#at_beginning_of_ not supported in Rails 1.2.x
date_from = @from.to_time
# 100 columns max
while date_from <= @to.to_time && @periods.length < 100
case @columns
when 'year'
@periods << "#{date_from.year}"
date_from = (date_from + 1.year).at_beginning_of_year
when 'month'
@periods << "#{date_from.year}-#{date_from.month}"
date_from = (date_from + 1.month).at_beginning_of_month
when 'week'
@periods << "#{date_from.year}-#{date_from.to_date.cweek}"
date_from = (date_from + 7.day).at_beginning_of_week
when 'day'
@periods << "#{date_from.to_date}"
date_from = date_from + 1.day
end
end
end
respond_to do |format|
format.html { render :layout => !request.xhr? }
format.csv { send_data(report_to_csv(@criterias, @periods, @hours), :type => 'text/csv; header=present', :filename => 'timelog.csv') }
end
end
def details
def index
sort_init 'spent_on', 'desc'
sort_update 'spent_on' => 'spent_on',
'user' => 'user_id',
@@ -166,7 +45,7 @@ class TimelogController < ApplicationController
elsif @issue.nil?
cond << @project.project_condition(Setting.display_subprojects_issues?)
else
cond << ["#{TimeEntry.table_name}.issue_id = ?", @issue.id]
cond << "#{Issue.table_name}.root_id = #{@issue.root_id} AND #{Issue.table_name}.lft >= #{@issue.lft} AND #{Issue.table_name}.rgt <= #{@issue.rgt}"
end
retrieve_date_range
@@ -176,7 +55,7 @@ class TimelogController < ApplicationController
respond_to do |format|
format.html {
# Paginate results
@entry_count = TimeEntry.count(:include => :project, :conditions => cond.conditions)
@entry_count = TimeEntry.count(:include => [:project, :issue], :conditions => cond.conditions)
@entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
@entries = TimeEntry.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
@@ -184,10 +63,20 @@ class TimelogController < ApplicationController
:order => sort_clause,
:limit => @entry_pages.items_per_page,
:offset => @entry_pages.current.offset)
@total_hours = TimeEntry.sum(:hours, :include => :project, :conditions => cond.conditions).to_f
@total_hours = TimeEntry.sum(:hours, :include => [:project, :issue], :conditions => cond.conditions).to_f
render :layout => !request.xhr?
}
format.api {
@entry_count = TimeEntry.count(:include => [:project, :issue], :conditions => cond.conditions)
@entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
@entries = TimeEntry.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
:conditions => cond.conditions,
:order => sort_clause,
:limit => @entry_pages.items_per_page,
:offset => @entry_pages.current.offset)
}
format.atom {
entries = TimeEntry.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
@@ -208,40 +97,114 @@ class TimelogController < ApplicationController
end
end
def edit
(render_403; return) if @time_entry && !@time_entry.editable_by?(User.current)
def show
respond_to do |format|
# TODO: Implement html response
format.html { render :nothing => true, :status => 406 }
format.api
end
end
def new
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
@time_entry.attributes = params[:time_entry]
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
render :action => 'edit'
end
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
def create
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
@time_entry.attributes = params[:time_entry]
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
if request.post? and @time_entry.save
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :action => 'details', :project_id => @time_entry.project
return
if @time_entry.save
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
}
format.api { render :action => 'show', :status => :created, :location => time_entry_url(@time_entry) }
end
else
respond_to do |format|
format.html { render :action => 'edit' }
format.api { render_validation_errors(@time_entry) }
end
end
end
def edit
@time_entry.attributes = params[:time_entry]
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
end
verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
def update
@time_entry.attributes = params[:time_entry]
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
if @time_entry.save
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
}
format.api { head :ok }
end
else
respond_to do |format|
format.html { render :action => 'edit' }
format.api { render_validation_errors(@time_entry) }
end
end
end
verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
def destroy
(render_404; return) unless @time_entry
(render_403; return) unless @time_entry.editable_by?(User.current)
@time_entry.destroy
flash[:notice] = l(:notice_successful_delete)
redirect_to :back
if @time_entry.destroy && @time_entry.destroyed?
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_delete)
redirect_to :back
}
format.api { head :ok }
end
else
respond_to do |format|
format.html {
flash[:error] = l(:notice_unable_delete_time_entry)
redirect_to :back
}
format.api { render_validation_errors(@time_entry) }
end
end
rescue ::ActionController::RedirectBackError
redirect_to :action => 'details', :project_id => @time_entry.project
redirect_to :action => 'index', :project_id => @time_entry.project
end
private
def find_time_entry
@time_entry = TimeEntry.find(params[:id])
unless @time_entry.editable_by?(User.current)
render_403
return false
end
@project = @time_entry.project
rescue ActiveRecord::RecordNotFound
render_404
end
def find_project
if params[:id]
@time_entry = TimeEntry.find(params[:id])
@project = @time_entry.project
elsif params[:issue_id]
@issue = Issue.find(params[:issue_id])
if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).present?
@issue = Issue.find(issue_id)
@project = @issue.project
elsif params[:project_id]
@project = Project.find(params[:project_id])
elsif (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present?
@project = Project.find(project_id)
else
render_404
return false
@@ -302,7 +265,8 @@ private
end
@from, @to = @to, @from if @from && @to && @from > @to
@from ||= (TimeEntry.minimum(:spent_on, :include => :project, :conditions => Project.allowed_to_condition(User.current, :view_time_entries)) || Date.today) - 1
@to ||= (TimeEntry.maximum(:spent_on, :include => :project, :conditions => Project.allowed_to_condition(User.current, :view_time_entries)) || Date.today)
@from ||= (TimeEntry.earilest_date_for_project(@project) || Date.today)
@to ||= (TimeEntry.latest_date_for_project(@project) || Date.today)
end
end

View File

@@ -20,16 +20,11 @@ class TrackersController < ApplicationController
before_filter :require_admin
def index
list
render :action => 'list' unless request.xhr?
end
verify :method => :post, :only => :destroy, :redirect_to => { :action => :list }
verify :method => :post, :only => :destroy, :redirect_to => { :action => :index }
def list
def index
@tracker_pages, @trackers = paginate :trackers, :per_page => 10, :order => 'position'
render :action => "list", :layout => false if request.xhr?
render :action => "index", :layout => false if request.xhr?
end
def new
@@ -40,7 +35,7 @@ class TrackersController < ApplicationController
@tracker.workflows.copy(copy_from)
end
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'list'
redirect_to :action => 'index'
return
end
@trackers = Tracker.find :all, :order => 'position'
@@ -51,7 +46,7 @@ class TrackersController < ApplicationController
@tracker = Tracker.find(params[:id])
if request.post? and @tracker.update_attributes(params[:tracker])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'list'
redirect_to :action => 'index'
return
end
@projects = Project.find(:all)
@@ -60,10 +55,10 @@ class TrackersController < ApplicationController
def destroy
@tracker = Tracker.find(params[:id])
unless @tracker.issues.empty?
flash[:error] = "This tracker contains issues and can\'t be deleted."
flash[:error] = l(:error_can_not_delete_tracker)
else
@tracker.destroy
end
redirect_to :action => 'list'
redirect_to :action => 'index'
end
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2009 Jean-Philippe Lang
# Copyright (C) 2006-2010 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -19,6 +19,8 @@ class UsersController < ApplicationController
layout 'admin'
before_filter :require_admin, :except => :show
before_filter :find_user, :only => [:show, :edit, :update, :edit_membership, :destroy_membership]
accept_key_auth :index, :show, :create, :update
helper :sort
include SortHelper
@@ -29,6 +31,13 @@ class UsersController < ApplicationController
sort_init 'login', 'asc'
sort_update %w(login firstname lastname mail admin created_on last_login_on)
case params[:format]
when 'xml', 'json'
@offset, @limit = api_offset_and_limit
else
@limit = per_page_option
end
@status = params[:status] ? params[:status].to_i : 1
c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
@@ -38,102 +47,159 @@ class UsersController < ApplicationController
end
@user_count = User.count(:conditions => c.conditions)
@user_pages = Paginator.new self, @user_count,
per_page_option,
params['page']
@users = User.find :all,:order => sort_clause,
@user_pages = Paginator.new self, @user_count, @limit, params['page']
@offset ||= @user_pages.current.offset
@users = User.find :all,
:order => sort_clause,
:conditions => c.conditions,
:limit => @user_pages.items_per_page,
:offset => @user_pages.current.offset
:limit => @limit,
:offset => @offset
render :layout => !request.xhr?
respond_to do |format|
format.html { render :layout => !request.xhr? }
format.api
end
end
def show
@user = User.active.find(params[:id])
@custom_values = @user.custom_values
# show only public projects and private projects that the logged in user is also a member of
@memberships = @user.memberships.select do |membership|
membership.project.is_public? || (User.current.member_of?(membership.project))
end
# show projects based on current user visibility
@memberships = @user.memberships.all(:conditions => Project.visible_by(User.current))
events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
@events_by_day = events.group_by(&:event_date)
if @user != User.current && !User.current.admin? && @memberships.empty? && events.empty?
render_404
return
end
render :layout => 'base'
rescue ActiveRecord::RecordNotFound
render_404
end
def add
if request.get?
@user = User.new(:language => Setting.default_language)
else
@user = User.new(params[:user])
@user.admin = params[:user][:admin] || false
@user.login = params[:user][:login]
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless @user.auth_source_id
if @user.save
Mailer.deliver_account_information(@user, params[:password]) if params[:send_information]
flash[:notice] = l(:notice_successful_create)
redirect_to(params[:continue] ? {:controller => 'users', :action => 'add'} :
{:controller => 'users', :action => 'edit', :id => @user})
unless User.current.admin?
if !@user.active? || (@user != User.current && @memberships.empty? && events.empty?)
render_404
return
end
end
respond_to do |format|
format.html { render :layout => 'base' }
format.api
end
end
def new
@user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
@auth_sources = AuthSource.find(:all)
end
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
def create
@user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
@user.safe_attributes = params[:user]
@user.admin = params[:user][:admin] || false
@user.login = params[:user][:login]
@user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] unless @user.auth_source_id
# TODO: Similar to My#account
@user.pref.attributes = params[:pref]
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
if @user.save
@user.pref.save
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
Mailer.deliver_account_information(@user, params[:user][:password]) if params[:send_information]
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_create)
redirect_to(params[:continue] ?
{:controller => 'users', :action => 'new'} :
{:controller => 'users', :action => 'edit', :id => @user}
)
}
format.api { render :action => 'show', :status => :created, :location => user_url(@user) }
end
else
@auth_sources = AuthSource.find(:all)
# Clear password input
@user.password = @user.password_confirmation = nil
respond_to do |format|
format.html { render :action => 'new' }
format.api { render_validation_errors(@user) }
end
end
end
def edit
@user = User.find(params[:id])
if request.post?
@user.admin = params[:user][:admin] if params[:user][:admin]
@user.login = params[:user][:login] if params[:user][:login]
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless params[:password].nil? or params[:password].empty? or @user.auth_source_id
@user.group_ids = params[:user][:group_ids] if params[:user][:group_ids]
@user.attributes = params[:user]
# Was the account actived ? (do it before User#save clears the change)
was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
if @user.save
if was_activated
Mailer.deliver_account_activated(@user)
elsif @user.active? && params[:send_information] && !params[:password].blank? && @user.auth_source_id.nil?
Mailer.deliver_account_information(@user, params[:password])
end
flash[:notice] = l(:notice_successful_update)
redirect_to :back
end
end
@auth_sources = AuthSource.find(:all)
@membership ||= Member.new
end
verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
def update
@user.admin = params[:user][:admin] if params[:user][:admin]
@user.login = params[:user][:login] if params[:user][:login]
if params[:user][:password].present? && (@user.auth_source_id.nil? || params[:user][:auth_source_id].blank?)
@user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation]
end
@user.safe_attributes = params[:user]
# Was the account actived ? (do it before User#save clears the change)
was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
# TODO: Similar to My#account
@user.pref.attributes = params[:pref]
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
if @user.save
@user.pref.save
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
if was_activated
Mailer.deliver_account_activated(@user)
elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.auth_source_id.nil?
Mailer.deliver_account_information(@user, params[:user][:password])
end
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_to :back
}
format.api { head :ok }
end
else
@auth_sources = AuthSource.find(:all)
@membership ||= Member.new
# Clear password input
@user.password = @user.password_confirmation = nil
respond_to do |format|
format.html { render :action => :edit }
format.api { render_validation_errors(@user) }
end
end
rescue ::ActionController::RedirectBackError
redirect_to :controller => 'users', :action => 'edit', :id => @user
end
def edit_membership
@user = User.find(params[:id])
@membership = params[:membership_id] ? Member.find(params[:membership_id]) : Member.new(:principal => @user)
@membership.attributes = params[:membership]
@membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
@membership.save if request.post?
respond_to do |format|
format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
format.js {
render(:update) {|page|
page.replace_html "tab-content-memberships", :partial => 'users/memberships'
page.visual_effect(:highlight, "member-#{@membership.id}")
}
}
end
if @membership.valid?
format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
format.js {
render(:update) {|page|
page.replace_html "tab-content-memberships", :partial => 'users/memberships'
page.visual_effect(:highlight, "member-#{@membership.id}")
}
}
else
format.js {
render(:update) {|page|
page.alert(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', ')))
}
}
end
end
end
def destroy_membership
@user = User.find(params[:id])
@membership = Member.find(params[:membership_id])
if request.post? && @membership.deletable?
@membership.destroy
@@ -143,4 +209,17 @@ class UsersController < ApplicationController
format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'users/memberships'} }
end
end
private
def find_user
if params[:id] == 'current'
require_login || return
@user = User.current
else
@user = User.find(params[:id])
end
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

@@ -17,40 +17,121 @@
class VersionsController < ApplicationController
menu_item :roadmap
before_filter :find_version, :except => :close_completed
before_filter :find_project, :only => :close_completed
model_object Version
before_filter :find_model_object, :except => [:index, :new, :create, :close_completed]
before_filter :find_project_from_association, :except => [:index, :new, :create, :close_completed]
before_filter :find_project, :only => [:index, :new, :create, :close_completed]
before_filter :authorize
helper :custom_fields
helper :projects
def show
def index
@trackers = @project.trackers.find(:all, :order => 'position')
retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?})
@with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
@versions = @project.shared_versions || []
@versions += @project.rolled_up_versions.visible if @with_subprojects
@versions = @versions.uniq.sort
@versions.reject! {|version| version.closed? || version.completed? } unless params[:completed]
@issues_by_version = {}
unless @selected_tracker_ids.empty?
@versions.each do |version|
issues = version.fixed_issues.visible.find(:all,
:include => [:project, :status, :tracker, :priority],
:conditions => {:tracker_id => @selected_tracker_ids, :project_id => project_ids},
:order => "#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id")
@issues_by_version[version] = issues
end
end
@versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?}
end
def show
@issues = @version.fixed_issues.visible.find(:all,
:include => [:status, :tracker, :priority],
:order => "#{Tracker.table_name}.position, #{Issue.table_name}.id")
end
def new
@version = @project.versions.build
if params[:version]
attributes = params[:version].dup
attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing'])
@version.attributes = attributes
end
end
def create
# TODO: refactor with code above in #new
@version = @project.versions.build
if params[:version]
attributes = params[:version].dup
attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing'])
@version.attributes = attributes
end
if request.post?
if @version.save
respond_to do |format|
format.html do
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
end
format.js do
# IE doesn't support the replace_html rjs method for select box options
render(:update) {|page| page.replace "issue_fixed_version_id",
content_tag('select', '<option></option>' + version_options_for_select(@project.shared_versions.open, @version), :id => 'issue_fixed_version_id', :name => 'issue[fixed_version_id]')
}
end
end
else
respond_to do |format|
format.html { render :action => 'new' }
format.js do
render(:update) {|page| page.alert(@version.errors.full_messages.join('\n')) }
end
end
end
end
end
def edit
if request.post? && params[:version]
end
def update
if request.put? && params[:version]
attributes = params[:version].dup
attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing'])
if @version.update_attributes(attributes)
flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
else
respond_to do |format|
format.html { render :action => 'edit' }
end
end
end
end
def close_completed
if request.post?
if request.put?
@project.close_completed_versions
end
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
end
def destroy
@version.destroy
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
rescue
flash[:error] = l(:notice_unable_delete_version)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
if @version.fixed_issues.empty?
@version.destroy
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
else
flash[:error] = l(:notice_unable_delete_version)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
end
end
def status_by
@@ -61,16 +142,18 @@ class VersionsController < ApplicationController
end
private
def find_version
@version = Version.find(params[:id])
@project = @version.project
rescue ActiveRecord::RecordNotFound
render_404
end
def find_project
@project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound
render_404
end
def retrieve_selected_tracker_ids(selectable_trackers, default_trackers=nil)
if ids = params[:tracker_ids]
@selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
else
@selected_tracker_ids = (default_trackers || selectable_trackers).collect {|t| t.id.to_s }
end
end
end

View File

@@ -83,7 +83,7 @@ private
replace_ids = [params[:replace]]
end
else
replace_ids = 'watcher'
replace_ids = ['watcher']
end
respond_to do |format|
format.html { redirect_to :back }

View File

@@ -17,23 +17,42 @@
require 'diff'
# The WikiController follows the Rails REST controller pattern but with
# a few differences
#
# * index - shows a list of WikiPages grouped by page or date
# * new - not used
# * create - not used
# * show - will also show the form for creating a new wiki page
# * edit - used to edit an existing or new page
# * update - used to save a wiki page update to the database, including new pages
# * destroy - normal
#
# Other member and collection methods are also used
#
# TODO: still being worked on
class WikiController < ApplicationController
default_search_scope :wiki_pages
before_filter :find_wiki, :authorize
before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy]
verify :method => :post, :only => [:destroy, :protect], :redirect_to => { :action => :index }
verify :method => :post, :only => [:protect], :redirect_to => { :action => :show }
helper :attachments
include AttachmentsHelper
helper :watchers
# display a page (in editing mode if it doesn't exist)
# List of pages, sorted alphabetically and by parent (hierarchy)
def index
page_title = params[:page]
load_pages_grouped_by_date_without_content
end
# display a page (in editing mode if it doesn't exist)
def show
page_title = params[:id]
@page = @wiki.find_or_new_page(page_title)
if @page.new_record?
if User.current.allowed_to?(:edit_wiki_pages, @project)
if User.current.allowed_to?(:edit_wiki_pages, @project) && editable?
edit
render :action => 'edit'
else
@@ -47,21 +66,23 @@ class WikiController < ApplicationController
return
end
@content = @page.content_for_version(params[:version])
if params[:format] == 'html'
export = render_to_string :action => 'export', :layout => false
send_data(export, :type => 'text/html', :filename => "#{@page.title}.html")
return
elsif params[:format] == 'txt'
send_data(@content.text, :type => 'text/plain', :filename => "#{@page.title}.txt")
return
if User.current.allowed_to?(:export_wiki_pages, @project)
if params[:format] == 'html'
export = render_to_string :action => 'export', :layout => false
send_data(export, :type => 'text/html', :filename => "#{@page.title}.html")
return
elsif params[:format] == 'txt'
send_data(@content.text, :type => 'text/plain', :filename => "#{@page.title}.txt")
return
end
end
@editable = editable?
@editable = editable?
render :action => 'show'
end
# edit an existing page or a new one
def edit
@page = @wiki.find_or_new_page(params[:page])
@page = @wiki.find_or_new_page(params[:id])
return render_403 unless editable?
@page.content = WikiContent.new(:page => @page) if @page.new_record?
@@ -69,30 +90,50 @@ class WikiController < ApplicationController
@content.text = initial_page_content(@page) if @content.text.blank?
# don't keep previous comment
@content.comments = nil
if request.get?
# To prevent StaleObjectError exception when reverting to a previous version
@content.version = @page.content.version
else
if !@page.new_record? && @content.text == params[:content][:text]
# don't save if text wasn't changed
redirect_to :action => 'index', :id => @project, :page => @page.title
return
end
#@content.text = params[:content][:text]
#@content.comments = params[:content][:comments]
@content.attributes = params[:content]
@content.author = User.current
# if page is new @page.save will also save content, but not if page isn't a new record
if (@page.new_record? ? @page.save : @content.save)
call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
redirect_to :action => 'index', :id => @project, :page => @page.title
end
end
# To prevent StaleObjectError exception when reverting to a previous version
@content.version = @page.content.version
rescue ActiveRecord::StaleObjectError
# Optimistic locking exception
flash[:error] = l(:notice_locking_conflict)
end
verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
# Creates a new page or updates an existing one
def update
@page = @wiki.find_or_new_page(params[:id])
return render_403 unless editable?
@page.content = WikiContent.new(:page => @page) if @page.new_record?
@content = @page.content_for_version(params[:version])
@content.text = initial_page_content(@page) if @content.text.blank?
# don't keep previous comment
@content.comments = nil
if !@page.new_record? && params[:content].present? && @content.text == params[:content][:text]
attachments = Attachment.attach_files(@page, params[:attachments])
render_attachment_warning_if_needed(@page)
# don't save if text wasn't changed
redirect_to :action => 'show', :project_id => @project, :id => @page.title
return
end
@content.attributes = params[:content]
@content.author = User.current
# if page is new @page.save will also save content, but not if page isn't a new record
if (@page.new_record? ? @page.save : @content.save)
attachments = Attachment.attach_files(@page, params[:attachments])
render_attachment_warning_if_needed(@page)
call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
redirect_to :action => 'show', :project_id => @project, :id => @page.title
else
render :action => 'edit'
end
rescue ActiveRecord::StaleObjectError
# Optimistic locking exception
flash[:error] = l(:notice_locking_conflict)
end
# rename a page
def rename
return render_403 unless editable?
@@ -101,13 +142,13 @@ class WikiController < ApplicationController
@original_title = @page.pretty_title
if request.post? && @page.update_attributes(params[:wiki_page])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'index', :id => @project, :page => @page.title
redirect_to :action => 'show', :project_id => @project, :id => @page.title
end
end
def protect
@page.update_attribute :protected, params[:protected]
redirect_to :action => 'index', :id => @project, :page => @page.title
redirect_to :action => 'show', :project_id => @project, :id => @page.title
end
# show page history
@@ -133,7 +174,8 @@ class WikiController < ApplicationController
@annotate = @page.annotate(params[:version])
render_404 unless @annotate
end
verify :method => :delete, :only => [:destroy], :redirect_to => { :action => :show }
# Removes a wiki page and its history
# Children can be either set as root pages, removed or reassigned to another parent page
def destroy
@@ -160,37 +202,26 @@ class WikiController < ApplicationController
end
end
@page.destroy
redirect_to :action => 'special', :id => @project, :page => 'Page_index'
redirect_to :action => 'index', :project_id => @project
end
# display special pages
def special
page_title = params[:page].downcase
case page_title
# show pages index, sorted by title
when 'page_index', 'date_index'
# eager load information about last updates, without loading text
@pages = @wiki.pages.find :all, :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",
:order => 'title'
@pages_by_date = @pages.group_by {|p| p.updated_on.to_date}
@pages_by_parent_id = @pages.group_by(&:parent_id)
# export wiki to a single html file
when 'export'
# Export wiki to a single html file
def export
if User.current.allowed_to?(:export_wiki_pages, @project)
@pages = @wiki.pages.find :all, :order => 'title'
export = render_to_string :action => 'export_multiple', :layout => false
send_data(export, :type => 'text/html', :filename => "wiki.html")
return
else
# requested special page doesn't exist, redirect to default page
redirect_to :action => 'index', :id => @project, :page => nil
return
redirect_to :action => 'show', :project_id => @project, :id => nil
end
render :action => "special_#{page_title}"
end
def date_index
load_pages_grouped_by_date_without_content
end
def preview
page = @wiki.find_page(params[:page])
page = @wiki.find_page(params[:id])
# page is nil when previewing a new page
return render_403 unless page.nil? || editable?(page)
if page
@@ -203,14 +234,15 @@ class WikiController < ApplicationController
def add_attachment
return render_403 unless editable?
attach_files(@page, params[:attachments])
redirect_to :action => 'index', :page => @page.title
attachments = Attachment.attach_files(@page, params[:attachments])
render_attachment_warning_if_needed(@page)
redirect_to :action => 'show', :id => @page.title, :project_id => @project
end
private
def find_wiki
@project = Project.find(params[:id])
@project = Project.find(params[:project_id])
@wiki = @project.wiki
render_404 unless @wiki
rescue ActiveRecord::RecordNotFound
@@ -219,7 +251,7 @@ private
# Finds the requested page and returns a 404 error if it doesn't exist
def find_existing_page
@page = @wiki.find_page(params[:page])
@page = @wiki.find_page(params[:id])
render_404 if @page.nil?
end
@@ -234,4 +266,14 @@ private
extend helper unless self.instance_of?(helper)
helper.instance_method(:initial_page_content).bind(self).call(page)
end
# eager load information about last updates, without loading text
def load_pages_grouped_by_date_without_content
@pages = @wiki.pages.find :all, :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",
:order => 'title'
@pages_by_date = @pages.group_by {|p| p.updated_on.to_date}
@pages_by_parent_id = @pages.group_by(&:parent_id)
end
end

View File

@@ -34,11 +34,4 @@ class WikisController < ApplicationController
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'wiki'
end
end
private
def find_project
@project = Project.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

@@ -19,7 +19,9 @@ class WorkflowsController < ApplicationController
layout 'admin'
before_filter :require_admin
before_filter :find_roles
before_filter :find_trackers
def index
@workflow_counts = Workflow.count_by_tracker_and_role
end
@@ -40,8 +42,6 @@ class WorkflowsController < ApplicationController
redirect_to :action => 'edit', :role_id => @role, :tracker_id => @tracker
end
end
@roles = Role.find(:all, :order => 'builtin, position')
@trackers = Tracker.find(:all, :order => 'position')
@used_statuses_only = (params[:used_statuses_only] == '0' ? false : true)
if @tracker && @used_statuses_only && @tracker.issue_statuses.any?
@@ -51,8 +51,6 @@ class WorkflowsController < ApplicationController
end
def copy
@trackers = Tracker.find(:all, :order => 'position')
@roles = Role.find(:all, :order => 'builtin, position')
if params[:source_tracker_id].blank? || params[:source_tracker_id] == 'any'
@source_tracker = nil
@@ -80,4 +78,14 @@ class WorkflowsController < ApplicationController
end
end
end
private
def find_roles
@roles = Role.find(:all, :order => 'builtin, position')
end
def find_trackers
@trackers = Tracker.find(:all, :order => 'position')
end
end

View File

@@ -20,12 +20,4 @@ module AdminHelper
options_for_select([[l(:label_all), ''],
[l(:status_active), 1]], selected)
end
def css_project_classes(project)
s = 'project'
s << ' root' if project.root?
s << ' child' if project.child?
s << (project.leaf? ? ' leaf' : ' parent')
s
end
end

View File

@@ -1,5 +1,5 @@
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
# Redmine - project management software
# Copyright (C) 2006-2010 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -15,8 +15,6 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'coderay'
require 'coderay/helpers/file_type'
require 'forwardable'
require 'cgi'
@@ -34,6 +32,11 @@ module ApplicationHelper
end
# Display a link if user is authorized
#
# @param [String] name Anchor text (passed to link_to)
# @param [Hash] options Hash params. This will checked by authorize_for to see if the user is authorized
# @param [optional, Hash] html_options Options passed to link_to
# @param [optional, Hash] parameters_for_method_reference Extra parameters for link_to
def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
end
@@ -101,8 +104,27 @@ module ApplicationHelper
# * :text - Link text (default to the formatted revision)
def link_to_revision(revision, project, options={})
text = options.delete(:text) || format_revision(revision)
rev = revision.respond_to?(:identifier) ? revision.identifier : revision
link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => revision}, :title => l(:label_revision_id, revision))
link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => rev},
:title => l(:label_revision_id, format_revision(revision)))
end
# Generates a link to a project if active
# Examples:
#
# link_to_project(project) # => link to the specified project overview
# link_to_project(project, :action=>'settings') # => link to project settings
# link_to_project(project, {:only_path => false}, :class => "project") # => 3rd arg adds html options
# link_to_project(project, {}, :class => "project") # => html options with default url (project overview)
#
def link_to_project(project, options={}, html_options = nil)
if project.active?
url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
link_to(h(project), url, html_options)
else
h(project)
end
end
def toggle_link(name, id, options={})
@@ -157,7 +179,7 @@ module ApplicationHelper
content << "<ul class=\"pages-hierarchy\">\n"
pages[node].each do |page|
content << "<li>"
content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'index', :id => page.project, :page => page.title},
content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title},
:title => (page.respond_to?(: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) if pages[page.id]
content << "</li>\n"
@@ -205,7 +227,12 @@ module ApplicationHelper
s = ''
project_tree(projects) do |project, level|
name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '')
tag_options = {:value => project.id, :selected => ((project == options[:selected]) ? 'selected' : nil)}
tag_options = {:value => project.id}
if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project))
tag_options[:selected] = 'selected'
else
tag_options[:selected] = nil
end
tag_options.merge!(yield(project)) if block_given?
s << content_tag('option', name_prefix + h(project), tag_options)
end
@@ -213,15 +240,10 @@ module ApplicationHelper
end
# Yields the given block for each project with its level in the tree
#
# Wrapper for Project#project_tree
def project_tree(projects, &block)
ancestors = []
projects.sort_by(&:lft).each do |project|
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
ancestors.pop
end
yield project, ancestors.size
ancestors << project
end
Project.project_tree(projects, &block)
end
def project_nested_ul(projects, &block)
@@ -260,6 +282,16 @@ module ApplicationHelper
def truncate_single_line(string, *args)
truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
end
# Truncates at line break after 250 characters or options[:length]
def truncate_lines(string, options={})
length = options[:length] || 250
if string.to_s =~ /\A(.{#{length}}.*?)$/m
"#{$1}..."
else
string
end
end
def html_hours(text)
text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
@@ -272,15 +304,14 @@ module ApplicationHelper
def time_tag(time)
text = distance_of_time_in_words(Time.now, time)
if @project
link_to(text, {:controller => 'projects', :action => 'activity', :id => @project, :from => time.to_date}, :title => format_time(time))
link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => time.to_date}, :title => format_time(time))
else
content_tag('acronym', text, :title => format_time(time))
end
end
def syntax_highlight(name, content)
type = CodeRay::FileType[name]
type ? CodeRay.scan(content, type).html : h(content)
Redmine::SyntaxHighlighting.highlight_by_filename(content, name)
end
def to_path_param(path)
@@ -289,6 +320,7 @@ module ApplicationHelper
def pagination_links_full(paginator, count=nil, options={})
page_param = options.delete(:page_param) || :page
per_page_links = options.delete(:per_page_links)
url_param = params.dup
# don't reuse query params if filters are present
url_param.merge!(:fields => nil, :values => nil, :operators => nil) if url_param.delete(:set_filter)
@@ -307,10 +339,10 @@ module ApplicationHelper
end
unless count.nil?
html << [
" (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})",
per_page_links(paginator.items_per_page)
].compact.join(' | ')
html << " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})"
if per_page_links != false && links = per_page_links(paginator.items_per_page)
html << " | #{links}"
end
end
html
@@ -355,12 +387,12 @@ module ApplicationHelper
ancestors = (@project.root? ? [] : @project.ancestors.visible)
if ancestors.any?
root = ancestors.shift
b << link_to(h(root), {:controller => 'projects', :action => 'show', :id => root, :jump => current_menu_item}, :class => 'root')
b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
if ancestors.size > 2
b << '&#8230;'
ancestors = ancestors[-2, 2]
end
b += ancestors.collect {|p| link_to(h(p), {:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item}, :class => 'ancestor') }
b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') }
end
b << h(@project)
b.join(' &#187; ')
@@ -380,6 +412,19 @@ module ApplicationHelper
end
end
# Returns the theme, controller name, and action as css classes for the
# HTML body.
def body_css_classes
css = []
if theme = Redmine::Themes.theme(Setting.ui_theme)
css << 'theme-' + theme.name
end
css << 'controller-' + params[:controller]
css << 'action-' + params[:action]
css.join(' ')
end
def accesskey(s)
Redmine::AccessKeys.key_for s
end
@@ -396,66 +441,99 @@ module ApplicationHelper
text = args.shift
when 2
obj = args.shift
text = obj.send(args.shift).to_s
attr = args.shift
text = obj.send(attr).to_s
else
raise ArgumentError, 'invalid arguments to textilizable'
end
return '' if text.blank?
project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
only_path = options.delete(:only_path) == false ? false : true
text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) { |macro, args| exec_macro(macro, obj, args) }
@parsed_headings = []
text = parse_non_pre_blocks(text) do |text|
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_headings].each do |method_name|
send method_name, text, project, obj, attr, only_path, options
end
end
if @parsed_headings.any?
replace_toc(text, @parsed_headings)
end
text
end
def parse_non_pre_blocks(text)
s = StringScanner.new(text)
tags = []
parsed = ''
while !s.eos?
s.scan(/(.*?)(<(\/)?(pre|code)(.*?)>|\z)/im)
text, full_tag, closing, tag = s[1], s[2], s[3], s[4]
if tags.empty?
yield text
end
parsed << text
if tag
if closing
if tags.last == tag.downcase
tags.pop
end
else
tags << tag.downcase
end
parsed << full_tag
end
end
# Close any non closing tags
while tag = tags.pop
parsed << "</#{tag}>"
end
parsed
end
def parse_inline_attachments(text, project, obj, attr, only_path, options)
# when using an image link, try to use an attachment, if possible
attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
if attachments
attachments = attachments.sort_by(&:created_on).reverse
text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(bmp|gif|jpg|jpeg|png))!/i) do |m|
style = $1
filename = $6.downcase
if options[:attachments] || (obj && obj.respond_to?(:attachments))
attachments = nil
text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
filename, ext, alt, alttext = $1.downcase, $2, $3, $4
attachments ||= (options[:attachments] || obj.attachments).sort_by(&:created_on).reverse
# search for the picture in attachments
if found = attachments.detect { |att| att.filename.downcase == filename }
image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
alt = desc.blank? ? nil : "(#{desc})"
"!#{style}#{image_url}#{alt}!"
desc = found.description.to_s.gsub('"', '')
if !desc.blank? && alttext.blank?
alt = " title=\"#{desc}\" alt=\"#{desc}\""
end
"src=\"#{image_url}\"#{alt}"
else
m
end
end
end
end
text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text) { |macro, args| exec_macro(macro, obj, args) }
# different methods for formatting wiki links
case options[:wiki_links]
when :local
# used for local links to html files
format_wiki_link = Proc.new {|project, title, anchor| "#{title}.html" }
when :anchor
# used for single-file wiki export
format_wiki_link = Proc.new {|project, title, anchor| "##{title}" }
else
format_wiki_link = Proc.new {|project, title, anchor| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title, :anchor => anchor) }
end
project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
# Wiki links
#
# Examples:
# [[mypage]]
# [[mypage|mytext]]
# wiki links can refer other project wikis, using project name or identifier:
# [[project:]] -> wiki starting page
# [[project:|mytext]]
# [[project:mypage]]
# [[project:mypage|mytext]]
text = text.gsub(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
# Wiki links
#
# Examples:
# [[mypage]]
# [[mypage|mytext]]
# wiki links can refer other project wikis, using project name or identifier:
# [[project:]] -> wiki starting page
# [[project:|mytext]]
# [[project:mypage]]
# [[project:mypage|mytext]]
def parse_wiki_links(text, project, obj, attr, only_path, options)
text.gsub!(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
link_project = project
esc, all, page, title = $1, $2, $3, $5
if esc.nil?
if page =~ /^([^\:]+)\:(.*)$/
link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
link_project = Project.find_by_identifier($1) || Project.find_by_name($1)
page = $2
title ||= $1 if page.blank?
end
@@ -468,8 +546,14 @@ module ApplicationHelper
end
# check if page exists
wiki_page = link_project.wiki.find_page(page)
link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page), anchor),
:class => ('wiki-page' + (wiki_page ? '' : ' new')))
url = case options[:wiki_links]
when :local; "#{title}.html"
when :anchor; "##{title}" # used for single-file wiki export
else
wiki_page_id = page.present? ? Wiki.titleize(page) : nil
url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project, :id => wiki_page_id, :anchor => anchor)
end
link_to((title || page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
else
# project or wiki doesn't exist
all
@@ -478,45 +562,47 @@ module ApplicationHelper
all
end
end
# Redmine links
#
# Examples:
# Issues:
# #52 -> Link to issue #52
# Changesets:
# r52 -> Link to revision 52
# commit:a85130f -> Link to scmid starting with a85130f
# Documents:
# document#17 -> Link to document with id 17
# document:Greetings -> Link to the document with title "Greetings"
# document:"Some document" -> Link to the document with title "Some document"
# Versions:
# version#3 -> Link to version with id 3
# version:1.0.0 -> Link to version named "1.0.0"
# version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
# Attachments:
# attachment:file.zip -> Link to the attachment of the current object named file.zip
# Source files:
# source:some/file -> Link to the file located at /some/file in the project's repository
# source:some/file@52 -> Link to the file's revision 52
# source:some/file#L120 -> Link to line 120 of the file
# source:some/file@52#L120 -> Link to line 120 of the file's revision 52
# export:some/file -> Force the download of the file
# Forum messages:
# message#1218 -> Link to message with id 1218
text = text.gsub(%r{([\s\(,\-\>]|^)(!)?(attachment|document|version|commit|source|export|message)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|<|$)}) do |m|
leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
end
# Redmine links
#
# Examples:
# Issues:
# #52 -> Link to issue #52
# Changesets:
# r52 -> Link to revision 52
# commit:a85130f -> Link to scmid starting with a85130f
# Documents:
# document#17 -> Link to document with id 17
# document:Greetings -> Link to the document with title "Greetings"
# document:"Some document" -> Link to the document with title "Some document"
# Versions:
# version#3 -> Link to version with id 3
# version:1.0.0 -> Link to version named "1.0.0"
# version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
# Attachments:
# attachment:file.zip -> Link to the attachment of the current object named file.zip
# Source files:
# source:some/file -> Link to the file located at /some/file in the project's repository
# source:some/file@52 -> Link to the file's revision 52
# source:some/file#L120 -> Link to line 120 of the file
# source:some/file@52#L120 -> Link to line 120 of the file's revision 52
# export:some/file -> Force the download of the file
# Forum messages:
# message#1218 -> Link to message with id 1218
def parse_redmine_links(text, project, obj, attr, only_path, options)
text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(attachment|document|version|commit|source|export|message|project)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|\]|<|$)}) do |m|
leading, esc, prefix, sep, identifier = $1, $2, $3, $5 || $7, $6 || $8
link = nil
if esc.nil?
if prefix.nil? && sep == 'r'
if project && (changeset = project.changesets.find_by_revision(oid))
link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => oid},
if project && (changeset = project.changesets.find_by_revision(identifier))
link = link_to("r#{identifier}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
:class => 'changeset',
:title => truncate_single_line(changeset.comments, :length => 100))
end
elsif sep == '#'
oid = oid.to_i
oid = identifier.to_i
case prefix
when nil
if issue = Issue.visible.find_by_id(oid, :include => :status)
@@ -544,10 +630,14 @@ module ApplicationHelper
:anchor => (message.parent ? "message-#{message.id}" : nil)},
:class => 'message'
end
when 'project'
if p = Project.visible.find_by_id(oid)
link = link_to_project(p, {:only_path => only_path}, :class => 'project')
end
end
elsif sep == ':'
# removes the double quotes if any
name = oid.gsub(%r{^"(.*)"$}, "\\1")
name = identifier.gsub(%r{^"(.*)"$}, "\\1")
case prefix
when 'document'
if project && document = project.documents.find_by_title(name)
@@ -577,17 +667,69 @@ module ApplicationHelper
:class => (prefix == 'export' ? 'source download' : 'source')
end
when 'attachment'
attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
if attachments && attachment = attachments.detect {|a| a.filename == name }
link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
:class => 'attachment'
end
when 'project'
if p = Project.visible.find(:first, :conditions => ["identifier = :s OR LOWER(name) = :s", {:s => name.downcase}])
link = link_to_project(p, {:only_path => only_path}, :class => 'project')
end
end
end
end
leading + (link || "#{prefix}#{sep}#{oid}")
leading + (link || "#{prefix}#{sep}#{identifier}")
end
end
HEADING_RE = /<h(1|2|3|4)( [^>]+)?>(.+?)<\/h(1|2|3|4)>/i unless const_defined?(:HEADING_RE)
# Headings and TOC
# Adds ids and links to headings unless options[:headings] is set to false
def parse_headings(text, project, obj, attr, only_path, options)
return if options[:headings] == false
text.gsub!(HEADING_RE) do
level, attrs, content = $1.to_i, $2, $3
item = strip_tags(content).strip
anchor = item.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
@parsed_headings << [level, anchor, item]
"<h#{level} #{attrs} id=\"#{anchor}\">#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a></h#{level}>"
end
end
TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
# Renders the TOC with given headings
def replace_toc(text, headings)
text.gsub!(TOC_RE) do
if headings.empty?
''
else
div_class = 'toc'
div_class << ' right' if $1 == '>'
div_class << ' left' if $1 == '<'
out = "<ul class=\"#{div_class}\"><li>"
root = headings.map(&:first).min
current = root
started = false
headings.each do |level, anchor, item|
if level > current
out << '<ul><li>' * (level - current)
elsif level < current
out << "</li></ul>\n" * (current - level) + "</li><li>"
elsif started
out << '</li><li>'
end
out << "<a href=\"##{anchor}\">#{item}</a>"
current = level
started = true
end
out << '</li></ul>' * (current - root)
out << '</li></ul>'
end
end
text
end
# Same as Rails' simple_format helper without using paragraphs
@@ -641,6 +783,28 @@ module ApplicationHelper
), :class => 'progress', :style => "width: #{width};") +
content_tag('p', legend, :class => 'pourcent')
end
def checked_image(checked=true)
if checked
image_tag 'toggle_check.png'
end
end
def context_menu(url)
unless @context_menu_included
content_for :header_tags do
javascript_include_tag('context_menu') +
stylesheet_link_tag('context_menu')
end
if l(:direction) == 'rtl'
content_for :header_tags do
stylesheet_link_tag('context_menu_rtl')
end
end
@context_menu_included = true
end
javascript_tag "new ContextMenu('#{ url_for(url) }')"
end
def context_menu_link(name, url, options={})
options[:class] ||= ''
@@ -700,7 +864,7 @@ module ApplicationHelper
# +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
def avatar(user, options = { })
if Setting.gravatar_enabled?
options.merge!({:ssl => Setting.protocol == 'https', :default => Setting.gravatar_default})
options.merge!({:ssl => (defined?(request) && request.ssl?), :default => Setting.gravatar_default})
email = nil
if user.respond_to?(:mail)
email = user.mail
@@ -708,9 +872,37 @@ module ApplicationHelper
email = $1
end
return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
else
''
end
end
def favicon
"<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />"
end
# Returns true if arg is expected in the API response
def include_in_api_response?(arg)
unless @included_in_api_response
param = params[:include]
@included_in_api_response = param.is_a?(Array) ? param.collect(&:to_s) : param.to_s.split(',')
@included_in_api_response.collect!(&:strip)
end
@included_in_api_response.include?(arg.to_s)
end
# Returns options or nil if nometa param or X-Redmine-Nometa header
# was set in the request
def api_meta(options)
if params[:nometa].present? || request.headers['X-Redmine-Nometa']
# compatibility mode for activeresource clients that raise
# an error when unserializing an array with attributes
nil
else
options
end
end
private
def wiki_helper

View File

@@ -0,0 +1,45 @@
module CalendarsHelper
def link_to_previous_month(year, month, options={})
target_year, target_month = if month == 1
[year - 1, 12]
else
[year, month - 1]
end
name = if target_month == 12
"#{month_name(target_month)} #{target_year}"
else
"#{month_name(target_month)}"
end
link_to_month(('&#171; ' + name), target_year, target_month, options)
end
def link_to_next_month(year, month, options={})
target_year, target_month = if month == 12
[year + 1, 1]
else
[year, month + 1]
end
name = if target_month == 1
"#{month_name(target_month)} #{target_year}"
else
"#{month_name(target_month)}"
end
link_to_month((name + ' &#187;'), target_year, target_month, options)
end
def link_to_month(link_name, year, month, options={})
project_id = options[:project].present? ? options[:project].to_param : nil
link_target = calendar_path(:year => year, :month => month, :project_id => project_id)
link_to_remote(link_name,
{:update => "content", :url => link_target, :method => :put},
{:href => link_target})
end
end

View File

@@ -35,8 +35,9 @@ module CustomFieldsHelper
custom_field = custom_value.custom_field
field_name = "#{name}[custom_field_values][#{custom_field.id}]"
field_id = "#{name}_custom_field_values_#{custom_field.id}"
case custom_field.field_format
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
case field_format.edit_as
when "date"
text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
calendar_for(field_id)
@@ -66,6 +67,27 @@ module CustomFieldsHelper
def custom_field_tag_with_label(name, custom_value)
custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
end
def custom_field_tag_for_bulk_edit(name, custom_field)
field_name = "#{name}[custom_field_values][#{custom_field.id}]"
field_id = "#{name}_custom_field_values_#{custom_field.id}"
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
case field_format.edit_as
when "date"
text_field_tag(field_name, '', :id => field_id, :size => 10) +
calendar_for(field_id)
when "text"
text_area_tag(field_name, '', :id => field_id, :rows => 3, :style => 'width:90%')
when "bool"
select_tag(field_name, options_for_select([[l(:label_no_change_option), ''],
[l(:general_text_yes), '1'],
[l(:general_text_no), '0']]), :id => field_id)
when "list"
select_tag(field_name, options_for_select([[l(:label_no_change_option), '']] + custom_field.possible_values), :id => field_id)
else
text_field_tag(field_name, '', :id => field_id)
end
end
# Return a string used to display a custom value
def show_value(custom_value)
@@ -75,19 +97,22 @@ module CustomFieldsHelper
# Return a string used to display a custom value
def format_value(value, field_format)
return "" unless value && !value.empty?
case field_format
when "date"
begin; format_date(value.to_date); rescue; value end
when "bool"
l(value == "1" ? :general_text_Yes : :general_text_No)
else
value
end
Redmine::CustomFieldFormat.format_value(value, field_format) # Proxy
end
# Return an array of custom field formats which can be used in select_tag
def custom_field_formats_for_select
CustomField::FIELD_FORMATS.sort {|a,b| a[1][:order]<=>b[1][:order]}.collect { |k| [ l(k[1][:name]), k[0] ] }
Redmine::CustomFieldFormat.as_select
end
# Renders the custom_values in api views
def render_api_custom_values(custom_values, api)
api.array :custom_fields do
custom_values.each do |custom_value|
api.custom_field :id => custom_value.custom_field_id, :name => custom_value.custom_field.name do
api.value custom_value.value
end
end
end unless custom_values.empty?
end
end

View File

@@ -0,0 +1,49 @@
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module GanttHelper
def gantt_zoom_link(gantt, in_or_out)
case in_or_out
when :in
if gantt.zoom < 4
link_to_remote(l(:text_zoom_in),
{:url => gantt.params.merge(:zoom => (gantt.zoom+1)), :method => :get, :update => 'content'},
{:href => url_for(gantt.params.merge(:zoom => (gantt.zoom+1))),
:class => 'icon icon-zoom-in'})
else
content_tag('span', l(:text_zoom_in), :class => 'icon icon-zoom-in')
end
when :out
if gantt.zoom > 1
link_to_remote(l(:text_zoom_out),
{:url => gantt.params.merge(:zoom => (gantt.zoom-1)), :method => :get, :update => 'content'},
{:href => url_for(gantt.params.merge(:zoom => (gantt.zoom-1))),
:class => 'icon icon-zoom-out'})
else
content_tag('span', l(:text_zoom_out), :class => 'icon icon-zoom-out')
end
end
end
def number_of_issues_on_versions(gantt)
versions = gantt.events.collect {|event| (event.is_a? Version) ? event : nil}.compact
versions.sum {|v| v.fixed_issues.for_gantt.with_query(@query).count}
end
end

View File

@@ -0,0 +1,2 @@
module IssueMovesHelper
end

View File

@@ -18,18 +18,67 @@
module IssuesHelper
include ApplicationHelper
def issue_list(issues, &block)
ancestors = []
issues.each do |issue|
while (ancestors.any? && !issue.is_descendant_of?(ancestors.last))
ancestors.pop
end
yield issue, ancestors.size
ancestors << issue unless issue.leaf?
end
end
# Renders a HTML/CSS tooltip
#
# To use, a trigger div is needed. This is a div with the class of "tooltip"
# that contains this method wrapped in a span with the class of "tip"
#
# <div class="tooltip"><%= link_to_issue(issue) %>
# <span class="tip"><%= render_issue_tooltip(issue) %></span>
# </div>
#
def render_issue_tooltip(issue)
@cached_label_status ||= l(:field_status)
@cached_label_start_date ||= l(:field_start_date)
@cached_label_due_date ||= l(:field_due_date)
@cached_label_assigned_to ||= l(:field_assigned_to)
@cached_label_priority ||= l(:field_priority)
@cached_label_project ||= l(:field_project)
link_to_issue(issue) + "<br /><br />" +
"<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />" +
"<strong>#{@cached_label_status}</strong>: #{issue.status.name}<br />" +
"<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" +
"<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" +
"<strong>#{@cached_label_assigned_to}</strong>: #{issue.assigned_to}<br />" +
"<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
end
def render_issue_subject_with_tree(issue)
s = ''
issue.ancestors.each do |ancestor|
s << '<div>' + content_tag('p', link_to_issue(ancestor))
end
s << '<div>' + content_tag('h3', h(issue.subject))
s << '</div>' * (issue.ancestors.size + 1)
s
end
def render_descendants_tree(issue)
s = '<form><table class="list issues">'
issue_list(issue.descendants.sort_by(&:lft)) do |child, level|
s << content_tag('tr',
content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
content_tag('td', link_to_issue(child, :truncate => 60), :class => 'subject') +
content_tag('td', h(child.status)) +
content_tag('td', link_to_user(child.assigned_to)) +
content_tag('td', progress_bar(child.done_ratio, :width => '80px')),
:class => "issue issue-#{child.id} hascontextmenu #{level > 0 ? "idnt idnt-#{level}" : nil}")
end
s << '</form></table>'
s
end
def render_custom_fields_rows(issue)
return if issue.custom_field_values.empty?
@@ -67,35 +116,25 @@ module IssuesHelper
def show_detail(detail, no_html=false)
case detail.property
when 'attr'
label = l(("field_" + detail.prop_key.to_s.gsub(/\_id$/, "")).to_sym)
case detail.prop_key
when 'due_date', 'start_date'
field = detail.prop_key.to_s.gsub(/\_id$/, "")
label = l(("field_" + field).to_sym)
case
when ['due_date', 'start_date'].include?(detail.prop_key)
value = format_date(detail.value.to_date) if detail.value
old_value = format_date(detail.old_value.to_date) if detail.old_value
when 'project_id'
p = Project.find_by_id(detail.value) and value = p.name if detail.value
p = Project.find_by_id(detail.old_value) and old_value = p.name if detail.old_value
when 'status_id'
s = IssueStatus.find_by_id(detail.value) and value = s.name if detail.value
s = IssueStatus.find_by_id(detail.old_value) and old_value = s.name if detail.old_value
when 'tracker_id'
t = Tracker.find_by_id(detail.value) and value = t.name if detail.value
t = Tracker.find_by_id(detail.old_value) and old_value = t.name if detail.old_value
when 'assigned_to_id'
u = User.find_by_id(detail.value) and value = u.name if detail.value
u = User.find_by_id(detail.old_value) and old_value = u.name if detail.old_value
when 'priority_id'
e = IssuePriority.find_by_id(detail.value) and value = e.name if detail.value
e = IssuePriority.find_by_id(detail.old_value) and old_value = e.name if detail.old_value
when 'category_id'
c = IssueCategory.find_by_id(detail.value) and value = c.name if detail.value
c = IssueCategory.find_by_id(detail.old_value) and old_value = c.name if detail.old_value
when 'fixed_version_id'
v = Version.find_by_id(detail.value) and value = format_version_name(v) if detail.value
v = Version.find_by_id(detail.old_value) and old_value = format_version_name(v) if detail.old_value
when 'estimated_hours'
when ['project_id', 'status_id', 'tracker_id', 'assigned_to_id', 'priority_id', 'category_id', 'fixed_version_id'].include?(detail.prop_key)
value = find_name_by_reflection(field, detail.value)
old_value = find_name_by_reflection(field, detail.old_value)
when detail.prop_key == 'estimated_hours'
value = "%0.02f" % detail.value.to_f unless detail.value.blank?
old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
when detail.prop_key == 'parent_id'
label = l(:field_parent_issue)
value = "##{detail.value}" unless detail.value.blank?
old_value = "##{detail.old_value}" unless detail.old_value.blank?
end
when 'cf'
custom_field = CustomField.find_by_id(detail.prop_key)
@@ -140,6 +179,29 @@ module IssuesHelper
l(:text_journal_deleted, :label => label, :old => old_value)
end
end
# Find the name of an associated record stored in the field attribute
def find_name_by_reflection(field, id)
association = Issue.reflect_on_association(field.to_sym)
if association
record = association.class_name.constantize.find_by_id(id)
return record.name if record
end
end
# Renders issue children recursively
def render_api_issue_children(issue, api)
return if issue.leaf?
api.array :children do
issue.children.each do |child|
api.issue(:id => child.id) do
api.tracker(:id => child.tracker_id, :name => child.tracker.name) unless child.tracker.nil?
api.subject child.subject
render_api_issue_children(child, api)
end
end
end
end
def issues_to_csv(issues, project = nil)
ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
@@ -160,6 +222,7 @@ module IssuesHelper
l(:field_due_date),
l(:field_done_ratio),
l(:field_estimated_hours),
l(:field_parent_issue),
l(:field_created_on),
l(:field_updated_on)
]
@@ -186,6 +249,7 @@ module IssuesHelper
format_date(issue.due_date),
issue.done_ratio,
issue.estimated_hours.to_s.gsub('.', decimal_separator),
issue.parent_id,
format_time(issue.created_on),
format_time(issue.updated_on)
]

View File

@@ -16,13 +16,13 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module JournalsHelper
def render_notes(journal, options={})
def render_notes(issue, journal, options={})
content = ''
editable = journal.editable_by?(User.current)
editable = User.current.logged? && (User.current.allowed_to?(:edit_issue_notes, issue.project) || (journal.user == User.current && User.current.allowed_to?(:edit_own_issue_notes, issue.project)))
links = []
if !journal.notes.blank?
links << link_to_remote(image_tag('comment.png'),
{ :url => {:controller => 'issues', :action => 'reply', :id => journal.journalized, :journal_id => journal} },
{ :url => {:controller => 'journals', :action => 'new', :id => issue, :journal_id => journal} },
:title => l(:button_quote)) if options[:reply_links]
links << link_to_in_place_notes_editor(image_tag('edit.png'), "journal-#{journal.id}-notes",
{ :controller => 'journals', :action => 'edit', :id => journal },
@@ -32,7 +32,6 @@ module JournalsHelper
content << textilizable(journal, :notes)
css_classes = "wiki"
css_classes << " editable" if editable
css_classes << " gravatar-margin" if Setting.gravatar_enabled?
content_tag('div', content, :id => "journal-#{journal.id}-notes", :class => css_classes)
end

View File

@@ -23,6 +23,7 @@ module MessagesHelper
:action => 'show',
:board_id => message.board_id,
:id => message.root,
:r => (message.parent_id && message.id),
:anchor => (message.parent_id ? "message-#{message.id}" : nil)
end
end

View File

@@ -46,7 +46,7 @@ module ProjectsHelper
options = ''
options << "<option value=''></option>" if project.allowed_parents.include?(nil)
options << project_tree_options_for_select(project.allowed_parents.compact, :selected => selected)
content_tag('select', options, :name => 'project[parent_id]')
content_tag('select', options, :name => 'project[parent_id]', :id => 'project_parent_id')
end
# Renders a tree of projects as a nested set of unordered lists
@@ -56,7 +56,10 @@ module ProjectsHelper
s = ''
if projects.any?
ancestors = []
original_project = @project
projects.each do |project|
# set the project environment to please macros.
@project = project
if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>\n"
else
@@ -69,12 +72,13 @@ module ProjectsHelper
end
classes = (ancestors.empty? ? 'root' : 'child')
s << "<li class='#{classes}'><div class='#{classes}'>" +
link_to(h(project), {:controller => 'projects', :action => 'show', :id => project}, :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}")
link_to_project(project, {}, :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}")
s << "<div class='wiki description'>#{textilizable(project.short_description, :project => project)}</div>" unless project.description.blank?
s << "</div>\n"
ancestors << project
end
s << ("</li></ul>\n" * ancestors.size)
@project = original_project
end
s
end

View File

@@ -50,15 +50,50 @@ module QueriesHelper
when 'User'
link_to_user value
when 'Project'
link_to(h(value), :controller => 'projects', :action => 'show', :id => value)
link_to_project value
when 'Version'
link_to(h(value), :controller => 'versions', :action => 'show', :id => value)
when 'TrueClass'
l(:general_text_Yes)
when 'FalseClass'
l(:general_text_No)
when 'Issue'
link_to_issue(value, :subject => false)
else
h(value)
end
end
# Retrieve query from session or build a new query
def retrieve_query
if !params[:query_id].blank?
cond = "project_id IS NULL"
cond << " OR project_id = #{@project.id}" if @project
@query = Query.find(params[:query_id], :conditions => cond)
@query.project = @project
session[:query] = {:id => @query.id, :project_id => @query.project_id}
sort_clear
else
if api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
# Give it a name, required to be valid
@query = Query.new(:name => "_")
@query.project = @project
if params[:fields]
@query.filters = {}
@query.add_filters(params[:fields], params[:operators], params[:values])
else
@query.available_filters.keys.each do |field|
@query.add_short_filter(field, params[field]) if params[field]
end
end
@query.group_by = params[:group_by]
@query.column_names = params[:query] && params[:query][:column_names]
session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
else
@query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
@query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
@query.project = @project
end
end
end
end

View File

@@ -18,8 +18,12 @@
require 'iconv'
module RepositoriesHelper
def format_revision(txt)
txt.to_s[0,8]
def format_revision(revision)
if revision.respond_to? :format_identifier
revision.format_identifier
else
revision.to_s
end
end
def truncate_at_line_break(text, length = 255)
@@ -52,17 +56,19 @@ module RepositoriesHelper
else
change
end
end.compact
end.compact
tree = { }
changes.each do |change|
p = tree
dirs = change.path.to_s.split('/').select {|d| !d.blank?}
path = ''
dirs.each do |dir|
path += '/' + dir
p[:s] ||= {}
p = p[:s]
p[dir] ||= {}
p = p[dir]
p[path] ||= {}
p = p[path]
end
p[:c] = change
end
@@ -76,31 +82,35 @@ module RepositoriesHelper
output = ''
output << '<ul>'
tree.keys.sort.each do |file|
s = !tree[file][:s].nil?
c = tree[file][:c]
style = 'change'
style << ' folder' if s
style << " change-#{c.action}" if c
text = h(file)
unless c.nil?
text = File.basename(h(file))
if s = tree[file][:s]
style << ' folder'
path_param = to_path_param(@repository.relative_path(file))
text = link_to(text, :controller => 'repositories',
:action => 'show',
:id => @project,
:path => path_param,
:rev => @changeset.identifier)
output << "<li class='#{style}'>#{text}</li>"
output << render_changes_tree(s)
elsif c = tree[file][:c]
style << " change-#{c.action}"
path_param = to_path_param(@repository.relative_path(c.path))
text = link_to(text, :controller => 'repositories',
:action => 'entry',
:id => @project,
:path => path_param,
:rev => @changeset.revision) unless s || c.action == 'D'
:rev => @changeset.identifier) unless c.action == 'D'
text << " - #{c.revision}" unless c.revision.blank?
text << ' (' + link_to('diff', :controller => 'repositories',
:action => 'diff',
:id => @project,
:path => path_param,
:rev => @changeset.revision) + ') ' if c.action == 'M'
:rev => @changeset.identifier) + ') ' if c.action == 'M'
text << ' ' + content_tag('span', c.from_path, :class => 'copied-from') unless c.from_path.blank?
output << "<li class='#{style}'>#{text}</li>"
end
output << "<li class='#{style}'>#{text}</li>"
output << render_changes_tree(tree[file][:s]) if s
end
output << '</ul>'
output
@@ -126,7 +136,7 @@ module RepositoriesHelper
def scm_select_tag(repository)
scm_options = [["--- #{l(:actionview_instancetag_blank_option)} ---", '']]
REDMINE_SUPPORTED_SCM.each do |scm|
Redmine::Scm::Base.all.each do |scm|
scm_options << ["Repository::#{scm}".constantize.scm_name, scm] if Setting.enabled_scm.include?(scm) || (repository && repository.class.name.demodulize == scm)
end

View File

@@ -39,7 +39,7 @@ module SearchHelper
end
def type_label(t)
l("label_#{t.singularize}_plural")
l("label_#{t.singularize}_plural", :default => t.to_s.humanize)
end
def project_select_tag

View File

@@ -71,4 +71,14 @@ module SettingsHelper
label = options.delete(:label)
label != false ? content_tag("label", l(label || "setting_#{setting}")) : ''
end
# Renders a notification field for a Redmine::Notifiable option
def notification_field(notifiable)
return content_tag(:label,
check_box_tag('settings[notified_events][]',
notifiable.name,
Setting.notified_events.include?(notifiable.name)) +
l_or_humanize(notifiable.name, :prefix => 'label_'),
:class => notifiable.parent.present? ? "parent" : '')
end
end

View File

@@ -81,7 +81,7 @@ module SortHelper
def to_sql
sql = @criteria.collect do |k,o|
if s = @available_criteria[k]
(o ? s.to_a : s.to_a.collect {|c| "#{c} DESC"}).join(', ')
(o ? s.to_a : s.to_a.collect {|c| append_desc(c)}).join(', ')
end
end.compact.join(', ')
sql.blank? ? nil : sql
@@ -120,6 +120,15 @@ module SortHelper
@criteria.slice!(3)
self
end
# Appends DESC to the sort criterion unless it has a fixed order
def append_desc(criterion)
if criterion =~ / (asc|desc)$/i
criterion
else
"#{criterion} DESC"
end
end
end
def sort_name

View File

@@ -57,7 +57,7 @@ module TimelogHelper
if value.to_s.empty?
data.select {|row| row[criteria].blank? }
else
data.select {|row| row[criteria] == value}
data.select {|row| row[criteria].to_s == value.to_s}
end
end

View File

@@ -33,15 +33,19 @@ module UsersHelper
options
end
def user_mail_notification_options(user)
user.valid_notification_options.collect {|o| [l(o.last), o.first]}
end
def change_status_link(user)
url = {:controller => 'users', :action => 'edit', :id => user, :page => params[:page], :status => params[:status], :tab => nil}
url = {:controller => 'users', :action => 'update', :id => user, :page => params[:page], :status => params[:status], :tab => nil}
if user.locked?
link_to l(:button_unlock), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :post, :class => 'icon icon-unlock'
link_to l(:button_unlock), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :put, :class => 'icon icon-unlock'
elsif user.registered?
link_to l(:button_activate), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :post, :class => 'icon icon-unlock'
link_to l(:button_activate), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :put, :class => 'icon icon-unlock'
elsif user != User.current
link_to l(:button_lock), url.merge(:user => {:status => User::STATUS_LOCKED}), :method => :post, :class => 'icon icon-lock'
link_to l(:button_lock), url.merge(:user => {:status => User::STATUS_LOCKED}), :method => :put, :class => 'icon icon-lock'
end
end

View File

@@ -48,8 +48,8 @@ module WatchersHelper
# Returns a comma separated list of users watching the given object
def watchers_list(object)
remove_allowed = User.current.allowed_to?("delete_#{object.class.name.underscore}_watchers".to_sym, object.project)
object.watcher_users.collect do |user|
s = content_tag('span', link_to_user(user), :class => 'user')
lis = object.watcher_users.collect do |user|
s = avatar(user, :size => "16").to_s + link_to_user(user, :class => 'user').to_s
if remove_allowed
url = {:controller => 'watchers',
:action => 'destroy',
@@ -59,9 +59,11 @@ module WatchersHelper
s += ' ' + link_to_remote(image_tag('delete.png'),
{:url => url},
:href => url_for(url),
:style => "vertical-align: middle")
:style => "vertical-align: middle",
:class => "delete")
end
s
end.join(",\n")
"<li>#{ s }</li>"
end
lis.empty? ? "" : "<ul>#{ lis.join("\n") }</ul>"
end
end

View File

@@ -24,7 +24,7 @@ module WikiHelper
attrs << " selected='selected'" if selected == page
indent = (level > 0) ? ('&nbsp;' * level * 2 + '&#187; ') : nil
s << "<option value='#{page.id}'>#{indent}#{h page.pretty_title}</option>\n" +
s << "<option #{attrs}>#{indent}#{h page.pretty_title}</option>\n" +
wiki_page_options_for_select(pages, selected, page, level + 1)
end
s

View File

@@ -58,6 +58,9 @@ class Attachment < ActiveRecord::Base
self.filename = sanitize_filename(@temp_file.original_filename)
self.disk_filename = Attachment.disk_filename(filename)
self.content_type = @temp_file.content_type.to_s.chomp
if content_type.blank?
self.content_type = Redmine::MimeType.of(filename)
end
self.filesize = @temp_file.size
end
end
@@ -130,6 +133,33 @@ class Attachment < ActiveRecord::Base
def readable?
File.readable?(diskfile)
end
# Bulk attaches a set of files to an object
#
# Returns a Hash of the results:
# :files => array of the attached files
# :unsaved => array of the files that could not be attached
def self.attach_files(obj, attachments)
attached = []
if attachments && attachments.is_a?(Hash)
attachments.each_value do |attachment|
file = attachment['file']
next unless file && file.size > 0
a = Attachment.create(:container => obj,
:file => file,
:description => attachment['description'].to_s.strip,
:author => User.current)
if a.new_record?
obj.unsaved_attachments ||= []
obj.unsaved_attachments << a
else
attached << a
end
end
end
{:files => attached, :unsaved => obj.unsaved_attachments}
end
private
def sanitize_filename(value)
@@ -144,14 +174,18 @@ private
# Returns an ASCII or hashed filename
def self.disk_filename(filename)
df = DateTime.now.strftime("%y%m%d%H%M%S") + "_"
timestamp = DateTime.now.strftime("%y%m%d%H%M%S")
ascii = ''
if filename =~ %r{^[a-zA-Z0-9_\.\-]*$}
df << filename
ascii = filename
else
df << Digest::MD5.hexdigest(filename)
ascii = Digest::MD5.hexdigest(filename)
# keep the extension if any
df << $1 if filename =~ %r{(\.[a-zA-Z0-9]+)$}
ascii << $1 if filename =~ %r{(\.[a-zA-Z0-9]+)$}
end
df
while File.exist?(File.join(@@storage_path, "#{timestamp}_#{ascii}"))
timestamp.succ!
end
"#{timestamp}_#{ascii}"
end
end

View File

@@ -32,6 +32,15 @@ class AuthSource < ActiveRecord::Base
"Abstract"
end
def allow_password_changes?
self.class.allow_password_changes?
end
# Does this auth source backend allow password changes?
def self.allow_password_changes?
false
end
# Try to authenticate a user not yet registered against available sources
def self.authenticate(login, password)
AuthSource.find(:all, :conditions => ["onthefly_register=?", true]).each do |source|

View File

@@ -33,30 +33,12 @@ class AuthSourceLdap < AuthSource
def authenticate(login, password)
return nil if login.blank? || password.blank?
attrs = []
# get user's DN
ldap_con = initialize_ldap_con(self.account, self.account_password)
login_filter = Net::LDAP::Filter.eq( self.attr_login, login )
object_filter = Net::LDAP::Filter.eq( "objectClass", "*" )
dn = String.new
ldap_con.search( :base => self.base_dn,
:filter => object_filter & login_filter,
# only ask for the DN if on-the-fly registration is disabled
:attributes=> (onthefly_register? ? ['dn', self.attr_firstname, self.attr_lastname, self.attr_mail] : ['dn'])) do |entry|
dn = entry.dn
attrs = [:firstname => AuthSourceLdap.get_attr(entry, self.attr_firstname),
:lastname => AuthSourceLdap.get_attr(entry, self.attr_lastname),
:mail => AuthSourceLdap.get_attr(entry, self.attr_mail),
:auth_source_id => self.id ] if onthefly_register?
attrs = get_user_dn(login)
if attrs && attrs[:dn] && authenticate_dn(attrs[:dn], password)
logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
return attrs.except(:dn)
end
return nil if dn.empty?
logger.debug "DN found for #{login}: #{dn}" if logger && logger.debug?
# authenticate user
ldap_con = initialize_ldap_con(dn, password)
return nil unless ldap_con.bind
# return user's attributes
logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
attrs
rescue Net::LDAP::LdapError => text
raise "LdapError: " + text
end
@@ -89,6 +71,56 @@ class AuthSourceLdap < AuthSource
options.merge!(:auth => { :method => :simple, :username => ldap_user, :password => ldap_password }) unless ldap_user.blank? && ldap_password.blank?
Net::LDAP.new options
end
def get_user_attributes_from_ldap_entry(entry)
{
:dn => entry.dn,
:firstname => AuthSourceLdap.get_attr(entry, self.attr_firstname),
:lastname => AuthSourceLdap.get_attr(entry, self.attr_lastname),
:mail => AuthSourceLdap.get_attr(entry, self.attr_mail),
:auth_source_id => self.id
}
end
# Return the attributes needed for the LDAP search. It will only
# include the user attributes if on-the-fly registration is enabled
def search_attributes
if onthefly_register?
['dn', self.attr_firstname, self.attr_lastname, self.attr_mail]
else
['dn']
end
end
# Check if a DN (user record) authenticates with the password
def authenticate_dn(dn, password)
if dn.present? && password.present?
initialize_ldap_con(dn, password).bind
end
end
# Get the user's dn and any attributes for them, given their login
def get_user_dn(login)
ldap_con = initialize_ldap_con(self.account, self.account_password)
login_filter = Net::LDAP::Filter.eq( self.attr_login, login )
object_filter = Net::LDAP::Filter.eq( "objectClass", "*" )
attrs = {}
ldap_con.search( :base => self.base_dn,
:filter => object_filter & login_filter,
:attributes=> search_attributes) do |entry|
if onthefly_register?
attrs = get_user_attributes_from_ldap_entry(entry)
else
attrs = {:dn => entry.dn}
end
logger.debug "DN found for #{login}: #{attrs[:dn]}" if logger && logger.debug?
end
attrs
end
def self.get_attr(entry, attr_name)
if !attr_name.blank?

View File

@@ -18,7 +18,7 @@
class Board < ActiveRecord::Base
belongs_to :project
has_many :topics, :class_name => 'Message', :conditions => "#{Message.table_name}.parent_id IS NULL", :order => "#{Message.table_name}.created_on DESC"
has_many :messages, :dependent => :delete_all, :order => "#{Message.table_name}.created_on DESC"
has_many :messages, :dependent => :destroy, :order => "#{Message.table_name}.created_on DESC"
belongs_to :last_message, :class_name => 'Message', :foreign_key => :last_message_id
acts_as_list :scope => :project_id
acts_as_watchable

View File

@@ -19,8 +19,13 @@ class Change < ActiveRecord::Base
belongs_to :changeset
validates_presence_of :changeset_id, :action, :path
before_save :init_path
def relative_path
changeset.repository.relative_path(path)
end
def init_path
self.path ||= ""
end
end

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2008 Jean-Philippe Lang
# Copyright (C) 2006-2010 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -23,10 +23,10 @@ class Changeset < ActiveRecord::Base
has_many :changes, :dependent => :delete_all
has_and_belongs_to_many :issues
acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.revision}" + (o.short_comments.blank? ? '' : (': ' + o.short_comments))},
acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.format_identifier}" + (o.short_comments.blank? ? '' : (': ' + o.short_comments))},
:description => :long_comments,
:datetime => :committed_on,
:url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :rev => o.revision}}
:url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :rev => o.identifier}}
acts_as_searchable :columns => 'comments',
:include => {:repository => :project},
@@ -41,9 +41,21 @@ class Changeset < ActiveRecord::Base
validates_uniqueness_of :revision, :scope => :repository_id
validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
named_scope :visible, lambda {|*args| { :include => {:repository => :project},
:conditions => Project.allowed_to_condition(args.first || User.current, :view_changesets) } }
def revision=(r)
write_attribute :revision, (r.nil? ? nil : r.to_s)
end
# Returns the identifier of this changeset; depending on repository backends
def identifier
if repository.class.respond_to? :changeset_identifier
repository.class.changeset_identifier self
else
revision.to_s
end
end
def comments=(comment)
write_attribute(:comments, Changeset.normalize_comments(comment))
@@ -53,7 +65,20 @@ class Changeset < ActiveRecord::Base
self.commit_date = date
super
end
# Returns the readable identifier
def format_identifier
if repository.class.respond_to? :format_changeset_identifier
repository.class.format_changeset_identifier self
else
identifier
end
end
def committer=(arg)
write_attribute(:committer, self.class.to_utf8(arg.to_s))
end
def project
repository.project
end
@@ -69,58 +94,47 @@ class Changeset < ActiveRecord::Base
def after_create
scan_comment_for_issue_ids
end
require 'pp'
TIMELOG_RE = /
(
((\d+)(h|hours?))((\d+)(m|min)?)?
|
((\d+)(h|hours?|m|min))
|
(\d+):(\d+)
|
(\d+([\.,]\d+)?)h?
)
/x
def scan_comment_for_issue_ids
return if comments.blank?
# keywords used to reference issues
ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip)
ref_keywords_any = ref_keywords.delete('*')
# keywords used to fix issues
fix_keywords = Setting.commit_fix_keywords.downcase.split(",").collect(&:strip)
# status and optional done ratio applied
fix_status = IssueStatus.find_by_id(Setting.commit_fix_status_id)
done_ratio = Setting.commit_fix_done_ratio.blank? ? nil : Setting.commit_fix_done_ratio.to_i
kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|")
return if kw_regexp.blank?
referenced_issues = []
if ref_keywords.delete('*')
# find any issue ID in the comments
target_issue_ids = []
comments.scan(%r{([\s\(\[,-]|^)#(\d+)(?=[[:punct:]]|\s|<|$)}).each { |m| target_issue_ids << m[1] }
referenced_issues += repository.project.issues.find_all_by_id(target_issue_ids)
end
comments.scan(Regexp.new("(#{kw_regexp})[\s:]+(([\s,;&]*#?\\d+)+)", Regexp::IGNORECASE)).each do |match|
action = match[0]
target_issue_ids = match[1].scan(/\d+/)
target_issues = repository.project.issues.find_all_by_id(target_issue_ids)
if fix_status && fix_keywords.include?(action.downcase)
# update status of issues
logger.debug "Issues fixed by changeset #{self.revision}: #{issue_ids.join(', ')}." if logger && logger.debug?
target_issues.each do |issue|
# the issue may have been updated by the closure of another one (eg. duplicate)
issue.reload
# don't change the status is the issue is closed
next if issue.status.is_closed?
csettext = "r#{self.revision}"
if self.scmid && (! (csettext =~ /^r[0-9]+$/))
csettext = "commit:\"#{self.scmid}\""
end
journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, csettext))
issue.status = fix_status
issue.done_ratio = done_ratio if done_ratio
Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update,
{ :changeset => self, :issue => issue })
issue.save
comments.scan(/([\s\(\[,-]|^)((#{kw_regexp})[\s:]+)?(#\d+(\s+@#{TIMELOG_RE})?([\s,;&]+#\d+(\s+@#{TIMELOG_RE})?)*)(?=[[:punct:]]|\s|<|$)/i) do |match|
action, refs = match[2], match[3]
next unless action.present? || ref_keywords_any
refs.scan(/#(\d+)(\s+@#{TIMELOG_RE})?/).each do |m|
issue, hours = find_referenced_issue_by_id(m[0].to_i), m[2]
if issue
referenced_issues << issue
fix_issue(issue) if fix_keywords.include?(action.to_s.downcase)
log_time(issue, hours) if hours && Setting.commit_logtime_enabled?
end
end
referenced_issues += target_issues
end
self.issues = referenced_issues.uniq
referenced_issues.uniq!
self.issues = referenced_issues unless referenced_issues.empty?
end
def short_comments
@@ -130,6 +144,14 @@ class Changeset < ActiveRecord::Base
def long_comments
@long_comments || split_comments.last
end
def text_tag
if scmid?
"commit:#{scmid}"
else
"r#{revision}"
end
end
# Returns the previous changeset
def previous
@@ -145,9 +167,78 @@ class Changeset < ActiveRecord::Base
def self.normalize_comments(str)
to_utf8(str.to_s.strip)
end
# Creates a new Change from it's common parameters
def create_change(change)
Change.create(:changeset => self,
:action => change[:action],
:path => change[:path],
:from_path => change[:from_path],
:from_revision => change[:from_revision])
end
private
# Finds an issue that can be referenced by the commit message
# i.e. an issue that belong to the repository project, a subproject or a parent project
def find_referenced_issue_by_id(id)
return nil if id.blank?
issue = Issue.find_by_id(id.to_i, :include => :project)
if issue
unless project == issue.project || project.is_ancestor_of?(issue.project) || project.is_descendant_of?(issue.project)
issue = nil
end
end
issue
end
def fix_issue(issue)
status = IssueStatus.find_by_id(Setting.commit_fix_status_id.to_i)
if status.nil?
logger.warn("No status macthes commit_fix_status_id setting (#{Setting.commit_fix_status_id})") if logger
return issue
end
# the issue may have been updated by the closure of another one (eg. duplicate)
issue.reload
# don't change the status is the issue is closed
return if issue.status && issue.status.is_closed?
journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, text_tag))
issue.status = status
unless Setting.commit_fix_done_ratio.blank?
issue.done_ratio = Setting.commit_fix_done_ratio.to_i
end
Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update,
{ :changeset => self, :issue => issue })
unless issue.save
logger.warn("Issue ##{issue.id} could not be saved by changeset #{id}: #{issue.errors.full_messages}") if logger
end
issue
end
def log_time(issue, hours)
time_entry = TimeEntry.new(
:user => user,
:hours => hours,
:issue => issue,
:spent_on => commit_date,
:comments => l(:text_time_logged_by_changeset, :value => text_tag, :locale => Setting.default_language)
)
time_entry.activity = log_time_activity unless log_time_activity.nil?
unless time_entry.save
logger.warn("TimeEntry could not be created by changeset #{id}: #{time_entry.errors.full_messages}") if logger
end
time_entry
end
def log_time_activity
if Setting.commit_logtime_activity_id.to_i > 0
TimeEntryActivity.find_by_id(Setting.commit_logtime_activity_id.to_i)
end
end
def split_comments
comments =~ /\A(.+?)\r?\n(.*)$/m
@short_comments = $1 || comments
@@ -160,11 +251,17 @@ class Changeset < ActiveRecord::Base
encoding = Setting.commit_logs_encoding.to_s.strip
unless encoding.blank? || encoding == 'UTF-8'
begin
return Iconv.conv('UTF-8', encoding, str)
str = Iconv.conv('UTF-8', encoding, str)
rescue Iconv::Failure
# do nothing here
end
end
str
# removes invalid UTF8 sequences
begin
Iconv.conv('UTF-8//IGNORE', 'UTF-8', str + ' ')[0..-3]
rescue Iconv::InvalidEncoding
# "UTF-8//IGNORE" is not supported on some OS
str
end
end
end

View File

@@ -20,20 +20,11 @@ class CustomField < ActiveRecord::Base
acts_as_list :scope => 'type = \'#{self.class}\''
serialize :possible_values
FIELD_FORMATS = { "string" => { :name => :label_string, :order => 1 },
"text" => { :name => :label_text, :order => 2 },
"int" => { :name => :label_integer, :order => 3 },
"float" => { :name => :label_float, :order => 4 },
"list" => { :name => :label_list, :order => 5 },
"date" => { :name => :label_date, :order => 6 },
"bool" => { :name => :label_boolean, :order => 7 }
}.freeze
validates_presence_of :name, :field_format
validates_uniqueness_of :name, :scope => :type
validates_length_of :name, :maximum => 30
validates_format_of :name, :with => /^[\w\s\.\'\-]*$/i
validates_inclusion_of :field_format, :in => FIELD_FORMATS.keys
validates_inclusion_of :field_format, :in => Redmine::CustomFieldFormat.available_formats
def initialize(attributes = nil)
super

View File

@@ -34,6 +34,10 @@ class CustomValue < ActiveRecord::Base
custom_field.editable?
end
def visible?
custom_field.visible?
end
def required?
custom_field.is_required?
end

View File

@@ -46,11 +46,4 @@ class Document < ActiveRecord::Base
end
@updated_on
end
# Returns the mail adresses of users that should be notified
def recipients
notified = project.notified_users
notified.reject! {|user| !visible?(user)}
notified.collect(&:mail)
end
end

View File

@@ -31,6 +31,7 @@ class Group < Principal
def user_added(user)
members.each do |member|
next if member.project.nil?
user_member = Member.find_by_project_id_and_user_id(member.project_id, user.id) || Member.new(:project_id => member.project_id, :user_id => user.id)
member.member_roles.each do |member_role|
user_member.member_roles << MemberRole.new(:role => member_role.role, :inherited_from => member_role.id)

View File

@@ -16,6 +16,8 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class Issue < ActiveRecord::Base
include Redmine::SafeAttributes
belongs_to :project
belongs_to :tracker
belongs_to :status, :class_name => 'IssueStatus', :foreign_key => 'status_id'
@@ -32,6 +34,7 @@ class Issue < ActiveRecord::Base
has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all
has_many :relations_to, :class_name => 'IssueRelation', :foreign_key => 'issue_to_id', :dependent => :delete_all
acts_as_nested_set :scope => 'root_id'
acts_as_attachable :after_remove => :attachment_removed
acts_as_customizable
acts_as_watchable
@@ -47,8 +50,11 @@ class Issue < ActiveRecord::Base
:author_key => :author_id
DONE_RATIO_OPTIONS = %w(issue_field issue_status)
attr_reader :current_journal
validates_presence_of :subject, :priority, :project, :tracker, :author, :status
validates_length_of :subject, :maximum => 255
validates_inclusion_of :done_ratio, :in => 0..100
validates_numericality_of :estimated_hours, :allow_nil => true
@@ -58,8 +64,33 @@ class Issue < ActiveRecord::Base
named_scope :open, :conditions => ["#{IssueStatus.table_name}.is_closed = ?", false], :include => :status
before_save :update_done_ratio_from_issue_status
after_save :create_journal
named_scope :recently_updated, :order => "#{Issue.table_name}.updated_on DESC"
named_scope :with_limit, lambda { |limit| { :limit => limit} }
named_scope :on_active_project, :include => [:status, :project, :tracker],
:conditions => ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"]
named_scope :for_gantt, lambda {
{
:include => [:tracker, :status, :assigned_to, :priority, :project, :fixed_version]
}
}
named_scope :without_version, lambda {
{
:conditions => { :fixed_version_id => nil}
}
}
named_scope :with_query, lambda {|query|
{
:conditions => Query.merge_conditions(query.statement)
}
}
before_create :default_assign
before_save :close_duplicates, :update_done_ratio_from_issue_status
after_save :reschedule_following_issues, :update_nested_set_attributes, :update_parent_attributes, :create_journal
after_destroy :destroy_children
after_destroy :update_parent_attributes
# Returns true if usr or current user is allowed to view the issue
def visible?(usr=nil)
@@ -80,61 +111,81 @@ class Issue < ActiveRecord::Base
end
def copy_from(arg)
issue = arg.is_a?(Issue) ? arg : Issue.find(arg)
self.attributes = issue.attributes.dup.except("id", "created_on", "updated_on")
self.custom_values = issue.custom_values.collect {|v| v.clone}
issue = arg.is_a?(Issue) ? arg : Issue.visible.find(arg)
self.attributes = issue.attributes.dup.except("id", "root_id", "parent_id", "lft", "rgt", "created_on", "updated_on")
self.custom_field_values = issue.custom_field_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h}
self.status = issue.status
self
end
# Moves/copies an issue to a new project and tracker
# Returns the moved/copied issue on success, false on failure
def move_to(new_project, new_tracker = nil, options = {})
def move_to_project(*args)
ret = Issue.transaction do
move_to_project_without_transaction(*args) || raise(ActiveRecord::Rollback)
end || false
end
def move_to_project_without_transaction(new_project, new_tracker = nil, options = {})
options ||= {}
issue = options[:copy] ? self.clone : self
transaction do
if new_project && issue.project_id != new_project.id
# delete issue relations
unless Setting.cross_project_issue_relations?
issue.relations_from.clear
issue.relations_to.clear
end
# issue is moved to another project
# reassign to the category with same name if any
new_category = issue.category.nil? ? nil : new_project.issue_categories.find_by_name(issue.category.name)
issue.category = new_category
# Keep the fixed_version if it's still valid in the new_project
unless new_project.shared_versions.include?(issue.fixed_version)
issue.fixed_version = nil
end
issue.project = new_project
issue = options[:copy] ? self.class.new.copy_from(self) : self
if new_project && issue.project_id != new_project.id
# delete issue relations
unless Setting.cross_project_issue_relations?
issue.relations_from.clear
issue.relations_to.clear
end
if new_tracker
issue.tracker = new_tracker
# issue is moved to another project
# reassign to the category with same name if any
new_category = issue.category.nil? ? nil : new_project.issue_categories.find_by_name(issue.category.name)
issue.category = new_category
# Keep the fixed_version if it's still valid in the new_project
unless new_project.shared_versions.include?(issue.fixed_version)
issue.fixed_version = nil
end
if options[:copy]
issue.custom_field_values = self.custom_field_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h}
issue.status = if options[:attributes] && options[:attributes][:status_id]
IssueStatus.find_by_id(options[:attributes][:status_id])
else
self.status
end
end
# Allow bulk setting of attributes on the issue
if options[:attributes]
issue.attributes = options[:attributes]
end
if issue.save
unless options[:copy]
# Manually update project_id on related time entries
TimeEntry.update_all("project_id = #{new_project.id}", {:issue_id => id})
end
else
Issue.connection.rollback_db_transaction
return false
issue.project = new_project
if issue.parent && issue.parent.project_id != issue.project_id
issue.parent_issue_id = nil
end
end
return issue
if new_tracker
issue.tracker = new_tracker
issue.reset_custom_values!
end
if options[:copy]
issue.custom_field_values = self.custom_field_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h}
issue.status = if options[:attributes] && options[:attributes][:status_id]
IssueStatus.find_by_id(options[:attributes][:status_id])
else
self.status
end
end
# Allow bulk setting of attributes on the issue
if options[:attributes]
issue.attributes = options[:attributes]
end
if issue.save
unless options[:copy]
# Manually update project_id on related time entries
TimeEntry.update_all("project_id = #{new_project.id}", {:issue_id => id})
issue.children.each do |child|
unless child.move_to_project_without_transaction(new_project)
# Move failed and transaction was rollback'd
return false
end
end
end
else
return false
end
issue
end
def status_id=(sid)
self.status = nil
write_attribute(:status_id, sid)
end
def priority_id=(pid)
@@ -144,7 +195,6 @@ class Issue < ActiveRecord::Base
def tracker_id=(tid)
self.tracker = nil
write_attribute(:tracker_id, tid)
result = write_attribute(:tracker_id, tid)
@custom_field_values = nil
result
@@ -157,16 +207,79 @@ class Issue < ActiveRecord::Base
if new_tracker_id
self.tracker_id = new_tracker_id
end
self.attributes_without_tracker_first = new_attributes, *args
send :attributes_without_tracker_first=, new_attributes, *args
end
alias_method_chain :attributes=, :tracker_first
# Do not redefine alias chain on reload (see #4838)
alias_method_chain(:attributes=, :tracker_first) unless method_defined?(:attributes_without_tracker_first=)
def estimated_hours=(h)
write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h)
end
safe_attributes 'tracker_id',
'status_id',
'parent_issue_id',
'category_id',
'assigned_to_id',
'priority_id',
'fixed_version_id',
'subject',
'description',
'start_date',
'due_date',
'done_ratio',
'estimated_hours',
'custom_field_values',
'custom_fields',
'lock_version',
:if => lambda {|issue, user| issue.new_record? || user.allowed_to?(:edit_issues, issue.project) }
safe_attributes 'status_id',
'assigned_to_id',
'fixed_version_id',
'done_ratio',
:if => lambda {|issue, user| issue.new_statuses_allowed_to(user).any? }
# Safely sets attributes
# Should be called from controllers instead of #attributes=
# attr_accessible is too rough because we still want things like
# Issue.new(:project => foo) to work
# TODO: move workflow/permission checks from controllers to here
def safe_attributes=(attrs, user=User.current)
return unless attrs.is_a?(Hash)
# User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
attrs = delete_unsafe_attributes(attrs, user)
return if attrs.empty?
# Tracker must be set before since new_statuses_allowed_to depends on it.
if t = attrs.delete('tracker_id')
self.tracker_id = t
end
if attrs['status_id']
unless new_statuses_allowed_to(user).collect(&:id).include?(attrs['status_id'].to_i)
attrs.delete('status_id')
end
end
unless leaf?
attrs.reject! {|k,v| %w(priority_id done_ratio start_date due_date estimated_hours).include?(k)}
end
if attrs.has_key?('parent_issue_id')
if !user.allowed_to?(:manage_subtasks, project)
attrs.delete('parent_issue_id')
elsif !attrs['parent_issue_id'].blank?
attrs.delete('parent_issue_id') unless Issue.visible(user).exists?(attrs['parent_issue_id'].to_i)
end
end
self.attributes = attrs
end
def done_ratio
if Issue.use_status_for_done_ratio? && status && status.default_done_ratio?
if Issue.use_status_for_done_ratio? && status && status.default_done_ratio
status.default_done_ratio
else
read_attribute(:done_ratio)
@@ -208,44 +321,32 @@ class Issue < ActiveRecord::Base
errors.add :tracker_id, :inclusion
end
end
end
def before_create
# default assignment based on category
if assigned_to.nil? && category && category.assigned_to
self.assigned_to = category.assigned_to
# Checks parent issue assignment
if @parent_issue
if @parent_issue.project_id != project_id
errors.add :parent_issue_id, :not_same_project
elsif !new_record?
# moving an existing issue
if @parent_issue.root_id != root_id
# we can always move to another tree
elsif move_possible?(@parent_issue)
# move accepted inside tree
else
errors.add :parent_issue_id, :not_a_valid_parent
end
end
end
end
# Set the done_ratio using the status if that setting is set. This will keep the done_ratios
# even if the user turns off the setting later
def update_done_ratio_from_issue_status
if Issue.use_status_for_done_ratio? && status && status.default_done_ratio?
if Issue.use_status_for_done_ratio? && status && status.default_done_ratio
self.done_ratio = status.default_done_ratio
end
end
def after_save
# Reload is needed in order to get the right status
reload
# Update start/due dates of following issues
relations_from.each(&:set_issue_to_dates)
# Close duplicates if the issue was closed
if @issue_before_change && !@issue_before_change.closed? && self.closed?
duplicates.each do |duplicate|
# Reload is need in case the duplicate was updated by a previous duplicate
duplicate.reload
# Don't re-close it if it's already closed
next if duplicate.closed?
# Same user and notes
duplicate.init_journal(@current_journal.user, @current_journal.notes)
duplicate.update_attribute :status, self.status
end
end
end
def init_journal(user, notes = "")
@current_journal ||= Journal.new(:journalized => self, :user => user, :notes => notes)
@issue_before_change = self.clone
@@ -273,15 +374,41 @@ class Issue < ActiveRecord::Base
end
false
end
# Return true if the issue is being closed
def closing?
if !new_record? && status_id_changed?
status_was = IssueStatus.find_by_id(status_id_was)
status_new = IssueStatus.find_by_id(status_id)
if status_was && status_new && !status_was.is_closed? && status_new.is_closed?
return true
end
end
false
end
# Returns true if the issue is overdue
def overdue?
!due_date.nil? && (due_date < Date.today) && !status.is_closed?
end
# Is the amount of work done less than it should for the due date
def behind_schedule?
return false if start_date.nil? || due_date.nil?
done_date = start_date + ((due_date - start_date+1)* done_ratio/100).floor
return done_date <= Date.today
end
# Does this issue have children?
def children?
!leaf?
end
# Users the issue can be assigned to
def assignable_users
project.assignable_users
users = project.assignable_users
users << author if author
users.uniq.sort
end
# Versions that the issue can be assigned to
@@ -295,9 +422,10 @@ class Issue < ActiveRecord::Base
end
# Returns an array of status that user is able to apply
def new_statuses_allowed_to(user)
def new_statuses_allowed_to(user, include_default=false)
statuses = status.find_new_statuses_allowed_to(user.roles_for_project(project), tracker)
statuses << status unless statuses.empty?
statuses << IssueStatus.default if include_default
statuses = statuses.uniq.sort
blocked? ? statuses.reject {|s| s.is_closed?} : statuses
end
@@ -305,22 +433,23 @@ class Issue < ActiveRecord::Base
# Returns the mail adresses of users that should be notified
def recipients
notified = project.notified_users
# Author and assignee are always notified unless they have been locked
notified << author if author && author.active?
notified << assigned_to if assigned_to && assigned_to.active?
# Author and assignee are always notified unless they have been
# locked or don't want to be notified
notified << author if author && author.active? && author.notify_about?(self)
notified << assigned_to if assigned_to && assigned_to.active? && assigned_to.notify_about?(self)
notified.uniq!
# Remove users that can not view the issue
notified.reject! {|user| !visible?(user)}
notified.collect(&:mail)
end
# Returns the total number of hours spent on this issue.
# Returns the total number of hours spent on this issue and its descendants
#
# Example:
# spent_hours => 0
# spent_hours => 50
# spent_hours => 0.0
# spent_hours => 50.2
def spent_hours
@spent_hours ||= time_entries.sum(:hours) || 0
@spent_hours ||= self_and_descendants.sum("#{TimeEntry.table_name}.hours", :include => :time_entries).to_f || 0.0
end
def relations
@@ -357,7 +486,34 @@ class Issue < ActiveRecord::Base
end
def soonest_start
@soonest_start ||= relations_to.collect{|relation| relation.successor_soonest_start}.compact.min
@soonest_start ||= (
relations_to.collect{|relation| relation.successor_soonest_start} +
ancestors.collect(&:soonest_start)
).compact.max
end
def reschedule_after(date)
return if date.nil?
if leaf?
if start_date.nil? || start_date < date
self.start_date, self.due_date = date, date + duration
save
end
else
leaves.each do |leaf|
leaf.reschedule_after(date)
end
end
end
def <=>(issue)
if issue.nil?
-1
elsif root_id != issue.root_id
(root_id || 0) <=> (issue.root_id || 0)
else
(lft || 0) <=> (issue.lft || 0)
end
end
def to_s
@@ -374,6 +530,42 @@ class Issue < ActiveRecord::Base
s
end
# Saves an issue, time_entry, attachments, and a journal from the parameters
# Returns false if save fails
def save_issue_with_child_records(params, existing_time_entry=nil)
Issue.transaction do
if params[:time_entry] && params[:time_entry][:hours].present? && User.current.allowed_to?(:log_time, project)
@time_entry = existing_time_entry || TimeEntry.new
@time_entry.project = project
@time_entry.issue = self
@time_entry.user = User.current
@time_entry.spent_on = Date.today
@time_entry.attributes = params[:time_entry]
self.time_entries << @time_entry
end
if valid?
attachments = Attachment.attach_files(self, params[:attachments])
attachments[:files].each {|a| @current_journal.details << JournalDetail.new(:property => 'attachment', :prop_key => a.id, :value => a.filename)}
# TODO: Rename hook
Redmine::Hook.call_hook(:controller_issues_edit_before_save, { :params => params, :issue => self, :time_entry => @time_entry, :journal => @current_journal})
begin
if save
# TODO: Rename hook
Redmine::Hook.call_hook(:controller_issues_edit_after_save, { :params => params, :issue => self, :time_entry => @time_entry, :journal => @current_journal})
else
raise ActiveRecord::Rollback
end
rescue ActiveRecord::StaleObjectError
attachments[:files].each(&:destroy)
errors.add_to_base l(:notice_locking_conflict)
raise ActiveRecord::Rollback
end
end
end
end
# Unassigns issues from +version+ if it's no longer shared with issue's project
def self.update_versions_from_sharing_change(version)
# Update issues assigned to the version
@@ -388,8 +580,189 @@ class Issue < ActiveRecord::Base
Issue.update_versions(["#{Version.table_name}.project_id IN (?) OR #{Issue.table_name}.project_id IN (?)", moved_project_ids, moved_project_ids])
end
def parent_issue_id=(arg)
parent_issue_id = arg.blank? ? nil : arg.to_i
if parent_issue_id && @parent_issue = Issue.find_by_id(parent_issue_id)
@parent_issue.id
else
@parent_issue = nil
nil
end
end
def parent_issue_id
if instance_variable_defined? :@parent_issue
@parent_issue.nil? ? nil : @parent_issue.id
else
parent_id
end
end
# Extracted from the ReportsController.
def self.by_tracker(project)
count_and_group_by(:project => project,
:field => 'tracker_id',
:joins => Tracker.table_name)
end
def self.by_version(project)
count_and_group_by(:project => project,
:field => 'fixed_version_id',
:joins => Version.table_name)
end
def self.by_priority(project)
count_and_group_by(:project => project,
:field => 'priority_id',
:joins => IssuePriority.table_name)
end
def self.by_category(project)
count_and_group_by(:project => project,
:field => 'category_id',
:joins => IssueCategory.table_name)
end
def self.by_assigned_to(project)
count_and_group_by(:project => project,
:field => 'assigned_to_id',
:joins => User.table_name)
end
def self.by_author(project)
count_and_group_by(:project => project,
:field => 'author_id',
:joins => User.table_name)
end
def self.by_subproject(project)
ActiveRecord::Base.connection.select_all("select s.id as status_id,
s.is_closed as closed,
i.project_id as project_id,
count(i.id) as total
from
#{Issue.table_name} i, #{IssueStatus.table_name} s
where
i.status_id=s.id
and i.project_id IN (#{project.descendants.active.collect{|p| p.id}.join(',')})
group by s.id, s.is_closed, i.project_id") if project.descendants.active.any?
end
# End ReportsController extraction
# Returns an array of projects that current user can move issues to
def self.allowed_target_projects_on_move
projects = []
if User.current.admin?
# admin is allowed to move issues to any active (visible) project
projects = Project.visible.all
elsif User.current.logged?
if Role.non_member.allowed_to?(:move_issues)
projects = Project.visible.all
else
User.current.memberships.each {|m| projects << m.project if m.roles.detect {|r| r.allowed_to?(:move_issues)}}
end
end
projects
end
private
def update_nested_set_attributes
if root_id.nil?
# issue was just created
self.root_id = (@parent_issue.nil? ? id : @parent_issue.root_id)
set_default_left_and_right
Issue.update_all("root_id = #{root_id}, lft = #{lft}, rgt = #{rgt}", ["id = ?", id])
if @parent_issue
move_to_child_of(@parent_issue)
end
reload
elsif parent_issue_id != parent_id
former_parent_id = parent_id
# moving an existing issue
if @parent_issue && @parent_issue.root_id == root_id
# inside the same tree
move_to_child_of(@parent_issue)
else
# to another tree
unless root?
move_to_right_of(root)
reload
end
old_root_id = root_id
self.root_id = (@parent_issue.nil? ? id : @parent_issue.root_id )
target_maxright = nested_set_scope.maximum(right_column_name) || 0
offset = target_maxright + 1 - lft
Issue.update_all("root_id = #{root_id}, lft = lft + #{offset}, rgt = rgt + #{offset}",
["root_id = ? AND lft >= ? AND rgt <= ? ", old_root_id, lft, rgt])
self[left_column_name] = lft + offset
self[right_column_name] = rgt + offset
if @parent_issue
move_to_child_of(@parent_issue)
end
end
reload
# delete invalid relations of all descendants
self_and_descendants.each do |issue|
issue.relations.each do |relation|
relation.destroy unless relation.valid?
end
end
# update former parent
recalculate_attributes_for(former_parent_id) if former_parent_id
end
remove_instance_variable(:@parent_issue) if instance_variable_defined?(:@parent_issue)
end
def update_parent_attributes
recalculate_attributes_for(parent_id) if parent_id
end
def recalculate_attributes_for(issue_id)
if issue_id && p = Issue.find_by_id(issue_id)
# priority = highest priority of children
if priority_position = p.children.maximum("#{IssuePriority.table_name}.position", :include => :priority)
p.priority = IssuePriority.find_by_position(priority_position)
end
# start/due dates = lowest/highest dates of children
p.start_date = p.children.minimum(:start_date)
p.due_date = p.children.maximum(:due_date)
if p.start_date && p.due_date && p.due_date < p.start_date
p.start_date, p.due_date = p.due_date, p.start_date
end
# done ratio = weighted average ratio of leaves
unless Issue.use_status_for_done_ratio? && p.status && p.status.default_done_ratio
leaves_count = p.leaves.count
if leaves_count > 0
average = p.leaves.average(:estimated_hours).to_f
if average == 0
average = 1
end
done = p.leaves.sum("COALESCE(estimated_hours, #{average}) * (CASE WHEN is_closed = #{connection.quoted_true} THEN 100 ELSE COALESCE(done_ratio, 0) END)", :include => :status).to_f
progress = done / (average * leaves_count)
p.done_ratio = progress.round
end
end
# estimate = sum of leaves estimates
p.estimated_hours = p.leaves.sum(:estimated_hours).to_f
p.estimated_hours = nil if p.estimated_hours == 0.0
# ancestors will be recursively updated
p.save(false)
end
end
def destroy_children
unless leaf?
children.each do |child|
child.destroy
end
end
end
# Update issues so their versions are not pointing to a
# fixed_version that is not shared with the issue's project
def self.update_versions(conditions=nil)
@@ -419,12 +792,45 @@ class Issue < ActiveRecord::Base
journal.save
end
# Default assignment based on category
def default_assign
if assigned_to.nil? && category && category.assigned_to
self.assigned_to = category.assigned_to
end
end
# Updates start/due dates of following issues
def reschedule_following_issues
if start_date_changed? || due_date_changed?
relations_from.each do |relation|
relation.set_issue_to_dates
end
end
end
# Closes duplicates if the issue is being closed
def close_duplicates
if closing?
duplicates.each do |duplicate|
# Reload is need in case the duplicate was updated by a previous duplicate
duplicate.reload
# Don't re-close it if it's already closed
next if duplicate.closed?
# Same user and notes
if @current_journal
duplicate.init_journal(@current_journal.user, @current_journal.notes)
end
duplicate.update_attribute :status, self.status
end
end
end
# Saves the changes in a Journal
# Called after_save
def create_journal
if @current_journal
# attributes changes
(Issue.column_names - %w(id description lock_version created_on updated_on)).each {|c|
(Issue.column_names - %w(id description root_id lft rgt lock_version created_on updated_on)).each {|c|
@current_journal.details << JournalDetail.new(:property => 'attr',
:prop_key => c,
:old_value => @issue_before_change.send(c),
@@ -440,6 +846,37 @@ class Issue < ActiveRecord::Base
:value => c.value)
}
@current_journal.save
# reset current journal
init_journal @current_journal.user, @current_journal.notes
end
end
# Query generator for selecting groups of issue counts for a project
# based on specific criteria
#
# Options
# * project - Project to search in.
# * field - String. Issue field to key off of in the grouping.
# * joins - String. The table name to join against.
def self.count_and_group_by(options)
project = options.delete(:project)
select_field = options.delete(:field)
joins = options.delete(:joins)
where = "i.#{select_field}=j.id"
ActiveRecord::Base.connection.select_all("select s.id as status_id,
s.is_closed as closed,
j.id as #{select_field},
count(i.id) as total
from
#{Issue.table_name} i, #{IssueStatus.table_name} s, #{joins} j
where
i.status_id=s.id
and #{where}
and i.project_id=#{project.id}
group by s.id, s.is_closed, j.id")
end
end

View File

@@ -21,15 +21,19 @@ class IssueRelation < ActiveRecord::Base
TYPE_RELATES = "relates"
TYPE_DUPLICATES = "duplicates"
TYPE_DUPLICATED = "duplicated"
TYPE_BLOCKS = "blocks"
TYPE_BLOCKED = "blocked"
TYPE_PRECEDES = "precedes"
TYPE_FOLLOWS = "follows"
TYPES = { TYPE_RELATES => { :name => :label_relates_to, :sym_name => :label_relates_to, :order => 1 },
TYPE_DUPLICATES => { :name => :label_duplicates, :sym_name => :label_duplicated_by, :order => 2 },
TYPE_BLOCKS => { :name => :label_blocks, :sym_name => :label_blocked_by, :order => 3 },
TYPE_PRECEDES => { :name => :label_precedes, :sym_name => :label_follows, :order => 4 },
TYPE_FOLLOWS => { :name => :label_follows, :sym_name => :label_precedes, :order => 5 }
TYPES = { TYPE_RELATES => { :name => :label_relates_to, :sym_name => :label_relates_to, :order => 1, :sym => TYPE_RELATES },
TYPE_DUPLICATES => { :name => :label_duplicates, :sym_name => :label_duplicated_by, :order => 2, :sym => TYPE_DUPLICATED },
TYPE_DUPLICATED => { :name => :label_duplicated_by, :sym_name => :label_duplicates, :order => 3, :sym => TYPE_DUPLICATES, :reverse => TYPE_DUPLICATES },
TYPE_BLOCKS => { :name => :label_blocks, :sym_name => :label_blocked_by, :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 }
}.freeze
validates_presence_of :issue_from, :issue_to, :relation_type
@@ -44,6 +48,7 @@ class IssueRelation < ActiveRecord::Base
errors.add :issue_to_id, :invalid if issue_from_id == issue_to_id
errors.add :issue_to_id, :not_same_project unless issue_from.project_id == issue_to.project_id || Setting.cross_project_issue_relations?
errors.add_to_base :circular_dependency if issue_to.all_dependent_issues.include? issue_from
errors.add_to_base :cant_link_an_issue_with_a_descendant if issue_from.is_descendant_of?(issue_to) || issue_from.is_ancestor_of?(issue_to)
end
end
@@ -51,6 +56,17 @@ class IssueRelation < ActiveRecord::Base
(self.issue_from_id == issue.id) ? issue_to : issue_from
end
# Returns the relation type for +issue+
def relation_type_for(issue)
if TYPES[relation_type]
if self.issue_from_id == issue.id
relation_type
else
TYPES[relation_type][:sym]
end
end
end
def label_for(issue)
TYPES[relation_type] ? TYPES[relation_type][(self.issue_from_id == issue.id) ? :name : :sym_name] : :unknow
end
@@ -68,15 +84,15 @@ class IssueRelation < ActiveRecord::Base
def set_issue_to_dates
soonest_start = self.successor_soonest_start
if soonest_start && (!issue_to.start_date || issue_to.start_date < soonest_start)
issue_to.start_date, issue_to.due_date = successor_soonest_start, successor_soonest_start + issue_to.duration
issue_to.save
if soonest_start && issue_to
issue_to.reschedule_after(soonest_start)
end
end
def successor_soonest_start
return nil unless (TYPE_PRECEDES == self.relation_type) && (issue_from.start_date || issue_from.due_date)
(issue_from.due_date || issue_from.start_date) + 1 + delay
if (TYPE_PRECEDES == self.relation_type) && delay && issue_from && (issue_from.start_date || issue_from.due_date)
(issue_from.due_date || issue_from.start_date) + 1 + delay
end
end
def <=>(relation)
@@ -85,12 +101,13 @@ class IssueRelation < ActiveRecord::Base
private
# Reverses the relation if needed so that it gets stored in the proper way
def reverse_if_needed
if (TYPE_FOLLOWS == relation_type)
if TYPES.has_key?(relation_type) && TYPES[relation_type][:reverse]
issue_tmp = issue_to
self.issue_to = issue_from
self.issue_from = issue_tmp
self.relation_type = TYPE_PRECEDES
self.relation_type = TYPES[relation_type][:reverse]
end
end
end

View File

@@ -17,8 +17,10 @@
class IssueStatus < ActiveRecord::Base
before_destroy :check_integrity
has_many :workflows, :foreign_key => "old_status_id", :dependent => :delete_all
has_many :workflows, :foreign_key => "old_status_id"
acts_as_list
before_destroy :delete_workflows
validates_presence_of :name
validates_uniqueness_of :name
@@ -89,4 +91,9 @@ private
def check_integrity
raise "Can't delete status" if Issue.find(:first, :conditions => ["status_id=?", self.id])
end
# Deletes associated workflows
def delete_workflows
Workflow.delete_all(["old_status_id = :id OR new_status_id = :id", {:id => id}])
end
end

View File

@@ -65,4 +65,12 @@ class Journal < ActiveRecord::Base
def attachments
journalized.respond_to?(:attachments) ? journalized.attachments : nil
end
# Returns a string of css classes
def css_classes
s = 'journal'
s << ' has-notes' unless notes.blank?
s << ' has-details' unless details.blank?
s
end
end

View File

@@ -17,6 +17,11 @@
class JournalObserver < ActiveRecord::Observer
def after_create(journal)
Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
if Setting.notified_events.include?('issue_updated') ||
(Setting.notified_events.include?('issue_note_added') && journal.notes.present?) ||
(Setting.notified_events.include?('issue_status_updated') && journal.new_status.present?) ||
(Setting.notified_events.include?('issue_priority_updated') && journal.new_value_for('priority_id').present?)
Mailer.deliver_issue_edit(journal)
end
end
end

View File

@@ -17,6 +17,7 @@
class MailHandler < ActionMailer::Base
include ActionView::Helpers::SanitizeHelper
include Redmine::I18n
class UnauthorizedAction < StandardError; end
class MissingInformation < StandardError; end
@@ -49,7 +50,7 @@ class MailHandler < ActionMailer::Base
logger.info "MailHandler: ignoring email from Redmine emission address [#{sender_email}]" if logger && logger.info
return false
end
@user = User.find_by_mail(sender_email)
@user = User.find_by_mail(sender_email) if sender_email.present?
if @user && !@user.active?
logger.info "MailHandler: ignoring email from non-active user [#{@user.login}]" if logger && logger.info
return false
@@ -99,7 +100,7 @@ class MailHandler < ActionMailer::Base
elsif m = email.subject.match(MESSAGE_REPLY_SUBJECT_RE)
receive_message_reply(m[1].to_i)
else
receive_issue
dispatch_to_default
end
rescue ActiveRecord::RecordInvalid => e
# TODO: send a email to the user
@@ -112,38 +113,28 @@ class MailHandler < ActionMailer::Base
logger.error "MailHandler: unauthorized attempt from #{user}" if logger
false
end
def dispatch_to_default
receive_issue
end
# Creates a new issue
def receive_issue
project = target_project
tracker = (get_keyword(:tracker) && project.trackers.find_by_name(get_keyword(:tracker))) || project.trackers.find(:first)
category = (get_keyword(:category) && project.issue_categories.find_by_name(get_keyword(:category)))
priority = (get_keyword(:priority) && IssuePriority.find_by_name(get_keyword(:priority)))
status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status)))
# check permission
unless @@handler_options[:no_permission_check]
raise UnauthorizedAction unless user.allowed_to?(:add_issues, project)
end
issue = Issue.new(:author => user, :project => project, :tracker => tracker, :category => category, :priority => priority)
# check workflow
if status && issue.new_statuses_allowed_to(user).include?(status)
issue.status = status
end
issue.subject = email.subject.chomp
issue.subject = issue.subject.toutf8 if issue.subject.respond_to?(:toutf8)
issue = Issue.new(:author => user, :project => project)
issue.safe_attributes = issue_attributes_from_keywords(issue)
issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
issue.subject = email.subject.to_s.chomp[0,255]
if issue.subject.blank?
issue.subject = '(no subject)'
end
# custom fields
issue.custom_field_values = issue.available_custom_fields.inject({}) do |h, c|
if value = get_keyword(c.name, :override => true)
h[c.id] = value
end
h
end
issue.description = cleaned_up_text_body
# add To and Cc as watchers before saving so the watchers can reply to Redmine
add_watchers(issue)
issue.save!
@@ -152,34 +143,22 @@ class MailHandler < ActionMailer::Base
issue
end
def target_project
# TODO: other ways to specify project:
# * parse the email To field
# * specific project (eg. Setting.mail_handler_target_project)
target = Project.find_by_identifier(get_keyword(:project))
raise MissingInformation.new('Unable to determine target project') if target.nil?
target
end
# Adds a note to an existing issue
def receive_issue_reply(issue_id)
status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status)))
issue = Issue.find_by_id(issue_id)
return unless issue
# check permission
unless @@handler_options[:no_permission_check]
raise UnauthorizedAction unless user.allowed_to?(:add_issue_notes, issue.project) || user.allowed_to?(:edit_issues, issue.project)
raise UnauthorizedAction unless status.nil? || user.allowed_to?(:edit_issues, issue.project)
end
# add the note
# ignore CLI-supplied defaults for new issues
@@handler_options[:issue].clear
journal = issue.init_journal(user, cleaned_up_text_body)
issue.safe_attributes = issue_attributes_from_keywords(issue)
issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
add_attachments(issue)
# check workflow
if status && issue.new_statuses_allowed_to(user).include?(status)
issue.status = status
end
issue.save!
logger.info "MailHandler: issue ##{issue.id} updated by #{user}" if logger && logger.info
journal
@@ -246,8 +225,8 @@ class MailHandler < ActionMailer::Base
@keywords[attr]
else
@keywords[attr] = begin
if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) && plain_text_body.gsub!(/^#{attr}[ \t]*:[ \t]*(.+)\s*$/i, '')
$1.strip
if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) && (v = extract_keyword!(plain_text_body, attr, options[:format]))
v
elsif !@@handler_options[:issue][attr].blank?
@@handler_options[:issue][attr]
end
@@ -255,6 +234,65 @@ class MailHandler < ActionMailer::Base
end
end
# Destructively extracts the value for +attr+ in +text+
# Returns nil if no matching keyword found
def extract_keyword!(text, attr, format=nil)
keys = [attr.to_s.humanize]
if attr.is_a?(Symbol)
keys << l("field_#{attr}", :default => '', :locale => user.language) if user
keys << l("field_#{attr}", :default => '', :locale => Setting.default_language)
end
keys.reject! {|k| k.blank?}
keys.collect! {|k| Regexp.escape(k)}
format ||= '.+'
text.gsub!(/^(#{keys.join('|')})[ \t]*:[ \t]*(#{format})\s*$/i, '')
$2 && $2.strip
end
def target_project
# TODO: other ways to specify project:
# * parse the email To field
# * specific project (eg. Setting.mail_handler_target_project)
target = Project.find_by_identifier(get_keyword(:project))
raise MissingInformation.new('Unable to determine target project') if target.nil?
target
end
# Returns a Hash of issue attributes extracted from keywords in the email body
def issue_attributes_from_keywords(issue)
assigned_to = (k = get_keyword(:assigned_to, :override => true)) && find_user_from_keyword(k)
assigned_to = nil if assigned_to && !issue.assignable_users.include?(assigned_to)
attrs = {
'tracker_id' => (k = get_keyword(:tracker)) && issue.project.trackers.find_by_name(k).try(:id),
'status_id' => (k = get_keyword(:status)) && IssueStatus.find_by_name(k).try(:id),
'priority_id' => (k = get_keyword(:priority)) && IssuePriority.find_by_name(k).try(:id),
'category_id' => (k = get_keyword(:category)) && issue.project.issue_categories.find_by_name(k).try(:id),
'assigned_to_id' => assigned_to.try(:id),
'fixed_version_id' => (k = get_keyword(:fixed_version, :override => true)) && issue.project.shared_versions.find_by_name(k).try(:id),
'start_date' => get_keyword(:start_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'),
'due_date' => get_keyword(:due_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'),
'estimated_hours' => get_keyword(:estimated_hours, :override => true),
'done_ratio' => get_keyword(:done_ratio, :override => true, :format => '(\d|10)?0')
}.delete_if {|k, v| v.blank? }
if issue.new_record? && attrs['tracker_id'].nil?
attrs['tracker_id'] = issue.project.trackers.find(:first).try(:id)
end
attrs
end
# Returns a Hash of issue custom field values extracted from keywords in the email body
def custom_field_values_from_keywords(customized)
customized.custom_field_values.inject({}) do |h, v|
if value = get_keyword(v.custom_field.name, :override => true)
h[v.custom_field.id.to_s] = value
end
h
end
end
# Returns the text/plain part of the email
# If not found (eg. HTML-only email), returns the body with tags removed
def plain_text_body
@@ -309,9 +347,19 @@ class MailHandler < ActionMailer::Base
def cleanup_body(body)
delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?).map {|s| Regexp.escape(s)}
unless delimiters.empty?
regex = Regexp.new("^(#{ delimiters.join('|') })\s*[\r\n].*", Regexp::MULTILINE)
regex = Regexp.new("^[> ]*(#{ delimiters.join('|') })\s*[\r\n].*", Regexp::MULTILINE)
body = body.gsub(regex, '')
end
body.strip
end
def find_user_from_keyword(keyword)
user ||= User.find_by_mail(keyword)
user ||= User.find_by_login(keyword)
if user.nil? && keyword.match(/ /)
firstname, lastname = *(keyword.split) # "First Last Throwaway"
user ||= User.find_by_firstname_and_lastname(firstname, lastname)
end
user
end
end

View File

@@ -15,6 +15,8 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'ar_condition'
class Mailer < ActionMailer::Base
layout 'mailer'
helper :application
@@ -80,7 +82,7 @@ class Mailer < ActionMailer::Base
def reminder(user, issues, days)
set_language_if_valid user.language
recipients user.mail
subject l(:mail_subject_reminder, issues.size)
subject l(:mail_subject_reminder, :count => issues.size, :days => days)
body :issues => issues,
:days => days,
:issues_url => url_for(:controller => 'issues', :action => 'index', :set_filter => 1, :assigned_to_id => user.id, :sort_key => 'due_date', :sort_order => 'asc')
@@ -114,11 +116,11 @@ class Mailer < ActionMailer::Base
when 'Project'
added_to_url = url_for(:controller => 'projects', :action => 'list_files', :id => container)
added_to = "#{l(:label_project)}: #{container}"
recipients container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)}
recipients container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)}.collect {|u| u.mail}
when 'Version'
added_to_url = url_for(:controller => 'projects', :action => 'list_files', :id => container.project_id)
added_to = "#{l(:label_version)}: #{container.name}"
recipients container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)}
recipients container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)}.collect {|u| u.mail}
when 'Document'
added_to_url = url_for(:controller => 'documents', :action => 'show', :id => container.id)
added_to = "#{l(:label_document)}: #{container.title}"
@@ -161,7 +163,7 @@ class Mailer < ActionMailer::Base
cc((message.root.watcher_recipients + message.board.watcher_recipients).uniq - @recipients)
subject "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] #{message.subject}"
body :message => message,
:message_url => url_for(:controller => 'messages', :action => 'show', :board_id => message.board_id, :id => message.root)
:message_url => url_for(message.event_url)
render_multipart('message_posted', body)
end
@@ -176,9 +178,9 @@ class Mailer < ActionMailer::Base
message_id wiki_content
recipients wiki_content.recipients
cc(wiki_content.page.wiki.watcher_recipients - recipients)
subject "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_added, :page => wiki_content.page.pretty_title)}"
subject "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_added, :id => wiki_content.page.pretty_title)}"
body :wiki_content => wiki_content,
:wiki_content_url => url_for(:controller => 'wiki', :action => 'index', :id => wiki_content.project, :page => wiki_content.page.title)
:wiki_content_url => url_for(:controller => 'wiki', :action => 'show', :project_id => wiki_content.project, :id => wiki_content.page.title)
render_multipart('wiki_content_added', body)
end
@@ -193,10 +195,10 @@ class Mailer < ActionMailer::Base
message_id wiki_content
recipients wiki_content.recipients
cc(wiki_content.page.wiki.watcher_recipients + wiki_content.page.watcher_recipients - recipients)
subject "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_updated, :page => wiki_content.page.pretty_title)}"
subject "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_updated, :id => wiki_content.page.pretty_title)}"
body :wiki_content => wiki_content,
:wiki_content_url => url_for(:controller => 'wiki', :action => 'index', :id => wiki_content.project, :page => wiki_content.page.title),
:wiki_diff_url => url_for(:controller => 'wiki', :action => 'diff', :id => wiki_content.project, :page => wiki_content.page.title, :version => wiki_content.version)
:wiki_content_url => url_for(:controller => 'wiki', :action => 'show', :project_id => wiki_content.project, :id => wiki_content.page.title),
:wiki_diff_url => url_for(:controller => 'wiki', :action => 'diff', :project_id => wiki_content.project, :id => wiki_content.page.title, :version => wiki_content.version)
render_multipart('wiki_content_updated', body)
end
@@ -272,6 +274,7 @@ class Mailer < ActionMailer::Base
# Overrides default deliver! method to prevent from sending an email
# with no recipient, cc or bcc
def deliver!(mail = @mail)
set_language_if_valid @initial_language
return false if (recipients.nil? || recipients.empty?) &&
(cc.nil? || cc.empty?) &&
(bcc.nil? || bcc.empty?)
@@ -283,7 +286,21 @@ class Mailer < ActionMailer::Base
if @references_objects
mail.references = @references_objects.collect {|o| self.class.message_id_for(o)}
end
super(mail)
# Log errors when raise_delivery_errors is set to false, Rails does not
raise_errors = self.class.raise_delivery_errors
self.class.raise_delivery_errors = true
begin
return super(mail)
rescue Exception => e
if raise_errors
raise e
elsif mylogger
mylogger.error "The following error occured while sending email notification: \"#{e.message}\". Check your configuration in config/email.yml."
end
ensure
self.class.raise_delivery_errors = raise_errors
end
end
# Sends reminders to issue assignees
@@ -291,13 +308,16 @@ class Mailer < ActionMailer::Base
# * :days => how many days in the future to remind about (defaults to 7)
# * :tracker => id of tracker for filtering issues (defaults to all trackers)
# * :project => id or identifier of project to process (defaults to all projects)
# * :users => array of user ids who should be reminded
def self.reminders(options={})
days = options[:days] || 7
project = options[:project] ? Project.find(options[:project]) : nil
tracker = options[:tracker] ? Tracker.find(options[:tracker]) : nil
user_ids = options[:users]
s = ARCondition.new ["#{IssueStatus.table_name}.is_closed = ? AND #{Issue.table_name}.due_date <= ?", false, days.day.from_now.to_date]
s << "#{Issue.table_name}.assigned_to_id IS NOT NULL"
s << ["#{Issue.table_name}.assigned_to_id IN (?)", user_ids] if user_ids.present?
s << "#{Project.table_name}.status = #{Project::STATUS_ACTIVE}"
s << "#{Issue.table_name}.project_id = #{project.id}" if project
s << "#{Issue.table_name}.tracker_id = #{tracker.id}" if tracker
@@ -309,10 +329,20 @@ class Mailer < ActionMailer::Base
deliver_reminder(assignee, issues, days) unless assignee.nil?
end
end
# Activates/desactivates email deliveries during +block+
def self.with_deliveries(enabled = true, &block)
was_enabled = ActionMailer::Base.perform_deliveries
ActionMailer::Base.perform_deliveries = !!enabled
yield
ensure
ActionMailer::Base.perform_deliveries = was_enabled
end
private
def initialize_defaults(method_name)
super
@initial_language = current_language
set_language_if_valid Setting.default_language
from Setting.mail_from
@@ -338,9 +368,14 @@ class Mailer < ActionMailer::Base
recipients.delete(@author.mail) if recipients
cc.delete(@author.mail) if cc
end
notified_users = [recipients, cc].flatten.compact.uniq
# Rails would log recipients only, not cc and bcc
mylogger.info "Sending email notification to: #{notified_users.join(', ')}" if mylogger
# Blind carbon copy recipients
if Setting.bcc_recipients?
bcc([recipients, cc].flatten.compact.uniq)
bcc(notified_users)
recipients []
cc []
end
@@ -391,6 +426,10 @@ class Mailer < ActionMailer::Base
@references_objects ||= []
@references_objects << object
end
def mylogger
RAILS_DEFAULT_LOGGER
end
end
# Patch TMail so that message_id is not overwritten

View File

@@ -57,17 +57,32 @@ class Member < ActiveRecord::Base
member_roles.detect {|mr| mr.inherited_from}.nil?
end
def include?(user)
if principal.is_a?(Group)
!user.nil? && user.groups.include?(principal)
else
self.user == user
end
end
def before_destroy
if user
# remove category based auto assignments for this member
IssueCategory.update_all "assigned_to_id = NULL", ["project_id = ? AND assigned_to_id = ?", project.id, user.id]
end
end
# Find or initilize a Member with an id, attributes, and for a Principal
def self.edit_membership(id, new_attributes, principal=nil)
@membership = id.present? ? Member.find(id) : Member.new(:principal => principal)
@membership.attributes = new_attributes
@membership
end
protected
def validate
errors.add_to_base "Role can't be blank" if member_roles.empty? && roles.empty?
errors.add_on_empty :role if member_roles.empty? && roles.empty?
end
private

View File

@@ -30,6 +30,10 @@ class MemberRole < ActiveRecord::Base
errors.add :role_id, :invalid if role && !role.member?
end
def inherited?
!inherited_from.nil?
end
private
def remove_member_if_empty

View File

@@ -30,7 +30,7 @@ class Message < ActiveRecord::Base
:description => :content,
:type => Proc.new {|o| o.parent_id.nil? ? 'message' : 'reply'},
:url => Proc.new {|o| {:controller => 'messages', :action => 'show', :board_id => o.board_id}.merge(o.parent_id.nil? ? {:id => o.id} :
{:id => o.parent_id, :anchor => "message-#{o.id}"})}
{:id => o.parent_id, :r => o.id, :anchor => "message-#{o.id}"})}
acts_as_activity_provider :find_options => {:include => [{:board => :project}, :author]},
:author_key => :author_id
@@ -90,13 +90,6 @@ class Message < ActiveRecord::Base
usr && usr.logged? && (usr.allowed_to?(:delete_messages, project) || (self.author == usr && usr.allowed_to?(:delete_own_messages, project)))
end
# Returns the mail adresses of users that should be notified
def recipients
notified = project.notified_users
notified.reject! {|user| !visible?(user)}
notified.collect(&:mail)
end
private
def add_author_as_watcher

View File

@@ -29,17 +29,15 @@ class News < ActiveRecord::Base
acts_as_activity_provider :find_options => {:include => [:project, :author]},
:author_key => :author_id
named_scope :visible, lambda {|*args| {
:include => :project,
:conditions => Project.allowed_to_condition(args.first || User.current, :view_news)
}}
def visible?(user=User.current)
!user.nil? && user.allowed_to?(:view_news, project)
end
# Returns the mail adresses of users that should be notified
def recipients
notified = project.notified_users
notified.reject! {|user| !visible?(user)}
notified.collect(&:mail)
end
# returns latest news for projects visible by user
def self.latest(user = User.current, count = 5)
find(:all, :limit => count, :conditions => Project.allowed_to_condition(user, :view_news), :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")

View File

@@ -16,7 +16,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class Principal < ActiveRecord::Base
set_table_name 'users'
set_table_name "#{table_name_prefix}users#{table_name_suffix}"
has_many :members, :foreign_key => 'user_id', :dependent => :destroy
has_many :memberships, :class_name => 'Member', :foreign_key => 'user_id', :include => [ :project, :roles ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name"
@@ -32,6 +32,12 @@ class Principal < ActiveRecord::Base
}
}
before_create :set_default_empty_values
def name(formatter = nil)
to_s
end
def <=>(principal)
if self.class.name == principal.class.name
self.to_s.downcase <=> principal.to_s.downcase
@@ -40,4 +46,16 @@ class Principal < ActiveRecord::Base
principal.class.name <=> self.class.name
end
end
protected
# Make sure we don't try to insert NULL values (see #4632)
def set_default_empty_values
self.login ||= ''
self.hashed_password ||= ''
self.firstname ||= ''
self.lastname ||= ''
self.mail ||= ''
true
end
end

View File

@@ -16,13 +16,19 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class Project < ActiveRecord::Base
include Redmine::SafeAttributes
# Project statuses
STATUS_ACTIVE = 1
STATUS_ARCHIVED = 9
# Maximum length for project identifiers
IDENTIFIER_MAX_LENGTH = 100
# Specific overidden Activities
has_many :time_entry_activities
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 :member_principals, :class_name => 'Member',
:include => :principal,
:conditions => "#{Principal.table_name}.type='Group' OR (#{Principal.table_name}.type='User' AND #{Principal.table_name}.status=#{User::STATUS_ACTIVE})"
@@ -50,36 +56,54 @@ class Project < ActiveRecord::Base
:join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}",
:association_foreign_key => 'custom_field_id'
acts_as_nested_set :order => 'name', :dependent => :destroy
acts_as_nested_set :order => 'name'
acts_as_attachable :view_permission => :view_files,
:delete_permission => :manage_files
acts_as_customizable
acts_as_searchable :columns => ['name', 'description'], :project_key => 'id', :permission => nil
acts_as_searchable :columns => ['name', 'identifier', 'description'], :project_key => 'id', :permission => nil
acts_as_event :title => Proc.new {|o| "#{l(:label_project)}: #{o.name}"},
:url => Proc.new {|o| {:controller => 'projects', :action => 'show', :id => o.id}},
:url => Proc.new {|o| {:controller => 'projects', :action => 'show', :id => o}},
:author => nil
attr_protected :status, :enabled_module_names
attr_protected :status
validates_presence_of :name, :identifier
validates_uniqueness_of :name, :identifier
validates_uniqueness_of :identifier
validates_associated :repository, :wiki
validates_length_of :name, :maximum => 30
validates_length_of :name, :maximum => 255
validates_length_of :homepage, :maximum => 255
validates_length_of :identifier, :in => 1..20
validates_length_of :identifier, :in => 1..IDENTIFIER_MAX_LENGTH
# donwcase letters, digits, dashes but not digits only
validates_format_of :identifier, :with => /^(?!\d+$)[a-z0-9\-]*$/, :if => Proc.new { |p| p.identifier_changed? }
# reserved words
validates_exclusion_of :identifier, :in => %w( new )
before_destroy :delete_all_members
before_destroy :delete_all_members, :destroy_children
named_scope :has_module, lambda { |mod| { :conditions => ["#{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name=?)", mod.to_s] } }
named_scope :active, { :conditions => "#{Project.table_name}.status = #{STATUS_ACTIVE}"}
named_scope :all_public, { :conditions => { :is_public => true } }
named_scope :visible, lambda { { :conditions => Project.visible_by(User.current) } }
def initialize(attributes = nil)
super
initialized = (attributes || {}).stringify_keys
if !initialized.key?('identifier') && Setting.sequential_project_identifiers?
self.identifier = Project.next_identifier
end
if !initialized.key?('is_public')
self.is_public = Setting.default_projects_public?
end
if !initialized.key?('enabled_module_names')
self.enabled_module_names = Setting.default_projects_modules
end
if !initialized.key?('trackers') && !initialized.key?('tracker_ids')
self.trackers = Tracker.all
end
end
def identifier=(identifier)
super unless identifier_frozen?
end
@@ -219,6 +243,10 @@ class Project < ActiveRecord::Base
self.status == STATUS_ACTIVE
end
def archived?
self.status == STATUS_ARCHIVED
end
# Archives the project and its descendants
def archive
# Check that there is no issue of a non descendant project that is assigned
@@ -248,7 +276,7 @@ class Project < ActiveRecord::Base
return @allowed_parents if @allowed_parents
@allowed_parents = Project.find(:all, :conditions => Project.allowed_to_condition(User.current, :add_subprojects))
@allowed_parents = @allowed_parents - self_and_descendants
if User.current.allowed_to?(:add_project, nil, :global => true)
if User.current.allowed_to?(:add_project, nil, :global => true) || (!new_record? && parent.nil?)
@allowed_parents << nil
end
unless parent.nil? || @allowed_parents.empty? || @allowed_parents.include?(parent)
@@ -335,6 +363,13 @@ class Project < ActiveRecord::Base
end
end
end
# Returns a scope of the Versions on subprojects
def rolled_up_versions
@rolled_up_versions ||=
Version.scoped(:include => :project,
:conditions => ["#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status = #{STATUS_ACTIVE}", lft, rgt])
end
# Returns a scope of the Versions used by the project
def shared_versions
@@ -374,12 +409,13 @@ class Project < ActiveRecord::Base
# Returns the mail adresses of users that should be always notified on project events
def recipients
members.select {|m| m.mail_notification? || m.user.mail_notification?}.collect {|m| m.user.mail}
notified_users.collect {|user| user.mail}
end
# Returns the users that should be notified on project events
def notified_users
members.select {|m| m.mail_notification? || m.user.mail_notification?}.collect {|m| m.user}
# TODO: User part should be extracted to User#notify_about?
members.select {|m| m.mail_notification? || m.user.mail_notification == 'all'}.collect {|m| m.user}
end
# Returns an array of all custom fields enabled for project issues
@@ -404,6 +440,54 @@ class Project < ActiveRecord::Base
def short_description(length = 255)
description.gsub(/^(.{#{length}}[^\n\r]*).*$/m, '\1...').strip if description
end
def css_classes
s = 'project'
s << ' root' if root?
s << ' child' if child?
s << (leaf? ? ' leaf' : ' parent')
s
end
# The earliest start date of a project, based on it's issues and versions
def start_date
[
issues.minimum('start_date'),
shared_versions.collect(&:effective_date),
shared_versions.collect(&:start_date)
].flatten.compact.min
end
# The latest due date of an issue or version
def due_date
[
issues.maximum('due_date'),
shared_versions.collect(&:effective_date),
shared_versions.collect {|v| v.fixed_issues.maximum('due_date')}
].flatten.compact.max
end
def overdue?
active? && !due_date.nil? && (due_date < Date.today)
end
# Returns the percent completed for this project, based on the
# progress on it's versions.
def completed_percent(options={:include_subprojects => false})
if options.delete(:include_subprojects)
total = self_and_descendants.collect(&:completed_percent).sum
total / self_and_descendants.count
else
if versions.count > 0
total = versions.collect(&:completed_pourcent).sum
total / versions.count
else
100
end
end
end
# Return true if this project is allowed to do the specified action.
# action can be:
@@ -424,7 +508,7 @@ class Project < ActiveRecord::Base
def enabled_module_names=(module_names)
if module_names && module_names.is_a?(Array)
module_names = module_names.collect(&:to_s)
module_names = module_names.collect(&:to_s).reject(&:blank?)
# remove disabled modules
enabled_modules.each {|mod| mod.destroy unless module_names.include?(mod.name)}
# add new modules
@@ -434,6 +518,33 @@ class Project < ActiveRecord::Base
end
end
# Returns an array of the enabled modules names
def enabled_module_names
enabled_modules.collect(&:name)
end
safe_attributes 'name',
'description',
'homepage',
'is_public',
'identifier',
'custom_field_values',
'custom_fields',
'tracker_ids',
'issue_custom_field_ids'
safe_attributes 'enabled_module_names',
:if => lambda {|project, user| project.new_record? || user.allowed_to?(:select_project_modules, project) }
# Returns an array of projects that are in this project's hierarchy
#
# Example: parents, children, siblings
def hierarchy
parents = project.self_and_ancestors || []
descendants = project.descendants || []
project_hierarchy = parents | descendants # Set union
end
# Returns an auto-generated project identifier based on the last identifier used
def self.next_identifier
p = Project.find(:first, :order => 'created_on DESC')
@@ -495,20 +606,51 @@ class Project < ActiveRecord::Base
return nil
end
end
# Yields the given block for each project with its level in the tree
def self.project_tree(projects, &block)
ancestors = []
projects.sort_by(&:lft).each do |project|
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
ancestors.pop
end
yield project, ancestors.size
ancestors << project
end
end
private
# Destroys children before destroying self
def destroy_children
children.each do |child|
child.destroy
end
end
# Copies wiki from +project+
def copy_wiki(project)
# Check that the source project has a wiki first
unless project.wiki.nil?
self.wiki ||= Wiki.new
wiki.attributes = project.wiki.attributes.dup.except("id", "project_id")
wiki_pages_map = {}
project.wiki.pages.each do |page|
# Skip pages without content
next if page.content.nil?
new_wiki_content = WikiContent.new(page.content.attributes.dup.except("id", "page_id", "updated_on"))
new_wiki_page = WikiPage.new(page.attributes.dup.except("id", "wiki_id", "created_on", "parent_id"))
new_wiki_page.content = new_wiki_content
wiki.pages << new_wiki_page
wiki_pages_map[page.id] = new_wiki_page
end
wiki.save
# Reproduce page hierarchy
project.wiki.pages.each do |page|
if page.parent_id && wiki_pages_map[page.id]
wiki_pages_map[page.id].parent = wiki_pages_map[page.parent_id]
wiki_pages_map[page.id].save
end
end
end
end
@@ -537,9 +679,12 @@ class Project < ActiveRecord::Base
# value. Used to map the two togeather for issue relations.
issues_map = {}
project.issues.each do |issue|
# Get issues sorted by root_id, lft so that parent issues
# get copied before their children
project.issues.find(:all, :order => 'root_id, lft').each do |issue|
new_issue = Issue.new
new_issue.copy_from(issue)
new_issue.project = self
# Reassign fixed_versions by name, since names are unique per
# project and the versions for self are not yet saved
if issue.fixed_version
@@ -550,13 +695,28 @@ class Project < ActiveRecord::Base
if issue.category
new_issue.category = self.issue_categories.select {|c| c.name == issue.category.name}.first
end
# Parent issue
if issue.parent_id
if copied_parent = issues_map[issue.parent_id]
new_issue.parent_issue_id = copied_parent.id
end
end
self.issues << new_issue
issues_map[issue.id] = new_issue
if new_issue.new_record?
logger.info "Project#copy_issues: issue ##{issue.id} could not be copied: #{new_issue.errors.full_messages}" if logger && logger.info
else
issues_map[issue.id] = new_issue unless new_issue.new_record?
end
end
# Relations after in case issues related each other
project.issues.each do |issue|
new_issue = issues_map[issue.id]
unless new_issue
# Issue was not copied
next
end
# Relations
issue.relations_from.each do |source_relation|
@@ -583,10 +743,19 @@ class Project < ActiveRecord::Base
# Copies members from +project+
def copy_members(project)
project.members.each do |member|
# Copy users first, then groups to handle members with inherited and given roles
members_to_copy = []
members_to_copy += project.memberships.select {|m| m.principal.is_a?(User)}
members_to_copy += project.memberships.select {|m| !m.principal.is_a?(User)}
members_to_copy.each do |member|
new_member = Member.new
new_member.attributes = member.attributes.dup.except("id", "project_id", "created_on")
new_member.role_ids = member.role_ids.dup
# only copy non inherited roles
# inherited roles will be added when copying the group membership
role_ids = member.member_roles.reject(&:inherited?).collect(&:role_id)
next if role_ids.empty?
new_member.role_ids = role_ids
new_member.project = self
self.members << new_member
end
@@ -615,7 +784,7 @@ class Project < ActiveRecord::Base
def allowed_permissions
@allowed_permissions ||= begin
module_names = enabled_modules.collect {|m| m.name}
module_names = enabled_modules.all(:select => :name).collect {|m| m.name}
Redmine::AccessControl.modules_permissions(module_names).collect {|p| p.name}
end
end

View File

@@ -27,10 +27,11 @@ class QueryColumn
self.groupable = name.to_s
end
self.default_order = options[:default_order]
@caption_key = options[:caption] || "field_#{name}"
end
def caption
l("field_#{name}")
l(@caption_key)
end
# Returns true if the column is sortable, otherwise false
@@ -120,6 +121,7 @@ class Query < ActiveRecord::Base
@@available_columns = [
QueryColumn.new(:project, :sortable => "#{Project.table_name}.name", :groupable => true),
QueryColumn.new(:tracker, :sortable => "#{Tracker.table_name}.position", :groupable => true),
QueryColumn.new(:parent, :sortable => ["#{Issue.table_name}.root_id", "#{Issue.table_name}.lft ASC"], :default_order => 'desc', :caption => :field_parent_issue),
QueryColumn.new(:status, :sortable => "#{IssueStatus.table_name}.position", :groupable => true),
QueryColumn.new(:priority, :sortable => "#{IssuePriority.table_name}.position", :default_order => 'desc', :groupable => true),
QueryColumn.new(:subject, :sortable => "#{Issue.table_name}.subject"),
@@ -185,12 +187,28 @@ class Query < ActiveRecord::Base
if project
user_values += project.users.sort.collect{|s| [s.name, s.id.to_s] }
else
# members of the user's projects
# OPTIMIZE: Is selecting from users per project (N+1)
user_values += User.current.projects.collect(&:users).flatten.uniq.sort.collect{|s| [s.name, s.id.to_s] }
all_projects = Project.visible.all
if all_projects.any?
# members of visible projects
user_values += User.active.find(:all, :conditions => ["#{User.table_name}.id IN (SELECT DISTINCT user_id FROM members WHERE project_id IN (?))", all_projects.collect(&:id)]).sort.collect{|s| [s.name, s.id.to_s] }
# project filter
project_values = []
Project.project_tree(all_projects) do |p, level|
prefix = (level > 0 ? ('--' * level + ' ') : '')
project_values << ["#{prefix}#{p.name}", p.id.to_s]
end
@available_filters["project_id"] = { :type => :list, :order => 1, :values => project_values} unless project_values.empty?
end
end
@available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => user_values } unless user_values.empty?
@available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values } unless user_values.empty?
group_values = Group.all.collect {|g| [g.name, g.id.to_s] }
@available_filters["member_of_group"] = { :type => :list_optional, :order => 6, :values => group_values } unless group_values.empty?
role_values = Role.givable.collect {|r| [r.name, r.id.to_s] }
@available_filters["assigned_to_role"] = { :type => :list_optional, :order => 7, :values => role_values } unless role_values.empty?
if User.current.logged?
@available_filters["watcher_id"] = { :type => :list, :order => 15, :values => [["<< #{l(:label_me)} >>", "me"]] }
@@ -210,6 +228,10 @@ class Query < ActiveRecord::Base
add_custom_fields_filters(@project.all_issue_custom_fields)
else
# global filters for cross project issue list
system_shared_versions = Version.visible.find_all_by_sharing('system')
unless system_shared_versions.empty?
@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] } }
end
add_custom_fields_filters(IssueCustomField.find(:all, :conditions => {:is_filter => true, :is_for_all => true}))
end
@available_filters
@@ -232,9 +254,18 @@ class Query < ActiveRecord::Base
def add_short_filter(field, expression)
return unless expression
parms = expression.scan(/^(o|c|\!|\*)?(.*)$/).first
parms = expression.scan(/^(o|c|!\*|!|\*)?(.*)$/).first
add_filter field, (parms[0] || "="), [parms[1] || ""]
end
# Add multiple filters using +add_filter+
def add_filters(fields, operators, values)
if fields.is_a?(Array) && operators.is_a?(Hash) && values.is_a?(Hash)
fields.each do |field|
add_filter(field, operators[field], values[field])
end
end
end
def has_filter?(field)
filters and filters[field]
@@ -261,11 +292,27 @@ class Query < ActiveRecord::Base
IssueCustomField.find(:all)
).collect {|cf| QueryCustomFieldColumn.new(cf) }
end
def self.available_columns=(v)
self.available_columns = (v)
end
def self.add_available_column(column)
self.available_columns << (column) if column.is_a?(QueryColumn)
end
# Returns an array of columns that can be used to group the results
def groupable_columns
available_columns.select {|c| c.groupable}
end
# Returns a Hash of columns and the key for sorting
def sortable_columns
{'id' => "#{Issue.table_name}.id"}.merge(available_columns.inject({}) {|h, column|
h[column.name.to_s] = column.sortable
h
})
end
def columns
if has_default_columns?
@@ -331,15 +378,15 @@ class Query < ActiveRecord::Base
# Returns true if the query is a grouped query
def grouped?
!group_by.blank?
!group_by_column.nil?
end
def group_by_column
groupable_columns.detect {|c| c.name.to_s == group_by}
groupable_columns.detect {|c| c.groupable && c.name.to_s == group_by}
end
def group_by_statement
group_by_column.groupable
group_by_column.try(:groupable)
end
def project_statement
@@ -395,6 +442,47 @@ class Query < ActiveRecord::Base
db_field = 'user_id'
sql << "#{Issue.table_name}.id #{ operator == '=' ? 'IN' : 'NOT IN' } (SELECT #{db_table}.watchable_id FROM #{db_table} WHERE #{db_table}.watchable_type='Issue' AND "
sql << sql_for_field(field, '=', v, db_table, db_field) + ')'
elsif field == "member_of_group" # named field
if operator == '*' # Any group
groups = Group.all
operator = '=' # Override the operator since we want to find by assigned_to
elsif operator == "!*"
groups = Group.all
operator = '!' # Override the operator since we want to find by assigned_to
else
groups = Group.find_all_by_id(v)
end
groups ||= []
members_of_groups = groups.inject([]) {|user_ids, group|
if group && group.user_ids.present?
user_ids << group.user_ids
end
user_ids.flatten.uniq.compact
}.sort.collect(&:to_s)
sql << '(' + sql_for_field("assigned_to_id", operator, members_of_groups, Issue.table_name, "assigned_to_id", false) + ')'
elsif field == "assigned_to_role" # named field
if operator == "*" # Any Role
roles = Role.givable
operator = '=' # Override the operator since we want to find by assigned_to
elsif operator == "!*" # No role
roles = Role.givable
operator = '!' # Override the operator since we want to find by assigned_to
else
roles = Role.givable.find_all_by_id(v)
end
roles ||= []
members_of_roles = roles.inject([]) {|user_ids, role|
if role && role.members
user_ids << role.members.collect(&:user_id)
end
user_ids.flatten.uniq.compact
}.sort.collect(&:to_s)
sql << '(' + sql_for_field("assigned_to_id", operator, members_of_roles, Issue.table_name, "assigned_to_id", false) + ')'
else
# regular field
db_table = Issue.table_name
@@ -478,9 +566,19 @@ class Query < ActiveRecord::Base
sql = ''
case operator
when "="
sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
if value.any?
sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
else
# IN an empty set
sql = "1=0"
end
when "!"
sql = "(#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} NOT IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + "))"
if value.any?
sql = "(#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} NOT IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + "))"
else
# NOT IN an empty set
sql = "1=1"
end
when "!*"
sql = "#{db_table}.#{db_field} IS NULL"
sql << " OR #{db_table}.#{db_field} = ''" if is_custom_filter

View File

@@ -91,12 +91,13 @@ class Repository < ActiveRecord::Base
def relative_path(path)
path
end
# Finds and returns a revision with a number or the beginning of a hash
def find_changeset_by_name(name)
return nil if name.blank?
changesets.find(:first, :conditions => (name.match(/^\d*$/) ? ["revision = ?", name.to_s] : ["revision LIKE ?", name + '%']))
end
def latest_changeset
@latest_changeset ||= changesets.find(:first)
end
@@ -136,6 +137,7 @@ class Repository < ActiveRecord::Base
end
end
@committers = nil
@found_committer_users = nil
true
else
false
@@ -146,24 +148,34 @@ class Repository < ActiveRecord::Base
# It will return nil if the committer is not yet mapped and if no User
# with the same username or email was found
def find_committer_user(committer)
if committer
unless committer.blank?
@found_committer_users ||= {}
return @found_committer_users[committer] if @found_committer_users.has_key?(committer)
user = nil
c = changesets.find(:first, :conditions => {:committer => committer}, :include => :user)
if c && c.user
c.user
user = c.user
elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
username, email = $1.strip, $3
u = User.find_by_login(username)
u ||= User.find_by_mail(email) unless email.blank?
u
user = u
end
@found_committer_users[committer] = user
user
end
end
# fetch new changesets for all repositories
# can be called periodically by an external script
# Fetches new changesets for all repositories of active projects
# Can be called periodically by an external script
# eg. ruby script/runner "Repository.fetch_changesets"
def self.fetch_changesets
find(:all).each(&:fetch_changesets)
Project.active.has_module(:repository).find(:all, :include => :repository).each do |project|
if project.repository
project.repository.fetch_changesets
end
end
end
# scan changeset comments to find related and fixed issues for all repositories

View File

@@ -85,11 +85,7 @@ class Repository::Darcs < Repository
:comments => revision.message)
revision.paths.each do |change|
Change.create(:changeset => changeset,
:action => change[:action],
:path => change[:path],
:from_path => change[:from_path],
:from_revision => change[:from_revision])
changeset.create_change(change)
end
next_rev += 1
end if revisions

View File

@@ -29,6 +29,16 @@ class Repository::Git < Repository
'Git'
end
# Returns the identifier for the given git changeset
def self.changeset_identifier(changeset)
changeset.scmid
end
# Returns the readable identifier for the given git changeset
def self.format_changeset_identifier(changeset)
changeset.revision[0, 8]
end
def branches
scm.branches
end
@@ -40,23 +50,26 @@ class Repository::Git < Repository
# With SCM's that have a sequential commit numbering, redmine is able to be
# clever and only fetch changesets going forward from the most recent one
# it knows about. However, with git, you never know if people have merged
# commits into the middle of the repository history, so we always have to
# parse the entire log.
# commits into the middle of the repository history, so we should parse
# the entire log. Since it's way too slow for large repositories, we only
# parse 1 week before the last known commit.
# The repository can still be fully reloaded by calling #clear_changesets
# before fetching changesets (eg. for offline resync)
def fetch_changesets
# Save ourselves an expensive operation if we're already up to date
return if scm.num_revisions == changesets.count
c = changesets.find(:first, :order => 'committed_on DESC')
since = (c ? c.committed_on - 7.days : nil)
revisions = scm.revisions('', nil, nil, :all => true)
revisions = scm.revisions('', nil, nil, :all => true, :since => since)
return if revisions.nil? || revisions.empty?
# Find revisions that redmine knows about already
existing_revisions = changesets.find(:all).map!{|c| c.scmid}
recent_changesets = changesets.find(:all, :conditions => ['committed_on >= ?', since])
# Clean out revisions that are no longer in git
Changeset.delete_all(["scmid NOT IN (?) AND repository_id = (?)", revisions.map{|r| r.scmid}, self.id])
recent_changesets.each {|c| c.destroy unless revisions.detect {|r| r.scmid.to_s == c.scmid.to_s }}
# Subtract revisions that redmine already knows about
revisions.reject!{|r| existing_revisions.include?(r.scmid)}
recent_revisions = recent_changesets.map{|c| c.scmid}
revisions.reject!{|r| recent_revisions.include?(r.scmid)}
# Save the remaining ones to the database
revisions.each{|r| r.save(self)} unless revisions.nil?

View File

@@ -18,6 +18,9 @@
require 'redmine/scm/adapters/mercurial_adapter'
class Repository::Mercurial < Repository
# sort changesets by revision number
has_many :changesets, :order => "#{Changeset.table_name}.id DESC", :foreign_key => 'repository_id'
attr_protected :root_url
validates_presence_of :url
@@ -52,6 +55,18 @@ class Repository::Mercurial < Repository
entries
end
# Returns the latest changesets for +path+; sorted by revision number
def latest_changesets(path, rev, limit=10)
if path.blank?
changesets.find(:all, :include => :user, :limit => limit)
else
changes.find(:all, :include => {:changeset => :user},
:conditions => ["path = ?", path.with_leading_slash],
:order => "#{Changeset.table_name}.id DESC",
:limit => limit).collect(&:changeset)
end
end
def fetch_changesets
scm_info = scm.info
if scm_info
@@ -78,11 +93,7 @@ class Repository::Mercurial < Repository
:comments => revision.message)
revision.paths.each do |change|
Change.create(:changeset => changeset,
:action => change[:action],
:path => change[:path],
:from_path => change[:from_path],
:from_revision => change[:from_revision])
changeset.create_change(change)
end
end
end unless revisions.nil?

View File

@@ -63,11 +63,7 @@ class Repository::Subversion < Repository
:comments => revision.message)
revision.paths.each do |change|
Change.create(:changeset => changeset,
:action => change[:action],
:path => change[:path],
:from_path => change[:from_path],
:from_revision => change[:from_revision])
changeset.create_change(change)
end unless changeset.new_record?
end
end unless revisions.nil?

View File

@@ -120,14 +120,30 @@ class Role < ActiveRecord::Base
find(:all, :conditions => {:builtin => 0}, :order => 'position')
end
# Return the builtin 'non member' role
# Return the builtin 'non member' role. If the role doesn't exist,
# it will be created on the fly.
def self.non_member
find(:first, :conditions => {:builtin => BUILTIN_NON_MEMBER}) || raise('Missing non-member builtin role.')
non_member_role = find(:first, :conditions => {:builtin => BUILTIN_NON_MEMBER})
if non_member_role.nil?
non_member_role = create(:name => 'Non member', :position => 0) do |role|
role.builtin = BUILTIN_NON_MEMBER
end
raise 'Unable to create the non-member role.' if non_member_role.new_record?
end
non_member_role
end
# Return the builtin 'anonymous' role
# Return the builtin 'anonymous' role. If the role doesn't exist,
# it will be created on the fly.
def self.anonymous
find(:first, :conditions => {:builtin => BUILTIN_ANONYMOUS}) || raise('Missing anonymous builtin role.')
anonymous_role = find(:first, :conditions => {:builtin => BUILTIN_ANONYMOUS})
if anonymous_role.nil?
anonymous_role = create(:name => 'Anonymous', :position => 0) do |role|
role.builtin = BUILTIN_ANONYMOUS
end
raise 'Unable to create the anonymous role.' if anonymous_role.new_record?
end
anonymous_role
end

View File

@@ -97,7 +97,7 @@ class Setting < ActiveRecord::Base
end
def value=(v)
v = v.to_yaml if v && @@available_settings[name]['serialized']
v = v.to_yaml if v && @@available_settings[name] && @@available_settings[name]['serialized']
write_attribute(:value, v.to_s)
end

View File

@@ -27,7 +27,7 @@ class TimeEntry < ActiveRecord::Base
acts_as_customizable
acts_as_event :title => Proc.new {|o| "#{l_hours(o.hours)} (#{(o.issue || o.project).event_title})"},
:url => Proc.new {|o| {:controller => 'timelog', :action => 'details', :project_id => o.project, :issue_id => o.issue}},
:url => Proc.new {|o| {:controller => 'timelog', :action => 'index', :project_id => o.project, :issue_id => o.issue}},
:author => :user,
:description => :comments
@@ -66,6 +66,9 @@ class TimeEntry < ActiveRecord::Base
# these attributes make time aggregations easier
def spent_on=(date)
super
if spent_on.is_a?(Time)
self.spent_on = spent_on.to_date
end
self.tyear = spent_on ? spent_on.year : nil
self.tmonth = spent_on ? spent_on.month : nil
self.tweek = spent_on ? Date.civil(spent_on.year, spent_on.month, spent_on.day).cweek : nil
@@ -81,4 +84,20 @@ class TimeEntry < ActiveRecord::Base
yield
end
end
def self.earilest_date_for_project(project=nil)
finder_conditions = ARCondition.new(Project.allowed_to_condition(User.current, :view_time_entries))
if project
finder_conditions << ["project_id IN (?)", project.hierarchy.collect(&:id)]
end
TimeEntry.minimum(:spent_on, :include => :project, :conditions => finder_conditions.conditions)
end
def self.latest_date_for_project(project=nil)
finder_conditions = ARCondition.new(Project.allowed_to_condition(User.current, :view_time_entries))
if project
finder_conditions << ["project_id IN (?)", project.hierarchy.collect(&:id)]
end
TimeEntry.maximum(:spent_on, :include => :project, :conditions => finder_conditions.conditions)
end
end

View File

@@ -18,7 +18,8 @@
require "digest/sha1"
class User < Principal
include Redmine::SafeAttributes
# Account statuses
STATUS_ANONYMOUS = 0
STATUS_ACTIVE = 1
@@ -33,6 +34,15 @@ class User < Principal
:username => '#{login}'
}
MAIL_NOTIFICATION_OPTIONS = [
['all', :label_user_mail_option_all],
['selected', :label_user_mail_option_selected],
['only_my_events', :label_user_mail_option_only_my_events],
['only_assigned', :label_user_mail_option_only_assigned],
['only_owner', :label_user_mail_option_only_owner],
['none', :label_user_mail_option_none]
]
has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
:after_remove => Proc.new {|user, group| group.user_removed(user)}
has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
@@ -50,10 +60,10 @@ class User < Principal
attr_accessor :password, :password_confirmation
attr_accessor :last_before_login_on
# Prevents unauthorized assignments
attr_protected :login, :admin, :password, :password_confirmation, :hashed_password, :group_ids
attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
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.blank? }
validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }, :case_sensitive => false
validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
# Login must contain lettres, numbers, underscores only
validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
@@ -63,15 +73,16 @@ class User < Principal
validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
validates_length_of :mail, :maximum => 60, :allow_nil => true
validates_confirmation_of :password, :allow_nil => true
validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
def before_create
self.mail_notification = false
self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
true
end
def before_save
# update hashed_password if password was set
self.hashed_password = User.hash_password(self.password) if self.password
self.hashed_password = User.hash_password(self.password) if self.password && self.auth_source_id.blank?
end
def reload(*args)
@@ -79,6 +90,10 @@ class User < Principal
super
end
def mail=(arg)
write_attribute(:mail, arg.to_s.strip)
end
def identity_url=(url)
if url.blank?
write_attribute(:identity_url, '')
@@ -96,7 +111,7 @@ class User < Principal
def self.try_to_login(login, password)
# Make sure no one can sign in with an empty password
return nil if password.to_s.empty?
user = find(:first, :conditions => ["login=?", login])
user = find_by_login(login)
if user
# user is already in local database
return nil if !user.active?
@@ -111,12 +126,12 @@ class User < Principal
# user is not yet registered, try to authenticate with available sources
attrs = AuthSource.authenticate(login, password)
if attrs
user = new(*attrs)
user = new(attrs)
user.login = login
user.language = Setting.default_language
if user.save
user.reload
logger.info("User '#{user.login}' created from the LDAP") if logger
logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
end
end
end
@@ -160,8 +175,42 @@ class User < Principal
self.status == STATUS_LOCKED
end
def activate
self.status = STATUS_ACTIVE
end
def register
self.status = STATUS_REGISTERED
end
def lock
self.status = STATUS_LOCKED
end
def activate!
update_attribute(:status, STATUS_ACTIVE)
end
def register!
update_attribute(:status, STATUS_REGISTERED)
end
def lock!
update_attribute(:status, STATUS_LOCKED)
end
def check_password?(clear_password)
User.hash_password(clear_password) == self.hashed_password
if auth_source_id.present?
auth_source.authenticate(self.login, clear_password)
else
User.hash_password(clear_password) == self.hashed_password
end
end
# Does the backend storage allow this user to change their password?
def change_password_allowed?
return true if auth_source_id.blank?
return auth_source.allow_password_changes?
end
# Generate and set a random password. Useful for automated user creation
@@ -196,7 +245,7 @@ class User < Principal
# Return user's API key (a 40 chars long string), used to access the API
def api_key
token = self.api_token || Token.create(:user => self, :action => 'api')
token = self.api_token || self.create_api_token(:action => 'api')
token.value
end
@@ -211,7 +260,34 @@ class User < Principal
@notified_projects_ids = nil
notified_projects_ids
end
def valid_notification_options
self.class.valid_notification_options(self)
end
# Only users that belong to more than 1 project can select projects for which they are notified
def self.valid_notification_options(user=nil)
# Note that @user.membership.size would fail since AR ignores
# :include association option when doing a count
if user.nil? || user.memberships.length < 1
MAIL_NOTIFICATION_OPTIONS.reject {|option| option.first == 'selected'}
else
MAIL_NOTIFICATION_OPTIONS
end
end
# Find a user account by matching the exact login and then a case-insensitive
# version. Exact matches will be given priority.
def self.find_by_login(login)
# force string comparison to be case sensitive on MySQL
type_cast = (ActiveRecord::Base.connection.adapter_name == 'MySQL') ? 'BINARY' : ''
# First look for an exact match
user = first(:conditions => ["#{type_cast} login = ?", login])
# Fail over to case-insensitive if none was found
user ||= first(:conditions => ["#{type_cast} LOWER(login) = ?", login.to_s.downcase])
end
def self.find_by_rss_key(key)
token = Token.find_by_value(key)
token && token.user.active? ? token.user : nil
@@ -274,23 +350,35 @@ class User < Principal
!roles_for_project(project).detect {|role| role.member?}.nil?
end
# Return true if the user is allowed to do the specified action on project
# action can be:
# Return true if the user is allowed to do the specified action on a specific context
# Action can be:
# * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
# * a permission Symbol (eg. :edit_project)
def allowed_to?(action, project, options={})
if project
# Context can be:
# * a project : returns true if user is allowed to do the specified action on this project
# * a group of projects : returns true if user is allowed on every project
# * nil with options[:global] set : check if user has at least one role allowed for this action,
# or falls back to Non Member / Anonymous permissions depending if the user is logged
def allowed_to?(action, context, options={})
if context && context.is_a?(Project)
# No action allowed on archived projects
return false unless project.active?
return false unless context.active?
# No action allowed on disabled modules
return false unless project.allows_to?(action)
return false unless context.allows_to?(action)
# Admin users are authorized for anything else
return true if admin?
roles = roles_for_project(project)
roles = roles_for_project(context)
return false unless roles
roles.detect {|role| (project.is_public? || role.member?) && role.allowed_to?(action)}
roles.detect {|role| (context.is_public? || role.member?) && role.allowed_to?(action)}
elsif context && context.is_a?(Array)
# Authorize if user is authorized on every element of the array
context.map do |project|
allowed_to?(action,project,options)
end.inject do |memo,allowed|
memo && allowed
end
elsif options[:global]
# Admin users are always authorized
return true if admin?
@@ -302,6 +390,69 @@ class User < Principal
false
end
end
# Is the user allowed to do the specified action on any project?
# See allowed_to? for the actions and valid options.
def allowed_to_globally?(action, options)
allowed_to?(action, nil, options.reverse_merge(:global => true))
end
safe_attributes 'login',
'firstname',
'lastname',
'mail',
'mail_notification',
'language',
'custom_field_values',
'custom_fields',
'identity_url'
safe_attributes 'status',
'auth_source_id',
:if => lambda {|user, current_user| current_user.admin?}
safe_attributes 'group_ids',
:if => lambda {|user, current_user| current_user.admin? && !user.new_record?}
# Utility method to help check if a user should be notified about an
# event.
#
# TODO: only supports Issue events currently
def notify_about?(object)
case mail_notification
when 'all'
true
when 'selected'
# user receives notifications for created/assigned issues on unselected projects
if object.is_a?(Issue) && (object.author == self || object.assigned_to == self)
true
else
false
end
when 'none'
false
when 'only_my_events'
if object.is_a?(Issue) && (object.author == self || object.assigned_to == self)
true
else
false
end
when 'only_assigned'
if object.is_a?(Issue) && object.assigned_to == self
true
else
false
end
when 'only_owner'
if object.is_a?(Issue) && object.author == self
true
else
false
end
else
false
end
end
def self.current=(user)
@current_user = user

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