Compare commits

..

125 Commits
1.3.3 ... 1.1.3

Author SHA1 Message Date
Jean-Philippe Lang
7e5ef18503 tagged version 1.1.3
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/tags/1.1.3@5594 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 09:37:47 +00:00
Jean-Philippe Lang
3f302b66ce Updates for 1.1.3 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5592 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 09:23:34 +00:00
Jean-Philippe Lang
32022267af Merged r5285 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5591 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 09:07:40 +00:00
Jean-Philippe Lang
f078f7127c Merged r5283 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5590 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 09:06:32 +00:00
Jean-Philippe Lang
c2cd4b7f48 Merged r5176 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5589 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 09:02:29 +00:00
Jean-Philippe Lang
0b5b8bebd1 Merged r5171 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5588 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 09:01:37 +00:00
Jean-Philippe Lang
9f36f18b39 Merged r4735 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5587 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 08:59:30 +00:00
Jean-Philippe Lang
fab0774c4b Fixes Prototypejs Form.serialize() for multiple selects (#7954).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5586 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 07:38:24 +00:00
Jean-Philippe Lang
93c0b120de Merged r5469 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5585 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 07:37:03 +00:00
Jean-Philippe Lang
e20191e666 Backported r5581 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5584 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 07:34:09 +00:00
Jean-Philippe Lang
8d3b32644b Merged r5330 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5583 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 07:31:21 +00:00
Jean-Philippe Lang
6153d5ab83 Merged r5265 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5582 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 07:29:23 +00:00
Jean-Philippe Lang
f49904569d Merged r5215 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5580 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 07:16:39 +00:00
Jean-Philippe Lang
f48460da4f Merged r5300 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5579 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 07:15:46 +00:00
Jean-Philippe Lang
bd55d7f815 Merged r5232 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5578 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 07:14:52 +00:00
Jean-Philippe Lang
4402e7e232 Merged r5214 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5577 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 07:13:59 +00:00
Jean-Philippe Lang
b43ebcbdc4 Merged r5186 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5576 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 07:12:52 +00:00
Jean-Philippe Lang
55fd2f5562 Merged r5185 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5575 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 07:11:56 +00:00
Jean-Philippe Lang
8a734f9997 Merged r5157 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5574 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 07:10:27 +00:00
Jean-Philippe Lang
d60949ca87 Merged r5181 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5573 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 07:09:19 +00:00
Jean-Philippe Lang
aef8228b0e Merged r5100 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5572 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 07:08:39 +00:00
Jean-Philippe Lang
20e6652109 Merged r5236 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5571 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 07:07:28 +00:00
Jean-Philippe Lang
d2bc5a9473 Merged r5105 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5570 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 07:05:41 +00:00
Jean-Philippe Lang
23e75d87d3 Merged r5097 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5569 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 07:04:23 +00:00
Jean-Philippe Lang
8a13595c64 Merged r5230 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5568 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 07:02:26 +00:00
Jean-Philippe Lang
7845843596 Merged r5225 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5567 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-29 07:00:16 +00:00
Toshi MARUYAMA
f26a504045 Merged r5557 from trunk.
Delete doc/git.rdoc.

http://www.redmine.org/projects/redmine/wiki/Contribute?version=27 has the github mirror link.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5558 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-27 22:43:42 +00:00
Toshi MARUYAMA
f274193893 Merged r5555 from trunk.
Remove obsolete github descriptions from doc/git.rdoc.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5556 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-27 14:08:01 +00:00
Toshi MARUYAMA
0ba400834c Merged r5553 from trunk.
Fix notice_failed_to_save_issues format in es, gl and ca locales.

Contributed by Jose M. Prieto.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5554 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-27 10:51:10 +00:00
Toshi MARUYAMA
e6148cfdd4 Merged r5502 from trunk.
Simplified Chinese translation updated by Peng Wang.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5503 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-18 04:29:28 +00:00
Toshi MARUYAMA
99b1b95222 Merged r5500 from trunk.
Czech translation updated by Lubor Nosek.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5501 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-17 23:42:31 +00:00
Toshi MARUYAMA
a9a5870d25 Merged r4901 from trunk.
Updated basque and czech translations. Contributed by Ales Zabala Alava and Michal Gebauer.

#7390 depends on this commit.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5497 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-17 16:26:53 +00:00
Toshi MARUYAMA
f09f2e8e63 scm: git: backout r5336 (#8081, #8083).
Git on Redmine CI Server does not support "--no-decorate" option of "git log".

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5347 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-06 06:59:50 +00:00
Toshi MARUYAMA
2a1203bfef scm: git: backout r5340 (#8081, #8083).
Git on Redmine CI Server does not support "--no-decorate" option of "git log".

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5346 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-06 06:58:47 +00:00
Toshi MARUYAMA
f6918d0242 Merged r5338 from trunk.
scm: git: add "decorate = short" in config log section of test repository.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5340 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-06 02:52:59 +00:00
Toshi MARUYAMA
e338f522dd Merged r5334 from trunk.
scm: git: add "--no-decorate" option in "git log".

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5336 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-05 23:18:29 +00:00
Jean-Philippe Lang
ab10e187a6 Merged r5322 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5323 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-05 12:17:43 +00:00
Toshi MARUYAMA
12801275a8 Merged r5281 from trunk.
r5256 in trunk and r5271 in 1.1-stable fixed #7794 completely.
r5253 (r5183) in trunk and r5184 effect the width of the ASCII character of Japanese PDF.

Contributed by Jun NAITOH.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5282 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-01 15:54:05 +00:00
Toshi MARUYAMA
e35e04de8e PDF: fix the width of the ASCII character of Japanese PDF (#7794).
r5256 in trunk has this change.
So, there is no need to commit in trunk.

Contributed by Jun NAITOH.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5271 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-04-01 01:26:38 +00:00
Toshi MARUYAMA
3ca48e1ecf Merged r5233 from trunk.
i18n: fix typo general_pdf_encoding "UFT-8" in sl.yml.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5234 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-03-28 01:14:15 +00:00
Toshi MARUYAMA
f49bbf48e7 Merged r5183 from trunk.
Fix an internal server error on formatting an issue as a PDF in Japanese.

Contributed by Yuki Sonoda.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5184 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-03-21 06:17:06 +00:00
Toshi MARUYAMA
891ed84fe3 Merged r5091 from trunk.
scm: mercurial: add :order => 'id DESC' explicitly for MySQL test fails.

Because :order => 'id DESC' is defined at 'has_many',
there is no need to set 'order'.
But, MySQL test fails.
Sqlite3 and PostgreSQL pass.
Is this MySQL bug?

MySQL svn trunk test on Redmine CI server fails.
But, svn 1.1-stable passes.
If this is MySQL bug, this effects 1.1-stable, too.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5092 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-03-11 07:46:23 +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
1160 changed files with 36822 additions and 68093 deletions

8
.gitignore vendored
View File

@@ -1,7 +1,6 @@
/.project
/.loadpath
/config/additional_environment.rb
/config/configuration.yml
/config/database.yml
/config/email.yml
/config/initializers/session_store.rb
@@ -10,8 +9,6 @@
/db/*.sqlite3
/db/schema.rb
/files/*
/lib/redmine/scm/adapters/mercurial/redminehelper.pyc
/lib/redmine/scm/adapters/mercurial/redminehelper.pyo
/log/*.log*
/log/mongrel_debug
/public/dispatch.*
@@ -23,8 +20,3 @@
/tmp/test/*
/vendor/rails
*.rbc
/.bundle
/Gemfile.lock
/Gemfile.local

View File

@@ -3,7 +3,6 @@ syntax: glob
.project
.loadpath
config/additional_environment.rb
config/configuration.yml
config/database.yml
config/email.yml
config/initializers/session_store.rb
@@ -12,8 +11,6 @@ db/*.db
db/*.sqlite3
db/schema.rb
files/*
lib/redmine/scm/adapters/mercurial/redminehelper.pyc
lib/redmine/scm/adapters/mercurial/redminehelper.pyo
log/*.log*
log/mongrel_debug
public/dispatch.*
@@ -25,11 +22,3 @@ tmp/sockets/*
tmp/test/*
vendor/rails
*.rbc
.svn/
.git/
.bundle
Gemfile.lock
Gemfile.local

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 in the doc directory or on the official website 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

@@ -5,11 +5,6 @@ require(File.join(File.dirname(__FILE__), 'config', 'boot'))
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
begin
require 'rdoc/task'
rescue LoadError
# RDoc is not available
end
require 'tasks/rails'
require 'tasks/rails'

View File

@@ -1,24 +1,24 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-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 AccountController < ApplicationController
helper :custom_fields
include CustomFieldsHelper
include CustomFieldsHelper
# prevents login action to be filtered by check_if_login_required application scope filter
skip_before_filter :check_if_login_required
@@ -36,7 +36,7 @@ class AccountController < ApplicationController
logout_user
redirect_to home_url
end
# Enable user to choose a new password
def lost_password
redirect_to(home_url) && return unless Setting.lost_password?
@@ -51,7 +51,7 @@ class AccountController < ApplicationController
flash[:notice] = l(:notice_account_password_updated)
redirect_to :action => 'login'
return
end
end
end
render :template => "account/password_recovery"
return
@@ -73,7 +73,7 @@ class AccountController < ApplicationController
end
end
end
# User self-registration
def register
redirect_to(home_url) && return unless Setting.self_registration? || session[:auth_source_registration]
@@ -109,7 +109,7 @@ class AccountController < ApplicationController
end
end
end
# Token based account activation
def activate
redirect_to(home_url) && return unless Setting.self_registration? && params[:token]
@@ -124,9 +124,9 @@ class AccountController < ApplicationController
end
redirect_to :action => 'login'
end
private
def logout_user
if User.current.logged?
cookies.delete :autologin
@@ -134,7 +134,7 @@ class AccountController < ApplicationController
self.logged_user = nil
end
end
def authenticate_user
if Setting.openid? && using_open_id?
open_id_authenticate(params[:openid_url])
@@ -156,6 +156,7 @@ class AccountController < ApplicationController
end
end
def open_id_authenticate(openid_url)
authenticate_with_open_id(openid_url, :required => [:nickname, :fullname, :email], :return_to => signin_url) do |result, identity_url, registration|
if result.successful?
@@ -184,7 +185,7 @@ class AccountController < ApplicationController
register_manually_by_administrator(user) do
onthefly_creation_failed(user)
end
end
end
else
# Existing record
if user.active?
@@ -196,31 +197,19 @@ class AccountController < ApplicationController
end
end
end
def successful_authentication(user)
# Valid user
self.logged_user = user
# generate a key and set cookie if autologin
if params[:autologin] && Setting.autologin?
set_autologin_cookie(user)
token = Token.create(:user => user, :action => 'autologin')
cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now }
end
call_hook(:controller_account_success_authentication_after, {:user => user })
redirect_back_or_default :controller => 'my', :action => 'page'
end
def set_autologin_cookie(user)
token = Token.create(:user => user, :action => 'autologin')
cookie_name = Redmine::Configuration['autologin_cookie_name'] || 'autologin'
cookie_options = {
:value => token.value,
:expires => 1.year.from_now,
:path => (Redmine::Configuration['autologin_cookie_path'] || '/'),
:secure => (Redmine::Configuration['autologin_cookie_secure'] ? true : false),
:httponly => true
}
cookies[cookie_name] = cookie_options
end
# Onthefly creation failed, display the registration form to fill/fix attributes
def onthefly_creation_failed(user, auth_source_options = { })
@user = user
@@ -246,7 +235,7 @@ class AccountController < ApplicationController
yield if block_given?
end
end
# Automatically register a user
#
# Pass a block for behavior when a user fails to save
@@ -262,7 +251,7 @@ class AccountController < ApplicationController
yield if block_given?
end
end
# Manual activation by the administrator
#
# Pass a block for behavior when a user fails to save

View File

@@ -1,28 +1,11 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# 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 ActivitiesController < ApplicationController
menu_item :activity
before_filter :find_optional_project
accept_rss_auth :index
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
@@ -31,18 +14,18 @@ class ActivitiesController < ApplicationController
@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,
@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 {
format.html {
@events_by_day = events.group_by(&:event_date)
render :layout => false if request.xhr?
}
@@ -57,7 +40,7 @@ class ActivitiesController < ApplicationController
}
end
end
rescue ActiveRecord::RecordNotFound
render_404
end
@@ -72,4 +55,5 @@ class ActivitiesController < ApplicationController
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

@@ -1,23 +1,25 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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 AdminController < ApplicationController
layout 'admin'
before_filter :require_admin
helper :sort
include SortHelper
@@ -28,20 +30,22 @@ class AdminController < ApplicationController
def projects
@status = params[:status] ? params[:status].to_i : 1
c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
unless params[:name].blank?
name = "%#{params[:name].strip.downcase}%"
c << ["LOWER(identifier) LIKE ? OR LOWER(name) LIKE ?", name, name]
end
@projects = Project.find :all, :order => 'lft',
:conditions => c.conditions
render :action => "projects", :layout => false if request.xhr?
end
def plugins
@plugins = Redmine::Plugin.all
end
# Loads the default configuration
# (roles, trackers, statuses, workflow, enumerations)
def default_configuration
@@ -55,7 +59,7 @@ class AdminController < ApplicationController
end
redirect_to :action => 'index'
end
def test_email
raise_delivery_errors = ActionMailer::Base.raise_delivery_errors
# Force ActionMailer to raise delivery errors so we can catch it
@@ -69,14 +73,14 @@ class AdminController < ApplicationController
ActionMailer::Base.raise_delivery_errors = raise_delivery_errors
redirect_to :controller => 'settings', :action => 'edit', :tab => 'notifications'
end
def info
@db_adapter_name = ActiveRecord::Base.connection.adapter_name
@checklist = [
[:text_default_administrator_account_changed, User.default_admin_account_changed?],
[:text_default_administrator_account_changed, User.find(:first, :conditions => ["login=? and hashed_password=?", 'admin', User.hash_password('admin')]).nil?],
[:text_file_repository_writable, File.writable?(Attachment.storage_path)],
[:text_plugin_assets_writable, File.writable?(Engines.public_directory)],
[:text_rmagick_available, Object.const_defined?(:Magick)]
[:text_plugin_assets_writable, File.writable?(Engines.public_directory)],
[:text_rmagick_available, Object.const_defined?(:Magick)]
]
end
end
end

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-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.
@@ -18,41 +18,34 @@
require 'uri'
require 'cgi'
class Unauthorized < Exception; end
class ApplicationController < ActionController::Base
include Redmine::I18n
layout 'base'
exempt_from_layout 'builder', 'rsb'
protect_from_forgery
def handle_unverified_request
super
cookies.delete(:autologin)
end
# Remove broken cookie after upgrade from 0.8.x (#4292)
# See https://rails.lighthouseapp.com/projects/8994/tickets/3360
# TODO: remove it when Rails is fixed
before_filter :delete_broken_cookies
def delete_broken_cookies
if cookies['_redmine_session'] && cookies['_redmine_session'] !~ /--/
cookies.delete '_redmine_session'
cookies.delete '_redmine_session'
redirect_to home_path
return false
end
end
before_filter :user_setup, :check_if_login_required, :set_localization
filter_parameter_logging :password
protect_from_forgery
rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
rescue_from ::Unauthorized, :with => :deny_access
include Redmine::Search::Controller
include Redmine::MenuManager::MenuController
helper Redmine::MenuManager::MenuHelper
Redmine::Scm::Base.all.each do |scm|
require_dependency "repository/#{scm.underscore}"
end
@@ -63,7 +56,7 @@ class ApplicationController < ActionController::Base
# Find the current user
User.current = find_current_user
end
# Returns the current user or nil if no user is logged in
# and starts a session if needed
def find_current_user
@@ -75,11 +68,11 @@ class ApplicationController < ActionController::Base
user = User.try_to_autologin(cookies[:autologin])
session[:user_id] = user.id if user
user
elsif params[:format] == 'atom' && params[:key] && request.get? && accept_rss_auth?
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? && accept_api_auth?
if (key = api_key_from_request)
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(key)
else
@@ -101,14 +94,14 @@ class ApplicationController < ActionController::Base
User.current = User.anonymous
end
end
# check if login is globally required to access the application
def check_if_login_required
# no check needed if user is already logged in
return true if User.current.logged?
require_login if Setting.login_required?
end
end
def set_localization
lang = nil
if User.current.logged?
@@ -124,7 +117,7 @@ class ApplicationController < ActionController::Base
lang ||= Setting.default_language
set_language_if_valid(lang)
end
def require_login
if !User.current.logged?
# Extract only the basic url parameters on non-GET requests
@@ -153,7 +146,7 @@ class ApplicationController < ActionController::Base
end
true
end
def deny_access
User.current.logged? ? render_403 : require_login
end
@@ -204,8 +197,10 @@ class ApplicationController < ActionController::Base
# 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
@@ -226,16 +221,12 @@ class ApplicationController < ActionController::Base
def find_issues
@issues = Issue.find_all_by_id(params[:id] || params[:ids])
raise ActiveRecord::RecordNotFound if @issues.empty?
if @issues.detect {|issue| !issue.visible?}
deny_access
return
end
@projects = @issues.collect(&:project).compact.uniq
@project = @projects.first if @projects.size == 1
rescue ActiveRecord::RecordNotFound
render_404
end
# Check if project is unique before bulk operations
def check_project_uniqueness
unless @project
@@ -244,7 +235,7 @@ class ApplicationController < ActionController::Base
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
def check_project_privacy
@@ -252,7 +243,7 @@ class ApplicationController < ActionController::Base
if @project.is_public? || User.current.member_of?(@project) || User.current.admin?
true
else
deny_access
User.current.logged? ? render_403 : require_login
end
else
@project = nil
@@ -280,28 +271,27 @@ class ApplicationController < ActionController::Base
end
end
redirect_to default
false
end
def render_403(options={})
@project = nil
render_error({:message => :notice_not_authorized, :status => 403}.merge(options))
return false
end
def render_404(options={})
render_error({:message => :notice_file_not_found, :status => 404}.merge(options))
return false
end
# 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
@@ -312,19 +302,6 @@ class ApplicationController < ActionController::Base
format.json { head @status }
end
end
# Filter for actions that provide an API response
# but have no HTML representation for non admin users
def require_admin_or_api_request
return true if api_request?
if User.current.admin?
true
elsif User.current.logged?
render_error(:status => 406)
else
deny_access
end
end
# Picks which layout to use based on the request
#
@@ -332,59 +309,31 @@ class ApplicationController < ActionController::Base
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
def render_feed(items, options={})
def render_feed(items, options={})
@items = items || []
@items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
@items = @items.slice(0, Setting.feeds_limit.to_i)
@title = options[:title] || Setting.app_title
render :template => "common/feed.atom", :layout => false,
:content_type => 'application/atom+xml'
render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml'
end
# TODO: remove in Redmine 1.4
def self.accept_key_auth(*actions)
ActiveSupport::Deprecation.warn "ApplicationController.accept_key_auth is deprecated and will be removed in Redmine 1.4. Use accept_rss_auth (or accept_api_auth) instead."
accept_rss_auth(*actions)
actions = actions.flatten.map(&:to_s)
write_inheritable_attribute('accept_key_auth_actions', actions)
end
# TODO: remove in Redmine 1.4
def accept_key_auth_actions
ActiveSupport::Deprecation.warn "ApplicationController.accept_key_auth_actions is deprecated and will be removed in Redmine 1.4. Use accept_rss_auth (or accept_api_auth) instead."
self.class.accept_rss_auth
self.class.read_inheritable_attribute('accept_key_auth_actions') || []
end
def self.accept_rss_auth(*actions)
if actions.any?
write_inheritable_attribute('accept_rss_auth_actions', actions)
else
read_inheritable_attribute('accept_rss_auth_actions') || []
end
end
def accept_rss_auth?(action=action_name)
self.class.accept_rss_auth.include?(action.to_sym)
end
def self.accept_api_auth(*actions)
if actions.any?
write_inheritable_attribute('accept_api_auth_actions', actions)
else
read_inheritable_attribute('accept_api_auth_actions') || []
end
end
def accept_api_auth?(action=action_name)
self.class.accept_api_auth.include?(action.to_sym)
end
# Returns the number of objects that should be displayed
# on the paginated list
def per_page_option
@@ -420,10 +369,10 @@ class ApplicationController < ActionController::Base
offset = 0 if offset < 0
end
offset ||= 0
[offset, limit]
end
# qvalues http header parser
# code taken from webrick
def parse_qvalues(value)
@@ -444,16 +393,16 @@ class ApplicationController < ActionController::Base
rescue
nil
end
# Returns a string that can be used as filename value in Content-Disposition header
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?
@@ -491,6 +440,13 @@ class ApplicationController < ActionController::Base
render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
end
# 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 }
@@ -503,7 +459,7 @@ class ApplicationController < ActionController::Base
)
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)
@@ -517,7 +473,7 @@ class ApplicationController < ActionController::Base
end
super
end
# Overrides #pick_layout so that #render with no arguments
# doesn't use the layout for api requests
def pick_layout(*args)

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2008 Jean-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.
@@ -19,46 +19,33 @@ class AttachmentsController < ApplicationController
before_filter :find_project
before_filter :file_readable, :read_authorize, :except => :destroy
before_filter :delete_authorize, :only => :destroy
accept_api_auth :show, :download
verify :method => :post, :only => :destroy
def show
respond_to do |format|
format.html {
if @attachment.is_diff?
@diff = File.new(@attachment.diskfile, "rb").read
@diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
@diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
# Save diff type as user preference
if User.current.logged? && @diff_type != User.current.pref[:diff_type]
User.current.pref[:diff_type] = @diff_type
User.current.preference.save
end
render :action => 'diff'
elsif @attachment.is_text? && @attachment.filesize <= Setting.file_max_size_displayed.to_i.kilobyte
@content = File.new(@attachment.diskfile, "rb").read
render :action => 'file'
else
download
end
}
format.api
if @attachment.is_diff?
@diff = File.new(@attachment.diskfile, "rb").read
render :action => 'diff'
elsif @attachment.is_text? && @attachment.filesize <= Setting.file_max_size_displayed.to_i.kilobyte
@content = File.new(@attachment.diskfile, "rb").read
render :action => 'file'
else
download
end
end
def download
if @attachment.container.is_a?(Version) || @attachment.container.is_a?(Project)
@attachment.increment_download
end
# images are sent inline
send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
:type => detect_content_type(@attachment),
:type => detect_content_type(@attachment),
:disposition => (@attachment.image? ? 'inline' : 'attachment')
end
verify :method => :delete, :only => :destroy
def destroy
# Make sure association callbacks are called
@attachment.container.attachments.delete(@attachment)
@@ -66,7 +53,7 @@ class AttachmentsController < ApplicationController
rescue ::ActionController::RedirectBackError
redirect_to :controller => 'projects', :action => 'show', :id => @project
end
private
def find_project
@attachment = Attachment.find(params[:id])
@@ -76,20 +63,20 @@ private
rescue ActiveRecord::RecordNotFound
render_404
end
# Checks that the file exists and is readable
def file_readable
@attachment.readable? ? true : render_404
end
def read_authorize
@attachment.visible? ? true : deny_access
end
def delete_authorize
@attachment.deletable? ? true : deny_access
end
def detect_content_type(attachment)
content_type = attachment.content_type
if content_type.blank?

View File

@@ -1,23 +1,23 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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 AuthSourcesController < ApplicationController
layout 'admin'
before_filter :require_admin
# GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
@@ -58,7 +58,7 @@ class AuthSourcesController < ApplicationController
render 'auth_sources/edit'
end
end
def test_connection
@auth_method = AuthSource.find(params[:id])
begin

View File

@@ -1,6 +1,6 @@
class AutoCompletesController < ApplicationController
before_filter :find_project
def issues
@issues = []
q = params[:q].to_s

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-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.
@@ -18,7 +18,7 @@
class BoardsController < ApplicationController
default_search_scope :messages
before_filter :find_project, :find_board_if_available, :authorize
accept_rss_auth :index, :show
accept_key_auth :index, :show
helper :messages
include MessagesHelper
@@ -26,7 +26,7 @@ class BoardsController < ApplicationController
include SortHelper
helper :watchers
include WatchersHelper
def index
@boards = @project.boards
# show the board if there is only one
@@ -43,7 +43,7 @@ class BoardsController < ApplicationController
sort_update 'created_on' => "#{Message.table_name}.created_on",
'replies' => "#{Message.table_name}.replies_count",
'updated_on' => "#{Message.table_name}.updated_on"
@topic_count = @board.topics.count
@topic_pages = Paginator.new self, @topic_count, per_page_option, params['page']
@topics = @board.topics.find :all, :order => ["#{Message.table_name}.sticky DESC", sort_clause].compact.join(', '),
@@ -61,12 +61,11 @@ class BoardsController < ApplicationController
}
end
end
verify :method => :post, :only => [ :destroy ], :redirect_to => { :action => :index }
def new
@board = Board.new
@board.safe_attributes = params[:board]
@board = Board.new(params[:board])
@board.project = @project
if request.post? && @board.save
flash[:notice] = l(:notice_successful_create)
@@ -75,8 +74,7 @@ class BoardsController < ApplicationController
end
def edit
@board.safe_attributes = params[:board]
if request.post? && @board.save
if request.post? && @board.update_attributes(params[:board])
redirect_to_settings_in_projects
end
end
@@ -85,7 +83,7 @@ class BoardsController < ApplicationController
@board.destroy
redirect_to_settings_in_projects
end
private
def redirect_to_settings_in_projects
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'boards'

View File

@@ -1,20 +1,3 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# 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 CalendarsController < ApplicationController
menu_item :calendar
before_filter :find_optional_project
@@ -33,11 +16,11 @@ class CalendarsController < ApplicationController
@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
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
@@ -47,10 +30,15 @@ class CalendarsController < ApplicationController
: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

@@ -7,13 +7,12 @@ class CommentsController < ApplicationController
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
def create
@comment = Comment.new
@comment.safe_attributes = params[:comment]
@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
@@ -33,5 +32,5 @@ class CommentsController < ApplicationController
@comment = nil
@news
end
end

View File

@@ -1,10 +1,9 @@
class ContextMenusController < ApplicationController
helper :watchers
helper :issues
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)
@@ -26,35 +25,20 @@ class ContextMenusController < ApplicationController
:delete => User.current.allowed_to?(:delete_issues, @projects)
}
if @project
if @issue
@assignables = @issue.assignable_users
else
@assignables = @project.assignable_users
end
@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.active.reverse
@priorities = IssuePriority.all.reverse
@statuses = IssueStatus.find(:all, :order => 'position')
@back = back_url
render :layout => false
end
def time_entries
@time_entries = TimeEntry.all(
:conditions => {:id => params[:ids]}, :include => :project)
@projects = @time_entries.collect(&:project).compact.uniq
@project = @projects.first if @projects.size == 1
@activities = TimeEntryActivity.shared.active
@can = {:edit => User.current.allowed_to?(:edit_time_entries, @projects),
:delete => User.current.allowed_to?(:edit_time_entries, @projects)
}
@back = back_url
render :layout => false
end
end

View File

@@ -1,30 +1,30 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-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 CustomFieldsController < ApplicationController
layout 'admin'
before_filter :require_admin
def index
@custom_fields_by_type = CustomField.find(:all).group_by {|f| f.class.name }
@tab = params[:tab] || 'IssueCustomField'
end
def new
@custom_field = begin
if params[:type].to_s.match(/.+CustomField$/)
@@ -33,14 +33,13 @@ class CustomFieldsController < ApplicationController
rescue
end
(redirect_to(:action => 'index'); return) unless @custom_field.is_a?(CustomField)
if request.post? and @custom_field.save
flash[:notice] = l(:notice_successful_create)
call_hook(:controller_custom_fields_new_after_save, :params => params, :custom_field => @custom_field)
redirect_to :action => 'index', :tab => @custom_field.class.name
else
@trackers = Tracker.find(:all, :order => 'position')
end
@trackers = Tracker.find(:all, :order => 'position')
end
def edit
@@ -49,11 +48,10 @@ class CustomFieldsController < ApplicationController
flash[:notice] = l(:notice_successful_update)
call_hook(:controller_custom_fields_edit_after_save, :params => params, :custom_field => @custom_field)
redirect_to :action => 'index', :tab => @custom_field.class.name
else
@trackers = Tracker.find(:all, :order => 'position')
end
@trackers = Tracker.find(:all, :order => 'position')
end
def destroy
@custom_field = CustomField.find(params[:id]).destroy
redirect_to :action => 'index', :tab => @custom_field.class.name

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-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.
@@ -22,9 +22,9 @@ class DocumentsController < ApplicationController
before_filter :find_model_object, :except => [:index, :new]
before_filter :find_project_from_association, :except => [:index, :new]
before_filter :authorize
helper :attachments
def index
@sort_by = %w(category date title author).include?(params[:sort_by]) ? params[:sort_by] : 'category'
documents = @project.documents.find :all, :include => [:attachments, :category]
@@ -41,14 +41,13 @@ class DocumentsController < ApplicationController
@document = @project.documents.build
render :layout => false if request.xhr?
end
def show
@attachments = @document.attachments.find(:all, :order => "created_on DESC")
end
def new
@document = @project.documents.build
@document.safe_attributes = params[:document]
@document = @project.documents.build(params[:document])
if request.post? and @document.save
attachments = Attachment.attach_files(@document, params[:attachments])
render_attachment_warning_if_needed(@document)
@@ -56,21 +55,20 @@ class DocumentsController < ApplicationController
redirect_to :action => 'index', :project_id => @project
end
end
def edit
@categories = DocumentCategory.active #TODO: use it in the views
@document.safe_attributes = params[:document]
if request.post? and @document.save
@categories = DocumentCategory.all
if request.post? and @document.update_attributes(params[:document])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'show', :id => @document
end
end
end
def destroy
@document.destroy
redirect_to :controller => 'documents', :action => 'index', :project_id => @project
end
def add_attachment
attachments = Attachment.attach_files(@document, params[:attachments])
render_attachment_warning_if_needed(@document)

View File

@@ -1,39 +1,45 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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 EnumerationsController < ApplicationController
layout 'admin'
before_filter :require_admin
helper :custom_fields
include CustomFieldsHelper
def index
list
render :action => 'list'
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 => :index }
:redirect_to => { :action => :list }
def list
end
def new
begin
@enumeration = params[:type].constantize.new
rescue NameError
@enumeration = Enumeration.new
@enumeration = Enumeration.new
end
end
@@ -42,7 +48,7 @@ class EnumerationsController < ApplicationController
@enumeration.type = params[:enumeration][:type]
if @enumeration.save
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'index', :type => @enumeration.type
redirect_to :action => 'list', :type => @enumeration.type
else
render :action => 'new'
end
@@ -57,24 +63,22 @@ class EnumerationsController < ApplicationController
@enumeration.type = params[:enumeration][:type] if params[:enumeration][:type]
if @enumeration.update_attributes(params[:enumeration])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'index', :type => @enumeration.type
redirect_to :action => 'list', :type => @enumeration.type
else
render :action => 'edit'
end
end
def destroy
@enumeration = Enumeration.find(params[:id])
if !@enumeration.in_use?
# No associated objects
@enumeration.destroy
redirect_to :action => 'index'
return
elsif params[:reassign_to_id]
if reassign_to = @enumeration.class.find_by_id(params[:reassign_to_id])
@enumeration.destroy(reassign_to)
redirect_to :action => 'index'
return
end
end
@enumerations = @enumeration.class.find(:all) - [@enumeration]

View File

@@ -13,7 +13,7 @@ class FilesController < ApplicationController
'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?

View File

@@ -1,20 +1,3 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# 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 GanttsController < ApplicationController
menu_item :gantt
before_filter :find_optional_project
@@ -29,20 +12,25 @@ class GanttsController < ApplicationController
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

@@ -1,27 +1,27 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-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 GroupsController < ApplicationController
layout 'admin'
before_filter :require_admin
helper :custom_fields
# GET /groups
# GET /groups.xml
def index
@@ -48,7 +48,7 @@ class GroupsController < ApplicationController
# GET /groups/new.xml
def new
@group = Group.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @group }
@@ -67,10 +67,8 @@ class GroupsController < ApplicationController
respond_to do |format|
if @group.save
format.html {
flash[:notice] = l(:notice_successful_create)
redirect_to(params[:continue] ? new_group_path : groups_path)
}
flash[:notice] = l(:notice_successful_create)
format.html { redirect_to(groups_path) }
format.xml { render :xml => @group, :status => :created, :location => @group }
else
format.html { render :action => "new" }
@@ -107,37 +105,37 @@ class GroupsController < ApplicationController
format.xml { head :ok }
end
end
def add_users
@group = Group.find(params[:id])
users = User.find_all_by_id(params[:user_ids])
@group.users << users if request.post?
respond_to do |format|
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'users' }
format.js {
render(:update) {|page|
format.js {
render(:update) {|page|
page.replace_html "tab-content-users", :partial => 'groups/users'
users.each {|user| page.visual_effect(:highlight, "user-#{user.id}") }
}
}
end
end
def remove_user
@group = Group.find(params[:id])
@group.users.delete(User.find(params[:user_id])) if request.delete?
@group.users.delete(User.find(params[:user_id])) if request.post?
respond_to do |format|
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'users' }
format.js { render(:update) {|page| page.replace_html "tab-content-users", :partial => 'groups/users'} }
end
end
def autocomplete_for_user
@group = Group.find(params[:id])
@users = User.active.not_in_group(@group).like(params[:q]).all(:limit => 100)
@users = User.active.like(params[:q]).find(:all, :limit => 100) - @group.users
render :layout => false
end
def edit_membership
@group = Group.find(params[:id])
@membership = Member.edit_membership(params[:membership_id], params[:membership], @group)
@@ -160,7 +158,7 @@ class GroupsController < ApplicationController
end
end
end
def destroy_membership
@group = Group.find(params[:id])
Member.find(params[:membership_id]).destroy if request.post?

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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.
@@ -18,96 +18,57 @@
class IssueCategoriesController < ApplicationController
menu_item :settings
model_object IssueCategory
before_filter :find_model_object, :except => [:index, :new, :create]
before_filter :find_project_from_association, :except => [:index, :new, :create]
before_filter :find_project, :only => [:index, :new, :create]
before_filter :find_model_object, :except => :new
before_filter :find_project_from_association, :except => :new
before_filter :find_project, :only => :new
before_filter :authorize
accept_api_auth :index, :show, :create, :update, :destroy
def index
respond_to do |format|
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project }
format.api { @categories = @project.issue_categories.all }
end
end
def show
respond_to do |format|
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project }
format.api
end
end
verify :method => :post, :only => :destroy
def new
@category = @project.issue_categories.build
@category.safe_attributes = params[:issue_category]
end
verify :method => :post, :only => :create
def create
@category = @project.issue_categories.build
@category.safe_attributes = params[:issue_category]
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
@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
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]')
}
else
respond_to do |format|
format.html
format.js do
render(:update) {|page| page.alert(@category.errors.full_messages.join('\n')) }
end
end
format.api { render :action => 'show', :status => :created, :location => issue_category_path(@category) }
end
else
respond_to do |format|
format.html { render :action => 'new'}
format.js do
render(:update) {|page| page.alert(@category.errors.full_messages.join('\n')) }
end
format.api { render_validation_errors(@category) }
end
end
end
def edit
end
verify :method => :put, :only => :update
def update
@category.safe_attributes = params[:issue_category]
if @category.save
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project
}
format.api { head :ok }
end
else
respond_to do |format|
format.html { render :action => 'edit' }
format.api { render_validation_errors(@category) }
end
if request.post? and @category.update_attributes(params[:category])
flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project
end
end
verify :method => :delete, :only => :destroy
def destroy
@issue_count = @category.issues.size
if @issue_count == 0 || params[:todo] || api_request?
reassign_to = nil
if params[:reassign_to_id] && (params[:todo] == 'reassign' || params[:todo].blank?)
reassign_to = @project.issue_categories.find_by_id(params[:reassign_to_id])
end
if @issue_count == 0
# No issue assigned to this category
@category.destroy
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'categories'
elsif params[:todo]
reassign_to = @project.issue_categories.find_by_id(params[:reassign_to_id]) if params[:todo] == 'reassign'
@category.destroy(reassign_to)
respond_to do |format|
format.html { redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'categories' }
format.api { head :ok }
end
return
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'categories'
end
@categories = @project.issue_categories - [@category]
end
@@ -118,8 +79,8 @@ private
def find_model_object
super
@category = @object
end
end
def find_project
@project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound

View File

@@ -1,27 +1,8 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# 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 IssueMovesController < ApplicationController
menu_item :issues
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?
@@ -67,7 +48,7 @@ class IssueMovesController < ApplicationController
@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
@target_project ||= @project
@trackers = @target_project.trackers
@available_statuses = Workflow.available_statuses(@project)
@notes = params[:notes]

View File

@@ -1,53 +1,30 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-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 IssueRelationsController < ApplicationController
before_filter :find_issue, :find_project_from_association, :authorize, :only => [:index, :create]
before_filter :find_relation, :except => [:index, :create]
accept_api_auth :index, :show, :create, :destroy
def index
@relations = @issue.relations
respond_to do |format|
format.html { render :nothing => true }
format.api
end
end
def show
raise Unauthorized unless @relation.visible?
respond_to do |format|
format.html { render :nothing => true }
format.api
end
end
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
def create
before_filter :find_issue, :find_project_from_association, :authorize
def new
@relation = IssueRelation.new(params[:relation])
@relation.issue_from = @issue
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
saved = @relation.save
@relation.save if request.post?
respond_to do |format|
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @issue }
format.js do
@@ -60,38 +37,28 @@ class IssueRelationsController < ApplicationController
end
end
end
format.api {
if saved
render :action => 'show', :status => :created, :location => relation_url(@relation)
else
render_validation_errors(@relation)
end
end
end
def destroy
relation = IssueRelation.find(params[:id])
if request.post? && @issue.relations.include?(relation)
relation.destroy
@issue.reload
end
respond_to do |format|
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @issue }
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
verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
def destroy
raise Unauthorized unless @relation.deletable?
@relation.destroy
respond_to do |format|
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @issue }
format.js { render(:update) {|page| page.remove "relation-#{@relation.id}"} }
format.api { head :ok }
end
end
private
def find_issue
@issue = @object = Issue.find(params[:issue_id])
rescue ActiveRecord::RecordNotFound
render_404
end
def find_relation
@relation = IssueRelation.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

@@ -1,37 +1,31 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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 IssueStatusesController < ApplicationController
layout 'admin'
before_filter :require_admin
before_filter :require_admin, :except => :index
before_filter :require_admin_or_api_request, :only => :index
accept_api_auth :index
verify :method => :post, :only => [ :destroy, :create, :update, :move, :update_issue_done_ratio ],
:redirect_to => { :action => :index }
def index
respond_to do |format|
format.html {
@issue_status_pages, @issue_statuses = paginate :issue_statuses, :per_page => 25, :order => "position"
render :action => "index", :layout => false if request.xhr?
}
format.api {
@issue_statuses = IssueStatus.all(:order => 'position')
}
end
@issue_status_pages, @issue_statuses = paginate :issue_statuses, :per_page => 25, :order => "position"
render :action => "index", :layout => false if request.xhr?
end
def new
@@ -40,7 +34,7 @@ class IssueStatusesController < ApplicationController
def create
@issue_status = IssueStatus.new(params[:issue_status])
if request.post? && @issue_status.save
if @issue_status.save
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'index'
else
@@ -54,7 +48,7 @@ class IssueStatusesController < ApplicationController
def update
@issue_status = IssueStatus.find(params[:id])
if request.put? && @issue_status.update_attributes(params[:issue_status])
if @issue_status.update_attributes(params[:issue_status])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'index'
else
@@ -62,7 +56,6 @@ class IssueStatusesController < ApplicationController
end
end
verify :method => :delete, :only => :destroy, :redirect_to => { :action => :index }
def destroy
IssueStatus.find(params[:id]).destroy
redirect_to :action => 'index'
@@ -70,9 +63,9 @@ class IssueStatusesController < ApplicationController
flash[:error] = l(:error_unable_delete_issue_status)
redirect_to :action => 'index'
end
def update_issue_done_ratio
if request.post? && IssueStatus.update_issue_done_ratios
if IssueStatus.update_issue_done_ratios
flash[:notice] = l(:notice_issue_done_ratios_updated)
else
flash[:error] = l(:error_issue_done_ratios_not_updated)

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2008 Jean-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.
@@ -18,7 +18,7 @@
class IssuesController < ApplicationController
menu_item :new_issue, :only => [:new, :create]
default_search_scope :issues
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]
@@ -27,14 +27,13 @@ class IssuesController < ApplicationController
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_rss_auth :index, :show
accept_api_auth :index, :show, :create, :update, :destroy
accept_key_auth :index, :show, :create, :update, :destroy
rescue_from Query::StatementInvalid, :with => :query_statement_invalid
helper :journals
helper :projects
include ProjectsHelper
include ProjectsHelper
helper :custom_fields
include CustomFieldsHelper
helper :issue_relations
@@ -61,12 +60,12 @@ class IssuesController < ApplicationController
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(@query.sortable_columns)
if @query.valid?
case params[:format]
when 'csv', 'pdf'
@@ -78,53 +77,44 @@ class IssuesController < ApplicationController
else
@limit = per_page_option
end
@issue_count = @query.issue_count
@issue_pages = Paginator.new self, @issue_count, @limit, params['page']
@offset ||= @issue_pages.current.offset
@issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
:order => sort_clause,
:offset => @offset,
:order => sort_clause,
:offset => @offset,
:limit => @limit)
@issue_count_by_group = @query.issue_count_by_group
respond_to do |format|
format.html { render :template => 'issues/index', :layout => !request.xhr? }
format.api {
Issue.load_relations(@issues) if include_in_api_response?('relations')
}
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, @query, params), :type => 'text/csv; header=present', :filename => 'export.csv') }
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') }
end
else
respond_to do |format|
format.html { render(:template => 'issues/index', :layout => !request.xhr?) }
format.any(:atom, :csv, :pdf) { render(:nothing => true) }
format.api { render_validation_errors(@query) }
end
# Send html if the query is not valid
render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
end
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?
if User.current.allowed_to?(:view_changesets, @project)
@changesets = @issue.changesets.visible.all
@changesets.reverse! if User.current.wants_comments_in_reverse_order?
end
@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.active
@time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
@priorities = IssuePriority.all
@time_entry = TimeEntry.new
respond_to do |format|
format.html { render :template => 'issues/show' }
format.html { render :template => 'issues/show.rhtml' }
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") }
@@ -144,11 +134,11 @@ class IssuesController < ApplicationController
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 {
render_attachment_warning_if_needed(@issue)
flash[:notice] = l(:notice_issue_successful_create, :id => "<a href='#{issue_path(@issue)}'>##{@issue.id}</a>")
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 })
}
@@ -162,7 +152,7 @@ class IssuesController < ApplicationController
end
end
end
def edit
update_issue_from_params
@@ -224,7 +214,7 @@ class IssuesController < ApplicationController
set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project})
end
def destroy
@hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
if @hours > 0
@@ -261,35 +251,29 @@ class IssuesController < ApplicationController
private
def find_issue
# Issue.visible.find(...) can not be used to redirect user to the login form
# if the issue actually exists but requires authentication
@issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
unless @issue.visible?
deny_access
return
end
@project = @issue.project
rescue ActiveRecord::RecordNotFound
render_404
end
def find_project
project_id = (params[: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.active
@priorities = IssuePriority.all
@edit_allowed = User.current.allowed_to?(:edit_issues, @project)
@time_entry = TimeEntry.new(:issue => @issue, :project => @issue.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]
@@ -305,23 +289,23 @@ private
else
@issue = @project.issues.visible.find(params[:id])
end
@issue.project = @project
@issue.author = User.current
# Tracker must be set before custom field values
@issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
if @issue.tracker.nil?
render_error l(:error_no_tracker_in_project)
return false
end
@issue.start_date ||= Date.today if Setting.default_issue_start_date_to_creation_date?
@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
@priorities = IssuePriority.active
@issue.author = User.current
@priorities = IssuePriority.all
@allowed_statuses = @issue.new_statuses_allowed_to(User.current, true)
end

View File

@@ -1,27 +1,26 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2008 Jean-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 JournalsController < ApplicationController
before_filter :find_journal, :only => [:edit, :diff]
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, :diff]
accept_rss_auth :index
menu_item :issues
before_filter :authorize, :only => [:new, :edit]
accept_key_auth :index
helper :issues
helper :custom_fields
@@ -34,9 +33,9 @@ class JournalsController < ApplicationController
retrieve_query
sort_init 'id', 'desc'
sort_update(@query.sortable_columns)
if @query.valid?
@journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC",
@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)
@@ -44,18 +43,7 @@ class JournalsController < ApplicationController
rescue ActiveRecord::RecordNotFound
render_404
end
def diff
@issue = @journal.issue
if params[:detail_id].present?
@detail = @journal.details.find_by_id(params[:detail_id])
else
@detail = @journal.details.detect {|d| d.prop_key == 'description'}
end
(render_404; return false) unless @issue && @detail
@diff = Redmine::Helpers::Diff.new(@detail.value, @detail.old_value)
end
def new
journal = Journal.find(params[:journal_id]) if params[:journal_id]
if journal
@@ -69,7 +57,7 @@ class JournalsController < ApplicationController
text = text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]')
content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> "
content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
render(:update) { |page|
page.<< "$('notes').value = \"#{escape_javascript content}\";"
page.show 'update'
@@ -78,9 +66,8 @@ class JournalsController < ApplicationController
page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;"
}
end
def edit
(render_403; return false) unless @journal.editable_by?(User.current)
if request.post?
@journal.update_attributes(:notes => params[:notes]) if params[:notes]
@journal.destroy if @journal.details.empty? && @journal.notes.blank?
@@ -93,17 +80,17 @@ class JournalsController < ApplicationController
respond_to do |format|
format.html {
# TODO: implement non-JS journal update
render :nothing => true
render :nothing => true
}
format.js
end
end
end
private
private
def find_journal
@journal = Journal.find(params[:id])
(render_403; return false) unless @journal.editable_by?(User.current)
@project = @journal.journalized.project
rescue ActiveRecord::RecordNotFound
render_404

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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.
@@ -18,7 +18,7 @@
class LdapAuthSourcesController < AuthSourcesController
protected
def auth_source_class
AuthSourceLdap
end

View File

@@ -1,27 +1,27 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2008 Jean-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 MailHandlerController < ActionController::Base
before_filter :check_credential
verify :method => :post,
:only => :index,
:render => { :nothing => true, :status => 405 }
# Submits an incoming email to MailHandler
def index
options = params.dup
@@ -32,9 +32,9 @@ class MailHandlerController < ActionController::Base
render :nothing => true, :status => :unprocessable_entity
end
end
private
def check_credential
User.current = nil
unless Setting.mail_handler_api_enabled? && params[:key].to_s == Setting.mail_handler_api_key

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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.
@@ -28,10 +28,10 @@ class MembersController < ApplicationController
attrs = params[:member].dup
if (user_ids = attrs.delete(:user_ids))
user_ids.each do |user_id|
members << Member.new(:role_ids => params[:member][:role_ids], :user_id => user_id)
members << Member.new(attrs.merge(:user_id => user_id))
end
else
members << Member.new(:role_ids => params[:member][:role_ids], :user_id => params[:member][:user_id])
members << Member.new(attrs)
end
@project.members << members
end
@@ -40,8 +40,8 @@ class MembersController < ApplicationController
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
format.js {
render(:update) {|page|
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}") }
@@ -58,20 +58,17 @@ class MembersController < ApplicationController
page.alert(l(:notice_failed_to_save_members, :errors => errors.join(', ')))
}
}
end
end
end
def edit
if params[:member]
@member.role_ids = params[:member][:role_ids]
end
if request.post? and @member.save
if request.post? and @member.update_attributes(params[:member])
respond_to do |format|
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
format.js {
render(:update) {|page|
format.js {
render(:update) {|page|
page.replace_html "tab-content-members", :partial => 'projects/settings/members'
page << 'hideOnLoad()'
page.visual_effect(:highlight, "member-#{@member.id}")
@@ -94,9 +91,9 @@ class MembersController < ApplicationController
}
end
end
def autocomplete_for_member
@principals = Principal.active.not_member_of(@project).like(params[:q]).all(:limit => 100)
@principals = Principal.active.like(params[:q]).find(:all, :limit => 100) - @project.principals
render :layout => false
end

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-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.
@@ -27,10 +27,10 @@ class MessagesController < ApplicationController
helper :watchers
helper :attachments
include AttachmentsHelper
include AttachmentsHelper
REPLIES_PER_PAGE = 25 unless const_defined?(:REPLIES_PER_PAGE)
# Show a topic and its replies
def show
page = params[:page]
@@ -39,24 +39,27 @@ class MessagesController < ApplicationController
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
# Create a new topic
def new
@message = Message.new
@message = Message.new(params[:message])
@message.author = User.current
@message.board = @board
@message.safe_attributes = params[:message]
if params[:message] && User.current.allowed_to?(:edit_messages, @project)
@message.locked = params[:message]['locked']
@message.sticky = params[:message]['sticky']
end
if request.post? && @message.save
call_hook(:controller_messages_new_after_save, { :params => params, :message => @message})
attachments = Attachment.attach_files(@message, params[:attachments])
@@ -67,10 +70,9 @@ class MessagesController < ApplicationController
# Reply to a topic
def reply
@reply = Message.new
@reply = Message.new(params[:reply])
@reply.author = User.current
@reply.board = @board
@reply.safe_attributes = params[:reply]
@topic.children << @reply
if !@reply.new_record?
call_hook(:controller_messages_reply_after_save, { :params => params, :message => @reply})
@@ -83,8 +85,11 @@ class MessagesController < ApplicationController
# Edit a message
def edit
(render_403; return false) unless @message.editable_by?(User.current)
@message.safe_attributes = params[:message]
if request.post? && @message.save
if params[:message]
@message.locked = params[:message]['locked']
@message.sticky = params[:message]['sticky']
end
if request.post? && @message.update_attributes(params[:message])
attachments = Attachment.attach_files(@message, params[:attachments])
render_attachment_warning_if_needed(@message)
flash[:notice] = l(:notice_successful_update)
@@ -92,7 +97,7 @@ class MessagesController < ApplicationController
redirect_to :action => 'show', :board_id => @message.board, :id => @message.root, :r => (@message.parent_id && @message.id)
end
end
# Delete a messages
def destroy
(render_403; return false) unless @message.destroyable_by?(User.current)
@@ -101,7 +106,7 @@ class MessagesController < ApplicationController
{ :controller => 'boards', :action => 'show', :project_id => @project, :id => @board } :
{ :action => 'show', :id => @message.parent, :r => @message }
end
def quote
user = @message.author
text = @message.content
@@ -110,7 +115,7 @@ class MessagesController < ApplicationController
content = "#{ll(Setting.default_language, :text_user_wrote, user)}\\n> "
content << text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]').gsub('"', '\"').gsub(/(\r?\n|\r\n?)/, "\\n> ") + "\\n\\n"
render(:update) { |page|
page << "$('message_subject').value = \"#{subject}\";"
page << "$('reply_subject').value = \"#{subject}\";"
page.<< "$('message_content').value = \"#{content}\";"
page.show 'reply'
page << "Form.Element.focus('message_content');"
@@ -118,14 +123,14 @@ class MessagesController < ApplicationController
page << "$('message_content').scrollTop = $('message_content').scrollHeight - $('message_content').clientHeight;"
}
end
def preview
message = @board.messages.find_by_id(params[:id])
@attachements = message.attachments if message
@text = (params[:message] || params[:reply])[:content]
render :partial => 'common/preview'
end
private
def find_message
find_board
@@ -134,7 +139,7 @@ private
rescue ActiveRecord::RecordNotFound
render_404
end
def find_board
@board = Board.find(params[:board_id], :include => :project)
@project = @board.project

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-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.
@@ -31,8 +31,8 @@ class MyController < ApplicationController
'timelog' => :label_spent_time
}.merge(Redmine::Views::MyPage::Block.additional_blocks).freeze
DEFAULT_LAYOUT = { 'left' => ['issuesassignedtome'],
'right' => ['issuesreportedbyme']
DEFAULT_LAYOUT = { 'left' => ['issuesassignedtome'],
'right' => ['issuesreportedbyme']
}.freeze
verify :xhr => true,
@@ -88,7 +88,7 @@ class MyController < ApplicationController
end
end
end
# Create a new feeds key
def reset_rss_key
if request.post?
@@ -122,7 +122,7 @@ class MyController < ApplicationController
@block_options = []
BLOCKS.each {|k, v| @block_options << [l("my.blocks.#{v}", :default => [v, v.to_s.humanize]), k.dasherize]}
end
# Add a block to user's page
# The block is added on top of the page
# params[:block] : id of the block to add
@@ -136,10 +136,10 @@ class MyController < ApplicationController
# add it on top
layout['top'].unshift block
@user.pref[:my_page_layout] = layout
@user.pref.save
@user.pref.save
render :partial => "block", :locals => {:user => @user, :block_name => block}
end
# Remove a block to user's page
# params[:block] : id of the block to remove
def remove_block
@@ -149,7 +149,7 @@ class MyController < ApplicationController
layout = @user.pref[:my_page_layout] || {}
%w(top left right).each {|f| (layout[f] ||= []).delete block }
@user.pref[:my_page_layout] = layout
@user.pref.save
@user.pref.save
render :nothing => true
end
@@ -169,7 +169,7 @@ class MyController < ApplicationController
}
layout[group] = group_items
@user.pref[:my_page_layout] = layout
@user.pref.save
@user.pref.save
end
end
render :nothing => true

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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.
@@ -23,11 +23,8 @@ class NewsController < ApplicationController
before_filter :find_project, :only => [:new, :create]
before_filter :authorize, :except => [:index]
before_filter :find_optional_project, :only => :index
accept_rss_auth :index
accept_api_auth :index
helper :watchers
accept_key_auth :index
def index
case params[:format]
when 'xml', 'json'
@@ -35,9 +32,9 @@ class NewsController < ApplicationController
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
@@ -45,17 +42,14 @@ class NewsController < ApplicationController
:order => "#{News.table_name}.created_on DESC",
:offset => @offset,
:limit => @limit)
respond_to do |format|
format.html {
@news = News.new # for adding news inline
render :layout => false if request.xhr?
}
format.html { render :layout => false if request.xhr? }
format.api
format.atom { render_feed(@newss, :title => (@project ? @project.name : Setting.app_title) + ": #{l(:label_news_plural)}") }
end
end
def show
@comments = @news.comments
@comments.reverse! if User.current.wants_comments_in_reverse_order?
@@ -67,8 +61,8 @@ class NewsController < ApplicationController
def create
@news = News.new(:project => @project, :author => User.current)
@news.safe_attributes = params[:news]
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
@@ -80,10 +74,9 @@ class NewsController < ApplicationController
def edit
end
def update
@news.safe_attributes = params[:news]
if request.put? and @news.save
if request.put? and @news.update_attributes(params[:news])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'show', :id => @news
else
@@ -95,14 +88,14 @@ class NewsController < ApplicationController
@news.destroy
redirect_to :action => 'index', :project_id => @project
end
private
def find_project
@project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound
render_404
end
def find_optional_project
return true unless params[:project_id]
@project = Project.find(params[:project_id])

View File

@@ -1,20 +1,3 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# 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 PreviewsController < ApplicationController
before_filter :find_project
@@ -39,12 +22,12 @@ class PreviewsController < ApplicationController
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

@@ -1,7 +1,7 @@
class ProjectEnumerationsController < ApplicationController
before_filter :find_project_by_project_id
before_filter :authorize
def update
if request.put? && params[:enumerations]
Project.transaction do
@@ -11,7 +11,7 @@ class ProjectEnumerationsController < ApplicationController
end
flash[:notice] = l(:notice_successful_update)
end
redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project
end

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-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.
@@ -19,13 +19,12 @@ class ProjectsController < ApplicationController
menu_item :overview
menu_item :roadmap, :only => :roadmap
menu_item :settings, :only => :settings
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_rss_auth :index
accept_api_auth :index, :show, :create, :update, :destroy
accept_key_auth :index, :show, :create, :update, :destroy
after_filter :only => [:create, :edit, :update, :archive, :unarchive, :destroy] do |controller|
if controller.request.post?
@@ -36,19 +35,19 @@ class ProjectsController < ApplicationController
helper :sort
include SortHelper
helper :custom_fields
include CustomFieldsHelper
include CustomFieldsHelper
helper :issues
helper :queries
include QueriesHelper
helper :repositories
include RepositoriesHelper
include ProjectsHelper
# Lists visible projects
def index
respond_to do |format|
format.html {
@projects = Project.visible.find(:all, :order => 'lft')
format.html {
@projects = Project.visible.find(:all, :order => 'lft')
}
format.api {
@offset, @limit = api_offset_and_limit
@@ -62,12 +61,11 @@ class ProjectsController < ApplicationController
}
end
end
def new
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.all
@project = Project.new
@project.safe_attributes = params[:project]
@project = Project.new(params[:project])
end
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
@@ -86,12 +84,9 @@ class ProjectsController < ApplicationController
@project.members << m
end
respond_to do |format|
format.html {
format.html {
flash[:notice] = l(:notice_successful_create)
redirect_to(params[:continue] ?
{:controller => 'projects', :action => 'new', :project => {:parent_id => @project.parent_id}.reject {|k,v| v.nil?}} :
{:controller => 'projects', :action => 'settings', :id => @project}
)
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
@@ -101,9 +96,9 @@ class ProjectsController < ApplicationController
format.api { render_validation_errors(@project) }
end
end
end
def copy
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.all
@@ -117,7 +112,7 @@ class ProjectsController < ApplicationController
@project.identifier = Project.next_identifier if Setting.sequential_project_identifiers?
else
redirect_to :controller => 'admin', :action => 'projects'
end
end
else
Mailer.with_deliveries(params[:notifications] == '1') do
@project = Project.new
@@ -145,27 +140,28 @@ class ProjectsController < ApplicationController
# try to redirect to the requested menu item
redirect_to_project_menu_item(@project, params[:jump]) && return
end
@users_by_role = @project.users_by_role
@subprojects = @project.children.visible.all
@subprojects = @project.children.visible
@news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
@trackers = @project.rolled_up_trackers
cond = @project.project_condition(Setting.display_subprojects_issues?)
@open_issues_by_tracker = Issue.visible.count(:group => :tracker,
:include => [:project, :status, :tracker],
:conditions => ["(#{cond}) AND #{IssueStatus.table_name}.is_closed=?", false])
@total_issues_by_tracker = Issue.visible.count(:group => :tracker,
:include => [:project, :status, :tracker],
:conditions => cond)
if User.current.allowed_to?(:view_time_entries, @project)
@total_hours = TimeEntry.visible.sum(:hours, :include => :project, :conditions => cond).to_f
TimeEntry.visible_by(User.current) do
@total_hours = TimeEntry.sum(:hours,
:include => :project,
:conditions => cond).to_f
end
@key = User.current.rss_key
respond_to do |format|
format.html
format.api
@@ -180,7 +176,7 @@ class ProjectsController < ApplicationController
@repository ||= @project.repository
@wiki ||= @project.wiki
end
def edit
end
@@ -191,7 +187,7 @@ class ProjectsController < ApplicationController
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 {
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'settings', :id => @project
}
@@ -199,7 +195,7 @@ class ProjectsController < ApplicationController
end
else
respond_to do |format|
format.html {
format.html {
settings
render :action => 'settings'
}
@@ -223,12 +219,12 @@ class ProjectsController < ApplicationController
end
redirect_to(url_for(:controller => 'admin', :action => 'projects', :status => params[:status]))
end
def unarchive
@project.unarchive if request.post? && !@project.active?
redirect_to(url_for(:controller => 'admin', :action => 'projects', :status => params[:status]))
end
# Delete @project
def destroy
@project_to_destroy = @project

View File

@@ -1,97 +1,64 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-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 QueriesController < ApplicationController
menu_item :issues
before_filter :find_query, :except => [:new, :create, :index]
before_filter :find_optional_project, :only => [:new, :create]
accept_api_auth :index
include QueriesHelper
def index
case params[:format]
when 'xml', 'json'
@offset, @limit = api_offset_and_limit
else
@limit = per_page_option
end
@query_count = Query.visible.count
@query_pages = Paginator.new self, @query_count, @limit, params['page']
@queries = Query.visible.all(:limit => @limit, :offset => @offset, :order => "#{Query.table_name}.name")
respond_to do |format|
format.html { render :nothing => true }
format.api
end
end
before_filter :find_query, :except => :new
before_filter :find_optional_project, :only => :new
def new
@query = Query.new
@query.user = User.current
@query.project = @project
@query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
build_query_from_params
end
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
def create
@query = Query.new(params[:query])
@query.user = User.current
@query.project = params[:query_is_for_all] ? nil : @project
@query.user = User.current
@query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
build_query_from_params
@query.column_names = nil if params[:default_columns]
if @query.save
@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
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'issues', :action => 'index', :project_id => @project, :query_id => @query
else
render :action => 'new', :layout => !request.xhr?
return
end
render :layout => false if request.xhr?
end
def edit
end
verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
def update
@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?
build_query_from_params
@query.column_names = nil if params[:default_columns]
if @query.save
flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'issues', :action => 'index', :project_id => @project, :query_id => @query
else
render :action => 'edit'
if request.post?
@query.filters = {}
@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?
@query.column_names = nil if params[:default_columns]
if @query.save
flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'issues', :action => 'index', :project_id => @project, :query_id => @query
end
end
end
verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
def destroy
@query.destroy
@query.destroy if request.post?
redirect_to :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1
end
private
def find_query
@query = Query.find(params[:id])
@@ -100,7 +67,7 @@ private
rescue ActiveRecord::RecordNotFound
render_404
end
def find_optional_project
@project = Project.find(params[:project_id]) if params[:project_id]
render_403 unless User.current.allowed_to?(:save_queries, @project, :global => true)

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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.
@@ -24,8 +24,8 @@ class ReportsController < ApplicationController
@versions = @project.shared_versions.sort
@priorities = IssuePriority.all
@categories = @project.issue_categories
@assignees = (Setting.issue_group_assignment? ? @project.principals : @project.users).sort
@authors = @project.users.sort
@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)
@@ -37,7 +37,7 @@ class ReportsController < ApplicationController
@issues_by_subproject = Issue.by_subproject(@project) || []
render :template => "reports/issue_report"
end
end
def issue_report_details
case params[:detail]
@@ -63,12 +63,12 @@ class ReportsController < ApplicationController
@report_title = l(:field_category)
when "assigned_to"
@field = "assigned_to_id"
@rows = (Setting.issue_group_assignment? ? @project.principals : @project.users).sort
@rows = @project.members.collect { |m| m.user }.sort
@data = Issue.by_assigned_to(@project)
@report_title = l(:field_assigned_to)
when "author"
@field = "author_id"
@rows = @project.users.sort
@rows = @project.members.collect { |m| m.user }.sort
@data = Issue.by_author(@project)
@report_title = l(:field_author)
when "subproject"

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-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.
@@ -26,45 +26,33 @@ class RepositoriesController < ApplicationController
menu_item :repository
menu_item :settings, :only => :edit
default_search_scope :changesets
before_filter :find_repository, :except => :edit
before_filter :find_project, :only => :edit
before_filter :authorize
accept_rss_auth :revisions
accept_key_auth :revisions
rescue_from Redmine::Scm::Adapters::CommandFailed, :with => :show_error_command_failed
def edit
@repository = @project.repository
if !@repository && !params[:repository_scm].blank?
if !@repository
@repository = Repository.factory(params[:repository_scm])
@repository.project = @project if @repository
end
if request.post? && @repository
p1 = params[:repository]
p = {}
p_extra = {}
p1.each do |k, v|
if k =~ /^extra_/
p_extra[k] = v
else
p[k] = v
end
end
@repository.attributes = p
@repository.merge_extra_info(p_extra)
@repository.attributes = params[:repository]
@repository.save
end
render(:update) do |page|
page.replace_html "tab-content-repository",
:partial => 'projects/settings/repository'
page.replace_html "tab-content-repository", :partial => 'projects/settings/repository'
if @repository && !@project.repository
@project.reload # needed to reload association
@project.reload #needed to reload association
page.replace_html "main-menu", render_main_menu(@project)
end
end
end
def committers
@committers = @repository.committers
@users = @project.users
@@ -79,20 +67,16 @@ class RepositoriesController < ApplicationController
redirect_to :action => 'committers', :id => @project
end
end
def destroy
@repository.destroy
redirect_to :controller => 'projects',
:action => 'settings',
:id => @project,
:tab => 'repository'
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
end
def show
def show
@repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty?
@entries = @repository.entries(@path, @rev)
@changeset = @repository.find_changeset_by_name(@rev)
if request.xhr?
@entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
else
@@ -104,31 +88,30 @@ class RepositoriesController < ApplicationController
end
alias_method :browse, :show
def changes
@entry = @repository.entry(@path, @rev)
(show_error_not_found; return) unless @entry
@changesets = @repository.latest_changesets(@path, @rev, Setting.repository_log_display_limit.to_i)
@properties = @repository.properties(@path, @rev)
@changeset = @repository.find_changeset_by_name(@rev)
end
def revisions
@changeset_count = @repository.changesets.count
@changeset_pages = Paginator.new self, @changeset_count,
per_page_option,
params['page']
per_page_option,
params['page']
@changesets = @repository.changesets.find(:all,
:limit => @changeset_pages.items_per_page,
:offset => @changeset_pages.current.offset,
:include => [:user, :repository, :parents])
:limit => @changeset_pages.items_per_page,
:offset => @changeset_pages.current.offset,
:include => [:user, :repository])
respond_to do |format|
format.html { render :layout => false if request.xhr? }
format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
end
end
def entry
@entry = @repository.entry(@path, @rev)
(show_error_not_found; return) unless @entry
@@ -138,55 +121,25 @@ class RepositoriesController < ApplicationController
@content = @repository.cat(@path, @rev)
(show_error_not_found; return) unless @content
if 'raw' == params[:format] ||
(@content.size && @content.size > Setting.file_max_size_displayed.to_i.kilobyte) ||
! is_entry_text_data?(@content, @path)
if 'raw' == params[:format] || @content.is_binary_data? || (@entry.size && @entry.size > Setting.file_max_size_displayed.to_i.kilobyte)
# Force the download
send_opt = { :filename => filename_for_content_disposition(@path.split('/').last) }
send_type = Redmine::MimeType.of(@path)
send_opt[:type] = send_type.to_s if send_type
send_data @content, send_opt
send_data @content, :filename => filename_for_content_disposition(@path.split('/').last)
else
# Prevent empty lines when displaying a file with Windows style eol
# TODO: UTF-16
# Is this needs? AttachmentsController reads file simply.
@content.gsub!("\r\n", "\n")
@changeset = @repository.find_changeset_by_name(@rev)
end
end
end
def is_entry_text_data?(ent, path)
# UTF-16 contains "\x00".
# It is very strict that file contains less than 30% of ascii symbols
# in non Western Europe.
return true if Redmine::MimeType.is_type?('text', path)
# Ruby 1.8.6 has a bug of integer divisions.
# http://apidock.com/ruby/v1_8_6_287/String/is_binary_data%3F
return false if ent.is_binary_data?
true
end
private :is_entry_text_data?
def annotate
@entry = @repository.entry(@path, @rev)
(show_error_not_found; return) unless @entry
@annotate = @repository.scm.annotate(@path, @rev)
if @annotate.nil? || @annotate.empty?
(render_error l(:error_scm_annotate); return)
end
ann_buf_size = 0
@annotate.lines.each do |buf|
ann_buf_size += buf.size
end
if ann_buf_size > Setting.file_max_size_displayed.to_i.kilobyte
(render_error l(:error_scm_annotate_big_text_file); return)
end
@changeset = @repository.find_changeset_by_name(@rev)
(render_error l(:error_scm_annotate); return) if @annotate.nil? || @annotate.empty?
end
def revision
raise ChangesetNotFound if @rev.blank?
raise ChangesetNotFound if @rev.nil? || @rev.empty?
@changeset = @repository.find_changeset_by_name(@rev)
raise ChangesetNotFound unless @changeset
@@ -197,7 +150,7 @@ class RepositoriesController < ApplicationController
rescue ChangesetNotFound
show_error_not_found
end
def diff
if params[:format] == 'diff'
@diff = @repository.diff(@path, @rev, @rev_to)
@@ -210,14 +163,14 @@ class RepositoriesController < ApplicationController
else
@diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
@diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
# Save diff type as user preference
if User.current.logged? && @diff_type != User.current.pref[:diff_type]
User.current.pref[:diff_type] = @diff_type
User.current.preference.save
end
@cache_key = "repositories/diff/#{@repository.id}/" +
Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}-#{current_language}")
@cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}")
unless read_fragment(@cache_key)
@diff = @repository.diff(@path, @rev, @rev_to)
show_error_not_found unless @diff
@@ -225,15 +178,14 @@ class RepositoriesController < ApplicationController
@changeset = @repository.find_changeset_by_name(@rev)
@changeset_to = @rev_to ? @repository.find_changeset_by_name(@rev_to) : nil
@diff_format_revisions = @repository.diff_format_revisions(@changeset, @changeset_to)
end
end
def stats
def stats
end
def graph
data = nil
data = nil
case params[:graph]
when "commits_per_month"
data = graph_commits_per_month(@repository)
@@ -247,7 +199,7 @@ class RepositoriesController < ApplicationController
render_404
end
end
private
REV_PARAM_RE = %r{\A[a-f0-9]*\Z}i
@@ -258,9 +210,9 @@ class RepositoriesController < ApplicationController
(render_404; return false) unless @repository
@path = params[:path].join('/') unless params[:path].nil?
@path ||= ''
@rev = params[:rev].blank? ? @repository.default_branch : params[:rev].to_s.strip
@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
@@ -275,31 +227,27 @@ class RepositoriesController < ApplicationController
def show_error_not_found
render_error :message => l(:error_scm_not_found), :status => 404
end
# Handler for Redmine::Scm::Adapters::CommandFailed exception
def show_error_command_failed(exception)
render_error l(:error_scm_command_failed, exception.message)
end
def graph_commits_per_month(repository)
@date_to = Date.today
@date_from = @date_to << 11
@date_from = Date.civil(@date_from.year, @date_from.month, 1)
commits_by_day = repository.changesets.count(
:all, :group => :commit_date,
:conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
commits_by_day = repository.changesets.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
commits_by_month = [0] * 12
commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
changes_by_day = repository.changes.count(
:all, :group => :commit_date,
:conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
changes_by_day = repository.changes.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
changes_by_month = [0] * 12
changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
fields = []
12.times {|m| fields << month_name(((Date.today.month - 1 - m) % 12) + 1)}
graph = SVG::Graph::Bar.new(
:height => 300,
:width => 800,
@@ -311,7 +259,7 @@ class RepositoriesController < ApplicationController
:graph_title => l(:label_commits_per_month),
:show_graph_title => true
)
graph.add_data(
:data => commits_by_month[0..11].reverse,
:title => l(:label_revision_plural)
@@ -321,7 +269,7 @@ class RepositoriesController < ApplicationController
:data => changes_by_month[0..11].reverse,
:title => l(:label_change_plural)
)
graph.burn
end
@@ -331,18 +279,18 @@ class RepositoriesController < ApplicationController
changes_by_author = repository.changes.count(:all, :group => :committer)
h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
fields = commits_by_author.collect {|r| r.first}
commits_data = commits_by_author.collect {|r| r.last}
changes_data = commits_by_author.collect {|r| h[r.first] || 0}
fields = fields + [""]*(10 - fields.length) if fields.length<10
commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
# Remove email adress in usernames
fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
graph = SVG::Graph::BarHorizontal.new(
:height => 400,
:width => 800,
@@ -354,18 +302,22 @@ class RepositoriesController < ApplicationController
:graph_title => l(:label_commits_per_author),
:show_graph_title => true
)
graph.add_data(
:data => commits_data,
:title => l(:label_revision_plural)
)
graph.add_data(
:data => changes_data,
:title => l(:label_change_plural)
)
graph.burn
end
end
end
class Date
def months_ago(date = Date.today)
(date.year - self.year)*12 + (date.month - self.month)

View File

@@ -1,26 +1,26 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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 RolesController < ApplicationController
layout 'admin'
before_filter :require_admin
verify :method => :post, :only => [ :destroy ],
verify :method => :post, :only => [ :destroy, :move ],
:redirect_to => { :action => :index }
def index
@@ -38,10 +38,9 @@ class RolesController < ApplicationController
end
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'index'
else
@permissions = @role.setable_permissions
@roles = Role.find :all, :order => 'builtin, position'
end
@permissions = @role.setable_permissions
@roles = Role.find :all, :order => 'builtin, position'
end
def edit
@@ -49,9 +48,8 @@ class RolesController < ApplicationController
if request.post? and @role.update_attributes(params[:role])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'index'
else
@permissions = @role.setable_permissions
end
@permissions = @role.setable_permissions
end
def destroy
@@ -62,8 +60,8 @@ class RolesController < ApplicationController
flash[:error] = l(:error_can_not_remove_role)
redirect_to :action => 'index'
end
def report
def report
@roles = Role.find(:all, :order => 'builtin, position')
@permissions = Redmine::AccessControl.permissions.select { |p| !p.public? }
if request.post?

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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.
@@ -24,9 +24,9 @@ class SearchController < ApplicationController
def index
@question = params[:q] || ""
@question.strip!
@all_words = params[:all_words] ? params[:all_words].present? : true
@titles_only = params[:titles_only] ? params[:titles_only].present? : false
@all_words = params[:all_words] || (params[:submit] ? false : true)
@titles_only = !params[:titles_only].nil?
projects_to_search =
case params[:scope]
when 'all'
@@ -38,16 +38,16 @@ class SearchController < ApplicationController
else
@project
end
offset = nil
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.to_i)
redirect_to :controller => "issues", :action => "show", :id => $1
return
end
@object_types = Redmine::Search.available_search_types.dup
if projects_to_search.is_a? Project
# don't search projects
@@ -55,23 +55,23 @@ class SearchController < ApplicationController
# only show what the user is allowed to view
@object_types = @object_types.select {|o| User.current.allowed_to?("view_#{o}".to_sym, projects_to_search)}
end
@scope = @object_types.select {|t| params[t]}
@scope = @object_types if @scope.empty?
# 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 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
@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(@tokens, projects_to_search,
@@ -87,13 +87,13 @@ class SearchController < ApplicationController
if params[:previous].nil?
@pagination_previous_date = @results[0].event_datetime if offset && @results[0]
if @results.size > limit
@pagination_next_date = @results[limit-1].event_datetime
@pagination_next_date = @results[limit-1].event_datetime
@results = @results[0, limit]
end
else
@pagination_next_date = @results[-1].event_datetime if offset && @results[-1]
if @results.size > limit
@pagination_previous_date = @results[-(limit)].event_datetime
@pagination_previous_date = @results[-(limit)].event_datetime
@results = @results[-(limit), limit]
end
end
@@ -103,7 +103,7 @@ class SearchController < ApplicationController
render :layout => false if request.xhr?
end
private
private
def find_optional_project
return true unless params[:id]
@project = Project.find(params[:id])

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,7 +17,7 @@
class SettingsController < ApplicationController
layout 'admin'
before_filter :require_admin
def index
@@ -36,16 +36,16 @@ class SettingsController < ApplicationController
end
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'edit', :tab => params[:tab]
else
@options = {}
@options[:user_format] = User::USER_FORMATS.keys.collect {|f| [User.current.name(f), f.to_s] }
@deliveries = ActionMailer::Base.perform_deliveries
@guessed_host_and_path = request.host_with_port.dup
@guessed_host_and_path << ('/'+ Redmine::Utils.relative_url_root.gsub(%r{^\/}, '')) unless Redmine::Utils.relative_url_root.blank?
Redmine::Themes.rescan
return
end
@options = {}
@options[:user_format] = User::USER_FORMATS.keys.collect {|f| [User.current.name(f), f.to_s] }
@deliveries = ActionMailer::Base.perform_deliveries
@guessed_host_and_path = request.host_with_port.dup
@guessed_host_and_path << ('/'+ Redmine::Utils.relative_url_root.gsub(%r{^\/}, '')) unless Redmine::Utils.relative_url_root.blank?
Redmine::Themes.rescan
end
def plugin
@@ -54,10 +54,9 @@ class SettingsController < ApplicationController
Setting["plugin_#{@plugin.id}"] = params[:settings]
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'plugin', :id => @plugin.id
else
@partial = @plugin.settings[:partial]
@settings = Setting["plugin_#{@plugin.id}"]
end
@partial = @plugin.settings[:partial]
@settings = Setting["plugin_#{@plugin.id}"]
rescue Redmine::PluginNotFound
render_404
end

View File

@@ -1,29 +1,28 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-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 SysController < ActionController::Base
before_filter :check_enabled
def projects
p = Project.active.has_module(:repository).find(:all, :include => :repository, :order => 'identifier')
# extra_info attribute from repository breaks activeresource client
render :xml => p.to_xml(:only => [:id, :identifier, :name, :is_public, :status], :include => {:repository => {:only => [:id, :url]}})
render :xml => p.to_xml(:include => :repository)
end
def create_project_repository
project = Project.find(params[:id])
if project.repository
@@ -32,13 +31,13 @@ class SysController < ActionController::Base
logger.info "Repository for #{project.name} was reported to be created by #{request.remote_ip}."
project.repository = Repository.factory(params[:vendor], params[:repository])
if project.repository && project.repository.save
render :xml => project.repository.to_xml(:only => [:id, :url]), :status => 201
render :xml => project.repository, :status => 201
else
render :nothing => true, :status => 422
end
end
end
def fetch_changesets
projects = []
if params[:id]

View File

@@ -16,16 +16,16 @@ class TimeEntryReportsController < ApplicationController
@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?
@@ -41,9 +41,9 @@ class TimeEntryReportsController < ApplicationController
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'
@@ -56,9 +56,9 @@ class TimeEntryReportsController < ApplicationController
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
@@ -80,13 +80,13 @@ class TimeEntryReportsController < ApplicationController
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
@@ -141,7 +141,7 @@ class TimeEntryReportsController < ApplicationController
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)
@@ -170,7 +170,7 @@ class TimeEntryReportsController < ApplicationController
: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|
@@ -178,7 +178,7 @@ class TimeEntryReportsController < ApplicationController
: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)",

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 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
# 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.
@@ -18,20 +18,18 @@
class TimelogController < ApplicationController
menu_item :issues
before_filter :find_project, :only => [:new, :create]
before_filter :find_time_entry, :only => [:show, :edit, :update]
before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
before_filter :find_time_entry, :only => [:show, :edit, :update, :destroy]
before_filter :authorize, :except => [:index]
before_filter :find_optional_project, :only => [:index]
accept_rss_auth :index
accept_api_auth :index, :show, :create, :update, :destroy
accept_key_auth :index, :show, :create, :update, :destroy
helper :sort
include SortHelper
helper :issues
include TimelogHelper
helper :custom_fields
include CustomFieldsHelper
def index
sort_init 'spent_on', 'desc'
sort_update 'spent_on' => 'spent_on',
@@ -40,61 +38,65 @@ class TimelogController < ApplicationController
'project' => "#{Project.table_name}.name",
'issue' => 'issue_id',
'hours' => 'hours'
cond = ARCondition.new
if @issue
cond << "#{Issue.table_name}.root_id = #{@issue.root_id} AND #{Issue.table_name}.lft >= #{@issue.lft} AND #{Issue.table_name}.rgt <= #{@issue.rgt}"
elsif @project
if @project.nil?
cond << Project.allowed_to_condition(User.current, :view_time_entries)
elsif @issue.nil?
cond << @project.project_condition(Setting.display_subprojects_issues?)
else
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
cond << ['spent_on BETWEEN ? AND ?', @from, @to]
respond_to do |format|
format.html {
# Paginate results
@entry_count = TimeEntry.visible.count(:include => [:project, :issue], :conditions => cond.conditions)
@entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
@entries = TimeEntry.visible.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)
@total_hours = TimeEntry.visible.sum(:hours, :include => [:project, :issue], :conditions => cond.conditions).to_f
TimeEntry.visible_by(User.current) do
respond_to do |format|
format.html {
# Paginate results
@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)
@total_hours = TimeEntry.sum(:hours, :include => [:project, :issue], :conditions => cond.conditions).to_f
render :layout => !request.xhr?
}
format.api {
@entry_count = TimeEntry.visible.count(:include => [:project, :issue], :conditions => cond.conditions)
@offset, @limit = api_offset_and_limit
@entries = TimeEntry.visible.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
:conditions => cond.conditions,
:order => sort_clause,
:limit => @limit,
:offset => @offset)
}
format.atom {
entries = TimeEntry.visible.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
:conditions => cond.conditions,
:order => "#{TimeEntry.table_name}.created_on DESC",
:limit => Setting.feeds_limit.to_i)
render_feed(entries, :title => l(:label_spent_time))
}
format.csv {
# Export all entries
@entries = TimeEntry.visible.find(:all,
:include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}],
:conditions => cond.conditions,
:order => sort_clause)
send_data(entries_to_csv(@entries), :type => 'text/csv; header=present', :filename => 'timelog.csv')
}
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}],
:conditions => cond.conditions,
:order => "#{TimeEntry.table_name}.created_on DESC",
:limit => Setting.feeds_limit.to_i)
render_feed(entries, :title => l(:label_spent_time))
}
format.csv {
# Export all entries
@entries = TimeEntry.find(:all,
:include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}],
:conditions => cond.conditions,
:order => sort_clause)
send_data(entries_to_csv(@entries), :type => 'text/csv; header=present', :filename => 'timelog.csv')
}
end
end
end
def show
respond_to do |format|
# TODO: Implement html response
@@ -105,8 +107,8 @@ class TimelogController < ApplicationController
def new
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
@time_entry.safe_attributes = params[:time_entry]
@time_entry.attributes = params[:time_entry]
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
render :action => 'edit'
end
@@ -114,10 +116,10 @@ class TimelogController < ApplicationController
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.safe_attributes = params[:time_entry]
@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 {
@@ -131,21 +133,21 @@ class TimelogController < ApplicationController
format.html { render :action => 'edit' }
format.api { render_validation_errors(@time_entry) }
end
end
end
end
def edit
@time_entry.safe_attributes = params[:time_entry]
@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.safe_attributes = params[:time_entry]
@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 {
@@ -159,58 +161,30 @@ class TimelogController < ApplicationController
format.html { render :action => 'edit' }
format.api { render_validation_errors(@time_entry) }
end
end
end
def bulk_edit
@available_activities = TimeEntryActivity.shared.active
@custom_fields = TimeEntry.first.available_custom_fields
end
def bulk_update
attributes = parse_params_for_bulk_time_entry_attributes(params)
unsaved_time_entry_ids = []
@time_entries.each do |time_entry|
time_entry.reload
time_entry.safe_attributes = attributes
call_hook(:controller_time_entries_bulk_edit_before_save, { :params => params, :time_entry => time_entry })
unless time_entry.save
# Keep unsaved time_entry ids to display them in flash error
unsaved_time_entry_ids << time_entry.id
end
end
set_flash_from_bulk_time_entry_save(@time_entries, unsaved_time_entry_ids)
redirect_back_or_default({:controller => 'timelog', :action => 'index', :project_id => @projects.first})
end
end
verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
def destroy
@time_entries.each do |t|
begin
unless t.destroy && t.destroyed?
respond_to do |format|
format.html {
flash[:error] = l(:notice_unable_delete_time_entry)
redirect_to :back
}
format.api { render_validation_errors(t) }
end
return
end
rescue ::ActionController::RedirectBackError
redirect_to :action => 'index', :project_id => @projects.first
return
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
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_delete)
redirect_back_or_default(:action => 'index', :project_id => @projects.first)
}
format.api { head :ok }
end
rescue ::ActionController::RedirectBackError
redirect_to :action => 'index', :project_id => @time_entry.project
end
private
@@ -225,26 +199,6 @@ private
render_404
end
def find_time_entries
@time_entries = TimeEntry.find_all_by_id(params[:id] || params[:ids])
raise ActiveRecord::RecordNotFound if @time_entries.empty?
@projects = @time_entries.collect(&:project).compact.uniq
@project = @projects.first if @projects.size == 1
rescue ActiveRecord::RecordNotFound
render_404
end
def set_flash_from_bulk_time_entry_save(time_entries, unsaved_time_entry_ids)
if unsaved_time_entry_ids.empty?
flash[:notice] = l(:notice_successful_update) unless time_entries.empty?
else
flash[:error] = l(:notice_failed_to_save_time_entries,
:count => unsaved_time_entry_ids.size,
:total => time_entries.size,
:ids => '#' + unsaved_time_entry_ids.join(', #'))
end
end
def find_project
if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).present?
@issue = Issue.find(issue_id)
@@ -258,7 +212,7 @@ private
rescue ActiveRecord::RecordNotFound
render_404
end
def find_optional_project
if !params[:issue_id].blank?
@issue = Issue.find(params[:issue_id])
@@ -268,7 +222,7 @@ private
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
def retrieve_date_range
@free_period = false
@@ -309,16 +263,10 @@ private
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 parse_params_for_bulk_time_entry_attributes(params)
attributes = (params[:time_entry] || {}).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

@@ -1,46 +1,33 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-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 TrackersController < ApplicationController
layout 'admin'
before_filter :require_admin
before_filter :require_admin, :except => :index
before_filter :require_admin_or_api_request, :only => :index
accept_api_auth :index
verify :method => :post, :only => :destroy, :redirect_to => { :action => :index }
def index
respond_to do |format|
format.html {
@tracker_pages, @trackers = paginate :trackers, :per_page => 10, :order => 'position'
render :action => "index", :layout => false if request.xhr?
}
format.api {
@trackers = Tracker.all
}
end
@tracker_pages, @trackers = paginate :trackers, :per_page => 10, :order => 'position'
render :action => "index", :layout => false if request.xhr?
end
def new
@tracker ||= Tracker.new(params[:tracker])
@trackers = Tracker.find :all, :order => 'position'
@projects = Project.find(:all)
end
def create
@tracker = Tracker.new(params[:tracker])
if request.post? and @tracker.save
# workflow copy
@@ -51,27 +38,20 @@ class TrackersController < ApplicationController
redirect_to :action => 'index'
return
end
new
render :action => 'new'
@trackers = Tracker.find :all, :order => 'position'
@projects = Project.find(:all)
end
def edit
@tracker ||= Tracker.find(params[:id])
@projects = Project.find(:all)
end
def update
@tracker = Tracker.find(params[:id])
if request.put? and @tracker.update_attributes(params[:tracker])
if request.post? and @tracker.update_attributes(params[:tracker])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'index'
return
end
edit
render :action => 'edit'
@projects = Project.find(:all)
end
verify :method => :delete, :only => :destroy, :redirect_to => { :action => :index }
def destroy
@tracker = Tracker.find(params[:id])
unless @tracker.issues.empty?
@@ -80,5 +60,5 @@ class TrackersController < ApplicationController
@tracker.destroy
end
redirect_to :action => 'index'
end
end
end

View File

@@ -1,46 +1,43 @@
# Redmine - project management software
# Copyright (C) 2006-2011 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
# 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 UsersController < ApplicationController
layout 'admin'
before_filter :require_admin, :except => :show
before_filter :find_user, :only => [:show, :edit, :update, :destroy, :edit_membership, :destroy_membership]
accept_api_auth :index, :show, :create, :update, :destroy
before_filter :find_user, :only => [:show, :edit, :update, :edit_membership, :destroy_membership]
accept_key_auth :index, :show, :create, :update
helper :sort
include SortHelper
helper :custom_fields
include CustomFieldsHelper
include CustomFieldsHelper
def index
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
scope = User
scope = scope.in_group(params[:group_id].to_i) if params[:group_id].present?
@status = params[:status] ? params[:status].to_i : 1
c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
@@ -48,39 +45,36 @@ class UsersController < ApplicationController
name = "%#{params[:name].strip.downcase}%"
c << ["LOWER(login) LIKE ? OR LOWER(firstname) LIKE ? OR LOWER(lastname) LIKE ? OR LOWER(mail) LIKE ?", name, name, name, name]
end
@user_count = scope.count(:conditions => c.conditions)
@user_count = User.count(:conditions => c.conditions)
@user_pages = Paginator.new self, @user_count, @limit, params['page']
@offset ||= @user_pages.current.offset
@users = scope.find :all,
@users = User.find :all,
:order => sort_clause,
:conditions => c.conditions,
:limit => @limit,
:offset => @offset
respond_to do |format|
format.html {
@groups = Group.all.sort
render :layout => !request.xhr?
}
respond_to do |format|
format.html { render :layout => !request.xhr? }
format.api
end
end
end
def show
# show projects based on current user visibility
@memberships = @user.memberships.all(:conditions => Project.visible_condition(User.current))
@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)
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
@@ -91,7 +85,7 @@ class UsersController < ApplicationController
@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)
@@ -109,12 +103,12 @@ class UsersController < ApplicationController
@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'} :
redirect_to(params[:continue] ?
{:controller => 'users', :action => 'new'} :
{:controller => 'users', :action => 'edit', :id => @user}
)
}
@@ -136,7 +130,7 @@ class UsersController < ApplicationController
@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]
@@ -160,7 +154,7 @@ class UsersController < ApplicationController
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)
@@ -183,15 +177,6 @@ class UsersController < ApplicationController
redirect_to :controller => 'users', :action => 'edit', :id => @user
end
verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
def destroy
@user.destroy
respond_to do |format|
format.html { redirect_to(users_url) }
format.api { head :ok }
end
end
def edit_membership
@membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
@membership.save if request.post?
@@ -213,7 +198,7 @@ class UsersController < ApplicationController
end
end
end
def destroy_membership
@membership = Member.find(params[:membership_id])
if request.post? && @membership.deletable?
@@ -224,9 +209,9 @@ 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

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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.
@@ -23,59 +23,45 @@ class VersionsController < ApplicationController
before_filter :find_project, :only => [:index, :new, :create, :close_completed]
before_filter :authorize
accept_api_auth :index, :show, :create, :update, :destroy
helper :custom_fields
helper :projects
def index
respond_to do |format|
format.html {
@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?}
}
format.api {
@versions = @project.shared_versions.all
}
@trackers = @project.trackers.find(:all, :order => 'position')
retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?})
@with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
@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
respond_to do |format|
format.html {
@issues = @version.fixed_issues.visible.find(:all,
:include => [:status, :tracker, :priority],
:order => "#{Tracker.table_name}.position, #{Issue.table_name}.id")
}
format.api
end
@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.safe_attributes = attributes
@version.attributes = attributes
end
end
@@ -85,7 +71,7 @@ class VersionsController < ApplicationController
if params[:version]
attributes = params[:version].dup
attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing'])
@version.safe_attributes = attributes
@version.attributes = attributes
end
if request.post?
@@ -93,7 +79,7 @@ class VersionsController < ApplicationController
respond_to do |format|
format.html do
flash[:notice] = l(:notice_successful_create)
redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
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
@@ -101,9 +87,6 @@ class VersionsController < ApplicationController
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
format.api do
render :action => 'show', :status => :created, :location => version_url(@version)
end
end
else
respond_to do |format|
@@ -111,7 +94,6 @@ class VersionsController < ApplicationController
format.js do
render(:update) {|page| page.alert(@version.errors.full_messages.join('\n')) }
end
format.api { render_validation_errors(@version) }
end
end
end
@@ -119,29 +101,22 @@ class VersionsController < ApplicationController
def edit
end
def update
if request.put? && params[:version]
attributes = params[:version].dup
attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing'])
@version.safe_attributes = attributes
if @version.save
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
}
format.api { head :ok }
end
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' }
format.api { render_validation_errors(@version) }
end
end
end
end
def close_completed
if request.put?
@project.close_completed_versions
@@ -149,25 +124,16 @@ class VersionsController < ApplicationController
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
end
verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
def destroy
if @version.fixed_issues.empty?
@version.destroy
respond_to do |format|
format.html { redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project }
format.api { head :ok }
end
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
else
respond_to do |format|
format.html {
flash[:error] = l(:notice_unable_delete_version)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
}
format.api { head :unprocessable_entity }
end
flash[:error] = l(:notice_unable_delete_version)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
end
end
def status_by
respond_to do |format|
format.html { render :action => 'show' }

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-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.
@@ -19,11 +19,11 @@ class WatchersController < ApplicationController
before_filter :find_project
before_filter :require_login, :check_project_privacy, :only => [:watch, :unwatch]
before_filter :authorize, :only => [:new, :destroy]
verify :method => :post,
:only => [ :watch, :unwatch ],
:render => { :nothing => true, :status => :method_not_allowed }
def watch
if @watched.respond_to?(:visible?) && !@watched.visible?(User.current)
render_403
@@ -31,11 +31,11 @@ class WatchersController < ApplicationController
set_watcher(User.current, true)
end
end
def unwatch
set_watcher(User.current, false)
end
def new
@watcher = Watcher.new(params[:watcher])
@watcher.watchable = @watched
@@ -51,7 +51,7 @@ class WatchersController < ApplicationController
rescue ::ActionController::RedirectBackError
render :text => 'Watcher added.', :layout => true
end
def destroy
@watched.set_watcher(User.find(params[:user_id]), false) if request.post?
respond_to do |format|
@@ -63,7 +63,7 @@ class WatchersController < ApplicationController
end
end
end
private
def find_project
klass = Object.const_get(params[:object_type].camelcase)
@@ -73,16 +73,24 @@ private
rescue
render_404
end
def set_watcher(user, watching)
@watched.set_watcher(user, watching)
if params[:replace].present?
if params[:replace].is_a? Array
replace_ids = params[:replace]
else
replace_ids = [params[:replace]]
end
else
replace_ids = ['watcher']
end
respond_to do |format|
format.html { redirect_to :back }
format.js do
render(:update) do |page|
c = watcher_css(@watched)
page.select(".#{c}").each do |item|
page.replace_html item, watcher_link(@watched, user)
replace_ids.each do |replace_id|
page.replace_html replace_id, watcher_link(@watched, user, :replace => replace_ids)
end
end
end

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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.
@@ -22,7 +22,7 @@ class WelcomeController < ApplicationController
@news = News.latest User.current
@projects = Project.latest User.current
end
def robots
@projects = Project.all_public.active
render :layout => false, :content_type => 'text/plain'

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-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.
@@ -34,28 +34,23 @@ require 'diff'
class WikiController < ApplicationController
default_search_scope :wiki_pages
before_filter :find_wiki, :authorize
before_filter :find_existing_or_new_page, :only => [:show, :edit, :update]
before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy]
verify :method => :post, :only => [:protect], :redirect_to => { :action => :show }
helper :attachments
include AttachmentsHelper
include AttachmentsHelper
helper :watchers
include Redmine::Export::PDF
# List of pages, sorted alphabetically and by parent (hierarchy)
def index
load_pages_for_index
@pages_by_parent_id = @pages.group_by(&:parent_id)
end
# List of page, by last update
def date_index
load_pages_for_index
@pages_by_date = @pages.group_by {|p| p.updated_on.to_date}
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) && editable?
edit
@@ -72,10 +67,7 @@ class WikiController < ApplicationController
end
@content = @page.content_for_version(params[:version])
if User.current.allowed_to?(:export_wiki_pages, @project)
if params[:format] == 'pdf'
send_data(wiki_to_pdf(@page, @project), :type => 'application/pdf', :filename => "#{@page.title}.pdf")
return
elsif params[:format] == 'html'
if params[:format] == 'html'
export = render_to_string :action => 'export', :layout => false
send_data(export, :type => 'text/html', :filename => "#{@page.title}.html")
return
@@ -85,18 +77,15 @@ class WikiController < ApplicationController
end
end
@editable = editable?
@sections_editable = @editable && User.current.allowed_to?(:edit_wiki_pages, @page.project) &&
@content.current_version? &&
Redmine::WikiFormatting.supports_section_edit?
render :action => 'show'
end
# edit an existing page or a new one
def edit
@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
@@ -104,21 +93,15 @@ class WikiController < ApplicationController
# To prevent StaleObjectError exception when reverting to a previous version
@content.version = @page.content.version
@text = @content.text
if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
@section = params[:section].to_i
@text, @section_hash = Redmine::WikiFormatting.formatter.new(@text).get_section(@section)
render_404 if @text.blank?
end
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
@@ -131,17 +114,7 @@ class WikiController < ApplicationController
redirect_to :action => 'show', :project_id => @project, :id => @page.title
return
end
@content.comments = params[:content][:comments]
@text = params[:content][:text]
if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
@section = params[:section].to_i
@section_hash = params[:section_hash]
@content.text = Redmine::WikiFormatting.formatter.new(@content.text).update_section(params[:section].to_i, @text, @section_hash)
else
@content.version = params[:content][:version]
@content.text = @text
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)
@@ -153,7 +126,7 @@ class WikiController < ApplicationController
render :action => 'edit'
end
rescue ActiveRecord::StaleObjectError, Redmine::WikiFormatting::StaleSectionError
rescue ActiveRecord::StaleObjectError
# Optimistic locking exception
flash.now[:error] = l(:notice_locking_conflict)
render :action => 'edit'
@@ -170,8 +143,7 @@ class WikiController < ApplicationController
redirect_to :action => 'show', :project_id => @project, :id => @page.title
end
end
verify :method => :post, :only => :protect, :redirect_to => { :action => :show }
def protect
@page.update_attribute :protected, params[:protected]
redirect_to :action => 'show', :project_id => @project, :id => @page.title
@@ -181,8 +153,8 @@ class WikiController < ApplicationController
def history
@version_count = @page.content.versions.count
@version_pages = Paginator.new self, @version_count, per_page_option, params['p']
# don't load text
@versions = @page.content.versions.find :all,
# don't load text
@versions = @page.content.versions.find :all,
:select => "id, author_id, comments, updated_on, version",
:order => 'version DESC',
:limit => @version_pages.items_per_page + 1,
@@ -190,12 +162,12 @@ class WikiController < ApplicationController
render :layout => false if request.xhr?
end
def diff
@diff = @page.diff(params[:version], params[:version_from])
render_404 unless @diff
end
def annotate
@annotate = @page.annotate(params[:version])
render_404 unless @annotate
@@ -206,7 +178,7 @@ class WikiController < ApplicationController
# Children can be either set as root pages, removed or reassigned to another parent page
def destroy
return render_403 unless editable?
@descendants_count = @page.descendants.size
if @descendants_count > 0
case params[:todo]
@@ -242,6 +214,10 @@ class WikiController < ApplicationController
end
end
def date_index
load_pages_grouped_by_date_without_content
end
def preview
page = @wiki.find_page(params[:id])
# page is nil when previewing a new page
@@ -262,7 +238,7 @@ class WikiController < ApplicationController
end
private
def find_wiki
@project = Project.find(params[:project_id])
@wiki = @project.wiki
@@ -270,27 +246,13 @@ private
rescue ActiveRecord::RecordNotFound
render_404
end
# Finds the requested page or a new page if it doesn't exist
def find_existing_or_new_page
@page = @wiki.find_or_new_page(params[:id])
if @wiki.page_found_with_redirect?
redirect_to params.update(:id => @page.title)
end
end
# Finds the requested page and returns a 404 error if it doesn't exist
def find_existing_page
@page = @wiki.find_page(params[:id])
if @page.nil?
render_404
return
end
if @wiki.page_found_with_redirect?
redirect_to params.update(:id => @page.title)
end
render_404 if @page.nil?
end
# Returns true if the current user is allowed to edit the page, otherwise false
def editable?(page = @page)
page.editable_by?(User.current)
@@ -303,7 +265,13 @@ private
helper.instance_method(:initial_page_content).bind(self).call(page)
end
def load_pages_for_index
@pages = @wiki.pages.with_updated_on.all(:order => 'title', :include => {:wiki => :project})
# 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

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-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.
@@ -18,11 +18,11 @@
class WikisController < ApplicationController
menu_item :settings
before_filter :find_project, :authorize
# Create or update a project's wiki
def edit
@wiki = @project.wiki || Wiki.new(:project => @project)
@wiki.safe_attributes = params[:wiki]
@wiki.attributes = params[:wiki]
@wiki.save if request.post?
render(:update) {|page| page.replace_html "tab-content-wiki", :partial => 'projects/settings/wiki'}
end
@@ -32,6 +32,6 @@ class WikisController < ApplicationController
if request.post? && params[:confirm] && @project.wiki
@project.wiki.destroy
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'wiki'
end
end
end
end

View File

@@ -1,68 +1,57 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2008 Jean-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 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
def edit
@role = Role.find_by_id(params[:role_id])
@tracker = Tracker.find_by_id(params[:tracker_id])
@tracker = Tracker.find_by_id(params[:tracker_id])
if request.post?
Workflow.destroy_all( ["role_id=? and tracker_id=?", @role.id, @tracker.id])
(params[:issue_status] || []).each { |status_id, transitions|
transitions.each { |new_status_id, options|
author = options.is_a?(Array) && options.include?('author') && !options.include?('always')
assignee = options.is_a?(Array) && options.include?('assignee') && !options.include?('always')
@role.workflows.build(:tracker_id => @tracker.id, :old_status_id => status_id, :new_status_id => new_status_id, :author => author, :assignee => assignee)
(params[:issue_status] || []).each { |old, news|
news.each { |new|
@role.workflows.build(:tracker_id => @tracker.id, :old_status_id => old, :new_status_id => new)
}
}
if @role.save
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'edit', :role_id => @role, :tracker_id => @tracker
return
end
end
@used_statuses_only = (params[:used_statuses_only] == '0' ? false : true)
if @tracker && @used_statuses_only && @tracker.issue_statuses.any?
@statuses = @tracker.issue_statuses
end
@statuses ||= IssueStatus.find(:all, :order => 'position')
if @tracker && @role && @statuses.any?
workflows = Workflow.all(:conditions => {:role_id => @role.id, :tracker_id => @tracker.id})
@workflows = {}
@workflows['always'] = workflows.select {|w| !w.author && !w.assignee}
@workflows['author'] = workflows.select {|w| w.author}
@workflows['assignee'] = workflows.select {|w| w.assignee}
end
end
def copy
if params[:source_tracker_id].blank? || params[:source_tracker_id] == 'any'
@source_tracker = nil
else
@@ -73,10 +62,10 @@ class WorkflowsController < ApplicationController
else
@source_role = Role.find_by_id(params[:source_role_id].to_i)
end
@target_trackers = params[:target_tracker_ids].blank? ? nil : Tracker.find_all_by_id(params[:target_tracker_ids])
@target_roles = params[:target_role_ids].blank? ? nil : Role.find_all_by_id(params[:target_role_ids])
if request.post?
if params[:source_tracker_id].blank? || params[:source_role_id].blank? || (@source_tracker.nil? && @source_role.nil?)
flash.now[:error] = l(:error_workflow_copy_source)
@@ -95,7 +84,7 @@ class WorkflowsController < ApplicationController
def find_roles
@roles = Role.find(:all, :order => 'builtin, position')
end
def find_trackers
@trackers = Tracker.find(:all, :order => 'position')
end

View File

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

View File

@@ -1,25 +1,23 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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 AdminHelper
def project_status_options_for_select(selected)
options_for_select([[l(:label_all), ''],
options_for_select([[l(:label_all), ''],
[l(:status_active), 1]], selected)
end
end

View File

@@ -1,7 +1,5 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 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
@@ -65,7 +63,7 @@ module ApplicationHelper
# Displays a link to +issue+ with its subject.
# Examples:
#
#
# link_to_issue(issue) # => Defect #6: This is the subject
# link_to_issue(issue, :truncate => 6) # => Defect #6: This i...
# link_to_issue(issue, :subject => false) # => Defect #6
@@ -82,7 +80,7 @@ module ApplicationHelper
subject = truncate(subject, :length => options[:truncate])
end
end
s = link_to "#{h(issue.tracker)} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue},
s = link_to "#{issue.tracker} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue},
:class => issue.css_classes,
:title => title
s << ": #{h subject}" if subject
@@ -97,10 +95,8 @@ module ApplicationHelper
def link_to_attachment(attachment, options={})
text = options.delete(:text) || attachment.filename
action = options.delete(:download) ? 'download' : 'show'
link_to(h(text),
{:controller => 'attachments', :action => action,
:id => attachment, :filename => attachment.filename },
options)
link_to(h(text), {:controller => 'attachments', :action => action, :id => attachment, :filename => attachment.filename }, options)
end
# Generates a link to a SCM revision
@@ -110,27 +106,13 @@ module ApplicationHelper
text = options.delete(:text) || format_revision(revision)
rev = revision.respond_to?(:identifier) ? revision.identifier : revision
link_to(h(text), {:controller => 'repositories', :action => 'revision', :id => project, :rev => rev},
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 message
def link_to_message(message, options={}, html_options = nil)
link_to(
h(truncate(message.subject, :length => 60)),
{ :controller => 'messages', :action => 'show',
:board_id => message.board_id,
:id => message.root,
:r => (message.parent_id && message.id),
:anchor => (message.parent_id ? "message-#{message.id}" : nil)
}.merge(options),
html_options
)
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
@@ -164,15 +146,15 @@ module ApplicationHelper
html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
link_to name, {}, html_options
end
def format_activity_title(text)
h(truncate_single_line(text, :length => 100))
end
def format_activity_day(date)
date == Date.today ? l(:label_today).titleize : format_date(date)
end
def format_activity_description(text)
h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')).gsub(/[\r\n]+/, "<br />")
end
@@ -184,38 +166,38 @@ module ApplicationHelper
h("#{version.project} - #{version}")
end
end
def due_date_distance_in_words(date)
if date
l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
end
end
def render_page_hierarchy(pages, node=nil, options={})
def render_page_hierarchy(pages, node=nil)
content = ''
if pages[node]
content << "<ul class=\"pages-hierarchy\">\n"
pages[node].each do |page|
content << "<li>"
content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title},
:title => (options[:timestamp] && page.updated_on ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
content << "\n" + render_page_hierarchy(pages, page.id, options) if pages[page.id]
: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"
end
content << "</ul>\n"
end
content.html_safe
content
end
# Renders flash messages
def render_flash_messages
s = ''
flash.each do |k,v|
s << content_tag('div', v, :class => "flash #{k}")
end
s.html_safe
s
end
# Renders tabs and their content
def render_tabs(tabs)
if tabs.any?
@@ -224,11 +206,11 @@ module ApplicationHelper
content_tag 'p', l(:label_no_data), :class => "nodata"
end
end
# Renders the project quick-jump box
def render_project_jump_box
return unless User.current.logged?
projects = User.current.memberships.collect(&:project).compact.uniq
# Retrieve them now to avoid a COUNT query
projects = User.current.projects.all
if projects.any?
s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
"<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
@@ -237,10 +219,10 @@ module ApplicationHelper
{ :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
end
s << '</select>'
s.html_safe
s
end
end
def project_tree_options_for_select(projects, options = {})
s = ''
project_tree(projects) do |project, level|
@@ -254,16 +236,16 @@ module ApplicationHelper
tag_options.merge!(yield(project)) if block_given?
s << content_tag('option', name_prefix + h(project), tag_options)
end
s.html_safe
s
end
# Yields the given block for each project with its level in the tree
#
# Wrapper for Project#project_tree
def project_tree(projects, &block)
Project.project_tree(projects, &block)
end
def project_nested_ul(projects, &block)
s = ''
if projects.any?
@@ -274,7 +256,7 @@ module ApplicationHelper
else
ancestors.pop
s << "</li>"
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
ancestors.pop
s << "</ul></li>\n"
end
@@ -285,36 +267,22 @@ module ApplicationHelper
end
s << ("</li></ul>\n" * ancestors.size)
end
s.html_safe
s
end
def principals_check_box_tags(name, principals)
s = ''
principals.sort.each do |principal|
s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
end
s.html_safe
end
# Returns a string for users/groups option tags
def principals_options_for_select(collection, selected=nil)
s = ''
groups = ''
collection.sort.each do |element|
selected_attribute = ' selected="selected"' if option_value_selected?(element, selected)
(element.is_a?(Group) ? groups : s) << %(<option value="#{element.id}"#{selected_attribute}>#{h element.name}</option>)
end
unless groups.empty?
s << %(<optgroup label="#{h(l(:label_group_plural))}">#{groups}</optgroup>)
end
s
s
end
# Truncates and returns the string as a single line
def truncate_single_line(string, *args)
truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
end
# Truncates at line break after 250 characters or options[:length]
def truncate_lines(string, options={})
length = options[:length] || 250
@@ -326,13 +294,13 @@ module ApplicationHelper
end
def html_hours(text)
text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>').html_safe
text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
end
def authoring(created, author, options={})
l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created)).html_safe
l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created))
end
def time_tag(time)
text = distance_of_time_in_words(Time.now, time)
if @project
@@ -354,24 +322,20 @@ module ApplicationHelper
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)
html = ''
if paginator.current.previous
# \xc2\xab(utf-8) = &#171;
html << link_to_content_update(
"\xc2\xab " + l(:label_previous),
url_param.merge(page_param => paginator.current.previous)) + ' '
html << link_to_remote_content_update('&#171; ' + l(:label_previous), url_param.merge(page_param => paginator.current.previous)) + ' '
end
html << (pagination_links_each(paginator, options) do |n|
link_to_content_update(n.to_s, url_param.merge(page_param => n))
link_to_remote_content_update(n.to_s, url_param.merge(page_param => n))
end || '')
if paginator.current.next
# \xc2\xbb(utf-8) = &#187;
html << ' ' + link_to_content_update(
(l(:label_next) + " \xc2\xbb"),
url_param.merge(page_param => paginator.current.next))
html << ' ' + link_to_remote_content_update((l(:label_next) + ' &#187;'), url_param.merge(page_param => paginator.current.next))
end
unless count.nil?
@@ -381,67 +345,66 @@ module ApplicationHelper
end
end
html.html_safe
html
end
def per_page_links(selected=nil)
url_param = params.dup
url_param.clear if url_param.has_key?(:set_filter)
links = Setting.per_page_options_array.collect do |n|
n == selected ? n : link_to_content_update(n, params.merge(:per_page => n))
n == selected ? n : link_to_remote(n, {:update => "content",
:url => params.dup.merge(:per_page => n),
:method => :get},
{:href => url_for(url_param.merge(:per_page => n))})
end
links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
end
def reorder_links(name, url, method = :post)
link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)),
url.merge({"#{name}[move_to]" => 'highest'}),
:method => method, :title => l(:label_sort_highest)) +
link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)),
url.merge({"#{name}[move_to]" => 'higher'}),
:method => method, :title => l(:label_sort_higher)) +
link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)),
url.merge({"#{name}[move_to]" => 'lower'}),
:method => method, :title => l(:label_sort_lower)) +
link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)),
url.merge({"#{name}[move_to]" => 'lowest'}),
:method => method, :title => l(:label_sort_lowest))
def reorder_links(name, url)
link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)), url.merge({"#{name}[move_to]" => 'highest'}), :method => :post, :title => l(:label_sort_highest)) +
link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)), url.merge({"#{name}[move_to]" => 'higher'}), :method => :post, :title => l(:label_sort_higher)) +
link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)), url.merge({"#{name}[move_to]" => 'lower'}), :method => :post, :title => l(:label_sort_lower)) +
link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)), url.merge({"#{name}[move_to]" => 'lowest'}), :method => :post, :title => l(:label_sort_lowest))
end
def breadcrumb(*args)
elements = args.flatten
elements.any? ? content_tag('p', (args.join(" \xc2\xbb ") + " \xc2\xbb ").html_safe, :class => 'breadcrumb') : nil
elements.any? ? content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb') : nil
end
def other_formats_links(&block)
concat('<p class="other-formats">'.html_safe + l(:label_export_to))
concat('<p class="other-formats">' + l(:label_export_to))
yield Redmine::Views::OtherFormatsBuilder.new(self)
concat('</p>'.html_safe)
concat('</p>')
end
def page_header_title
if @project.nil? || @project.new_record?
h(Setting.app_title)
else
b = []
ancestors = (@project.root? ? [] : @project.ancestors.visible.all)
ancestors = (@project.root? ? [] : @project.ancestors.visible)
if ancestors.any?
root = ancestors.shift
b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
if ancestors.size > 2
b << "\xe2\x80\xa6"
b << '&#8230;'
ancestors = ancestors[-2, 2]
end
b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') }
end
b << h(@project)
b.join(" \xc2\xbb ").html_safe
b.join(' &#187; ')
end
end
def html_title(*args)
if args.empty?
title = @html_title || []
title = []
title << @project.name if @project
title << Setting.app_title unless Setting.app_title == title.last
title += @html_title if @html_title
title << Setting.app_title
title.select {|t| !t.blank? }.join(' - ')
else
@html_title ||= []
@@ -487,27 +450,22 @@ module ApplicationHelper
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)
text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) { |macro, args| exec_macro(macro, obj, args) }
@parsed_headings = []
@heading_anchors = {}
@current_section = 0 if options[:edit_section_links]
parse_sections(text, project, obj, attr, only_path, options)
text = parse_non_pre_blocks(text) do |text|
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_macros].each do |method_name|
[: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
parse_headings(text, project, obj, attr, only_path, options)
if @parsed_headings.any?
replace_toc(text, @parsed_headings)
end
text
end
def parse_non_pre_blocks(text)
s = StringScanner.new(text)
tags = []
@@ -534,26 +492,26 @@ module ApplicationHelper
while tag = tags.pop
parsed << "</#{tag}>"
end
parsed.html_safe
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
if options[:attachments] || (obj && obj.respond_to?(:attachments))
attachments = options[:attachments] || obj.attachments
text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpe|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
filename, ext, alt, alttext = $1.downcase, $2, $3, $4
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 = Attachment.latest_attach(attachments, filename)
image_url = url_for :only_path => only_path, :controller => 'attachments',
:action => 'download', :id => found
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('"', '')
if !desc.blank? && alttext.blank?
alt = " title=\"#{desc}\" alt=\"#{desc}\""
end
"src=\"#{image_url}\"#{alt}".html_safe
"src=\"#{image_url}\"#{alt}"
else
m.html_safe
m
end
end
end
@@ -586,31 +544,26 @@ module ApplicationHelper
if page =~ /^(.+?)\#(.+)$/
page, anchor = $1, $2
end
anchor = sanitize_anchor_name(anchor) if anchor.present?
# check if page exists
wiki_page = link_project.wiki.find_page(page)
url = if anchor.present? && wiki_page.present? && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version)) && obj.page == wiki_page
"##{anchor}"
else
case options[:wiki_links]
when :local; "#{page.present? ? Wiki.titleize(page) : ''}.html" + (anchor.present? ? "##{anchor}" : '')
when :anchor; "##{page.present? ? Wiki.titleize(page) : title}" + (anchor.present? ? "_#{anchor}" : '') # used for single-file wiki export
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
end
link_to(title.present? ? title.html_safe : h(page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
link_to((title || page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
else
# project or wiki doesn't exist
all.html_safe
all
end
else
all.html_safe
all
end
end
end
# Redmine links
#
# Examples:
@@ -635,26 +588,16 @@ module ApplicationHelper
# 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:
# Forum messages:
# message#1218 -> Link to message with id 1218
#
# Links can refer other objects from other projects, using project identifier:
# identifier:r52
# identifier:document:"Some document"
# identifier:version:1.0.0
# identifier:source:some/file
def parse_redmine_links(text, project, obj, attr, only_path, options)
text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-]+):)?(attachment|document|version|forum|news|commit|source|export|message|project)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|\]|<|$)}) do |m|
leading, esc, project_prefix, project_identifier, prefix, sep, identifier = $1, $2, $3, $4, $5, $7 || $9, $8 || $10
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 project_identifier
project = Project.visible.find_by_identifier(project_identifier)
end
if esc.nil?
if prefix.nil? && sep == 'r'
# project.changesets.visible raises an SQL error because of a double join on repositories
if project && project.repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(project.repository.id, identifier))
link = link_to(h("#{project_prefix}r#{identifier}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
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
@@ -668,28 +611,24 @@ module ApplicationHelper
:title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
end
when 'document'
if document = Document.visible.find_by_id(oid)
if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
:class => 'document'
end
when 'version'
if version = Version.visible.find_by_id(oid)
if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
:class => 'version'
end
when 'message'
if message = Message.visible.find_by_id(oid, :include => :parent)
link = link_to_message(message, {:only_path => only_path}, :class => 'message')
end
when 'forum'
if board = Board.visible.find_by_id(oid)
link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
:class => 'board'
end
when 'news'
if news = News.visible.find_by_id(oid)
link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
:class => 'news'
if message = Message.find_by_id(oid, :include => [:parent, {:board => :project}], :conditions => Project.visible_by(User.current))
link = link_to h(truncate(message.subject, :length => 60)), {:only_path => only_path,
:controller => 'messages',
:action => 'show',
:board_id => message.board,
:id => message.root,
:anchor => (message.parent ? "message-#{message.id}" : nil)},
:class => 'message'
end
when 'project'
if p = Project.visible.find_by_id(oid)
@@ -701,36 +640,26 @@ module ApplicationHelper
name = identifier.gsub(%r{^"(.*)"$}, "\\1")
case prefix
when 'document'
if project && document = project.documents.visible.find_by_title(name)
if project && document = project.documents.find_by_title(name)
link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
:class => 'document'
end
when 'version'
if project && version = project.versions.visible.find_by_name(name)
if project && version = project.versions.find_by_name(name)
link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
:class => 'version'
end
when 'forum'
if project && board = project.boards.visible.find_by_name(name)
link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
:class => 'board'
end
when 'news'
if project && news = project.news.visible.find_by_title(name)
link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
:class => 'news'
end
when 'commit'
if project && project.repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", project.repository.id, "#{name}%"]))
link = link_to h("#{project_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.identifier},
if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
:class => 'changeset',
:title => truncate_single_line(h(changeset.comments), :length => 100)
:title => truncate_single_line(changeset.comments, :length => 100)
end
when 'source', 'export'
if project && project.repository && User.current.allowed_to?(:browse_repository, project)
if project && project.repository
name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
path, rev, anchor = $1, $3, $5
link = link_to h("#{project_prefix}#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
:path => to_path_param(path),
:rev => rev,
:anchor => anchor,
@@ -750,77 +679,28 @@ module ApplicationHelper
end
end
end
(leading + (link || "#{project_prefix}#{prefix}#{sep}#{identifier}")).html_safe
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)
def parse_sections(text, project, obj, attr, only_path, options)
return unless options[:edit_section_links]
text.gsub!(HEADING_RE) do
@current_section += 1
if @current_section > 1
content_tag('div',
link_to(image_tag('edit.png'), options[:edit_section_links].merge(:section => @current_section)),
:class => 'contextual',
:title => l(:button_edit_section)) + $1
else
$1
end
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 = $2.to_i, $3, $4
level, attrs, content = $1.to_i, $2, $3
item = strip_tags(content).strip
anchor = sanitize_anchor_name(item)
# used for single-file wiki export
anchor = "#{obj.page.title}_#{anchor}" if options[:wiki_links] == :anchor && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version))
@heading_anchors[anchor] ||= 0
idx = (@heading_anchors[anchor] += 1)
if idx > 1
anchor = "#{anchor}-#{idx}"
end
anchor = item.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
@parsed_headings << [level, anchor, item]
"<a name=\"#{anchor}\"></a>\n<h#{level} #{attrs}>#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a></h#{level}>"
"<h#{level} #{attrs} id=\"#{anchor}\">#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a></h#{level}>"
end
end
MACROS_RE = /
(!)? # escaping
(
\{\{ # opening tag
([\w]+) # macro name
(\(([^\}]*)\))? # optional arguments
\}\} # closing tag
)
/x unless const_defined?(:MACROS_RE)
# Macros substitution
def parse_macros(text, project, obj, attr, only_path, options)
text.gsub!(MACROS_RE) do
esc, all, macro = $1, $2, $3.downcase
args = ($5 || '').split(',').each(&:strip)
if esc.nil?
begin
exec_macro(macro, obj, args)
rescue => e
"<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
end || all
else
all
end
end
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
@@ -857,8 +737,7 @@ module ApplicationHelper
text.to_s.
gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
gsub(/([^\n]\n)(?=[^\n])/, '\1<br />'). # 1 newline -> br
html_safe
gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
end
def lang_options_for_select(blank=true)
@@ -871,20 +750,10 @@ module ApplicationHelper
content_tag("label", label_text)
end
def labelled_tabular_form_for(*args, &proc)
args << {} unless args.last.is_a?(Hash)
options = args.last
def labelled_tabular_form_for(name, object, options, &proc)
options[:html] ||= {}
options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
options.merge!({:builder => TabularFormBuilder})
form_for(*args, &proc)
end
def labelled_form_for(*args, &proc)
args << {} unless args.last.is_a?(Hash)
options = args.last
options.merge!({:builder => TabularFormBuilder})
form_for(*args, &proc)
form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
end
def back_url_hidden_field_tag
@@ -895,7 +764,7 @@ module ApplicationHelper
def check_all_links(form_name)
link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
" | ".html_safe +
" | " +
link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
end
@@ -908,19 +777,19 @@ module ApplicationHelper
legend = options[:legend] || ''
content_tag('table',
content_tag('tr',
(pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : ''.html_safe) +
(pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : ''.html_safe) +
(pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : ''.html_safe)
), :class => 'progress', :style => "width: #{width};").html_safe +
content_tag('p', legend, :class => 'pourcent').html_safe
(pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : '') +
(pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : '') +
(pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : '')
), :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
@@ -950,7 +819,7 @@ module ApplicationHelper
options[:class] << ' disabled'
url = '#'
end
link_to h(name), url, options
link_to name, url, options
end
def calendar_for(field_id)
@@ -968,15 +837,13 @@ module ApplicationHelper
'Calendar._FD = 1;' # Monday
when 7
'Calendar._FD = 0;' # Sunday
when 6
'Calendar._FD = 6;' # Saturday
else
'' # use language
end
javascript_include_tag('calendar/calendar') +
javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
javascript_tag(start_of_week) +
javascript_tag(start_of_week) +
javascript_include_tag('calendar/calendar-setup') +
stylesheet_link_tag('calendar')
end
@@ -993,10 +860,6 @@ module ApplicationHelper
(@has_content && @has_content[name]) || false
end
def email_delivery_enabled?
!!ActionMailer::Base.perform_deliveries
end
# Returns the avatar image tag for the given +user+ if avatars are enabled
# +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 = { })
@@ -1014,27 +877,10 @@ module ApplicationHelper
end
end
def sanitize_anchor_name(anchor)
anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
end
# Returns the javascript tags that are included in the html layout head
def javascript_heads
tags = javascript_include_tag(:defaults)
unless User.current.pref.warn_on_leaving_unsaved == '0'
tags << "\n".html_safe + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
end
tags
end
def favicon
"<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />".html_safe
"<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />"
end
def robot_exclusion_tag
'<meta name="robots" content="noindex,follow,noarchive" />'.html_safe
end
# Returns true if arg is expected in the API response
def include_in_api_response?(arg)
unless @included_in_api_response
@@ -1056,7 +902,7 @@ module ApplicationHelper
options
end
end
private
def wiki_helper
@@ -1064,8 +910,12 @@ module ApplicationHelper
extend helper
return self
end
def link_to_content_update(text, url_params = {}, html_options = {})
link_to(text, url_params, html_options)
def link_to_remote_content_update(text, url_params)
link_to_remote(text,
{:url => url_params, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'},
{:href => url_for(:params => url_params)}
)
end
end

View File

@@ -1,18 +1,16 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-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.
@@ -23,23 +21,14 @@ module AttachmentsHelper
# :author -- author names are not displayed if set to false
def link_to_attachments(container, options = {})
options.assert_valid_keys(:author)
if container.attachments.any?
options = {:deletable => container.attachments_deletable?, :author => true}.merge(options)
render :partial => 'attachments/links', :locals => {:attachments => container.attachments, :options => options}
end
end
def render_api_attachment(attachment, api)
api.attachment do
api.id attachment.id
api.filename attachment.filename
api.filesize attachment.filesize
api.content_type attachment.content_type
api.description attachment.description
api.content_url url_for(:controller => 'attachments', :action => 'download', :id => attachment, :filename => attachment.filename, :only_path => false)
api.author(:id => attachment.author.id, :name => attachment.author.name) if attachment.author
api.created_on attachment.created_on
end
def to_utf8(str)
str
end
end

View File

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

View File

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

View File

@@ -1,22 +1,3 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# 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 CalendarsHelper
def link_to_previous_month(year, month, options={})
target_year, target_month = if month == 1
@@ -24,15 +5,14 @@ module CalendarsHelper
else
[year, month - 1]
end
name = if target_month == 12
"#{month_name(target_month)} #{target_year}"
else
"#{month_name(target_month)}"
end
# \xc2\xab(utf-8) = &#171;
link_to_month(("\xc2\xab " + name), target_year, target_month, options)
link_to_month(('&#171; ' + name), target_year, target_month, options)
end
def link_to_next_month(year, month, options={})
@@ -48,11 +28,18 @@ module CalendarsHelper
"#{month_name(target_month)}"
end
# \xc2\xbb(utf-8) = &#187;
link_to_month((name + " \xc2\xbb"), target_year, target_month, options)
link_to_month((name + ' &#187;'), target_year, target_month, options)
end
def link_to_month(link_name, year, month, options={})
link_to_content_update(h(link_name), params.merge(:year => year, :month => month))
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

@@ -1,5 +1,3 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
@@ -7,12 +5,12 @@
# 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.
@@ -31,7 +29,7 @@ module CustomFieldsHelper
{:name => 'DocumentCategoryCustomField', :partial => 'custom_fields/index', :label => DocumentCategory::OptionName}
]
end
# Return custom field html tag corresponding to its format
def custom_field_tag(name, custom_value)
custom_field = custom_value.custom_field
@@ -41,7 +39,7 @@ module CustomFieldsHelper
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
case field_format.try(:edit_as)
when "date"
text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
calendar_for(field_id)
when "text"
text_area_tag(field_name, custom_value.value, :id => field_id, :rows => 3, :style => 'width:90%')
@@ -49,34 +47,34 @@ module CustomFieldsHelper
hidden_field_tag(field_name, '0') + check_box_tag(field_name, '1', custom_value.true?, :id => field_id)
when "list"
blank_option = custom_field.is_required? ?
(custom_field.default_value.blank? ? "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" : '') :
(custom_field.default_value.blank? ? "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" : '') :
'<option></option>'
select_tag(field_name, blank_option + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value), :id => field_id)
select_tag(field_name, blank_option + options_for_select(custom_field.possible_values, custom_value.value), :id => field_id)
else
text_field_tag(field_name, custom_value.value, :id => field_id)
end
end
# Return custom field label tag
def custom_field_label_tag(name, custom_value)
content_tag "label", h(custom_value.custom_field.name) +
(custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>".html_safe : ""),
content_tag "label", custom_value.custom_field.name +
(custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>" : ""),
:for => "#{name}_custom_field_values_#{custom_value.custom_field.id}",
:class => (custom_value.errors.empty? ? nil : "error" )
end
# Return custom field tag with its label tag
def custom_field_tag_with_label(name, custom_value)
custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
end
def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil)
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.try(:edit_as)
when "date"
text_field_tag(field_name, '', :id => field_id, :size => 10) +
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%')
@@ -85,7 +83,7 @@ module CustomFieldsHelper
[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_options(projects)), :id => field_id)
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
@@ -96,17 +94,17 @@ module CustomFieldsHelper
return "" unless custom_value
format_value(custom_value.value, custom_value.custom_field.field_format)
end
# Return a string used to display a custom value
def format_value(value, field_format)
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(custom_field)
Redmine::CustomFieldFormat.as_select(custom_field.class.customized_class.name)
def custom_field_formats_for_select
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

View File

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

View File

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

View File

@@ -1,18 +1,16 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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.
@@ -23,21 +21,29 @@ module GanttHelper
case in_or_out
when :in
if gantt.zoom < 4
link_to_content_update l(:text_zoom_in),
params.merge(gantt.params.merge(:zoom => (gantt.zoom+1))),
:class => 'icon icon-zoom-in'
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').html_safe
content_tag('span', l(:text_zoom_in), :class => 'icon icon-zoom-in')
end
when :out
if gantt.zoom > 1
link_to_content_update l(:text_zoom_out),
params.merge(gantt.params.merge(:zoom => (gantt.zoom-1))),
:class => 'icon icon-zoom-out'
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').html_safe
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

@@ -1,18 +1,16 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-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.
@@ -26,7 +24,7 @@ module GroupsHelper
end
options
end
def group_settings_tabs
tabs = [{:name => 'general', :partial => 'groups/general', :label => :label_general},
{:name => 'users', :partial => 'groups/users', :label => :label_user_plural},

View File

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

View File

@@ -1,4 +1,2 @@
# encoding: utf-8
#
module IssueMovesHelper
end

View File

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

View File

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

View File

@@ -1,18 +1,16 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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.
@@ -48,38 +46,28 @@ module IssuesHelper
@cached_label_priority ||= l(:field_priority)
@cached_label_project ||= l(:field_project)
(link_to_issue(issue) + "<br /><br />" +
link_to_issue(issue) + "<br /><br />" +
"<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />" +
"<strong>#{@cached_label_status}</strong>: #{h(issue.status.name)}<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>: #{h(issue.assigned_to)}<br />" +
"<strong>#{@cached_label_priority}</strong>: #{h(issue.priority.name)}").html_safe
"<strong>#{@cached_label_assigned_to}</strong>: #{issue.assigned_to}<br />" +
"<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
end
def issue_heading(issue)
h("#{issue.tracker} ##{issue.id}")
end
def render_issue_subject_with_tree(issue)
s = ''
ancestors = issue.root? ? [] : issue.ancestors.visible.all
ancestors.each do |ancestor|
issue.ancestors.each do |ancestor|
s << '<div>' + content_tag('p', link_to_issue(ancestor))
end
s << '<div>'
subject = h(issue.subject)
if issue.is_private?
subject = content_tag('span', l(:field_is_private), :class => 'private') + ' ' + subject
end
s << content_tag('h3', subject)
s << '</div>' * (ancestors.size + 1)
s.html_safe
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.visible.sort_by(&:lft)) do |child, level|
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') +
@@ -89,9 +77,9 @@ module IssuesHelper
:class => "issue issue-#{child.id} hascontextmenu #{level > 0 ? "idnt idnt-#{level}" : nil}")
end
s << '</form></table>'
s.html_safe
s
end
def render_custom_fields_rows(issue)
return if issue.custom_field_values.empty?
ordered_values = []
@@ -108,60 +96,23 @@ module IssuesHelper
n += 1
end
s << "</tr>\n"
s.html_safe
s
end
def issues_destroy_confirmation_message(issues)
issues = [issues] unless issues.is_a?(Array)
message = l(:text_issues_destroy_confirmation)
descendant_count = issues.inject(0) {|memo, i| memo += (i.right - i.left - 1)/2}
if descendant_count > 0
issues.each do |issue|
next if issue.root?
issues.each do |other_issue|
descendant_count -= 1 if issue.is_descendant_of?(other_issue)
end
end
if descendant_count > 0
message << "\n" + l(:text_issues_destroy_descendants_confirmation, :count => descendant_count)
end
end
message
end
def sidebar_queries
unless @sidebar_queries
# User can see public queries and his own queries
visible = ARCondition.new(["is_public = ? OR user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
# Project specific queries and global queries
visible << (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
@sidebar_queries = Query.find(:all,
:select => 'id, name, is_public',
@sidebar_queries = Query.find(:all,
:select => 'id, name',
:order => "name ASC",
:conditions => visible.conditions)
end
@sidebar_queries
end
def query_links(title, queries)
# links to #index on issues/show
url_params = controller_name == 'issues' ? {:controller => 'issues', :action => 'index', :project_id => @project} : params
content_tag('h3', h(title)) +
queries.collect {|query|
link_to(h(query.name), url_params.merge(:query_id => query))
}.join('<br />')
end
def render_sidebar_queries
out = ''
queries = sidebar_queries.select {|q| !q.is_public?}
out << query_links(l(:label_my_queries), queries) if queries.any?
queries = sidebar_queries.select {|q| q.is_public?}
out << query_links(l(:label_query_plural), queries) if queries.any?
out
end
def show_detail(detail, no_html=false)
case detail.property
when 'attr'
@@ -184,10 +135,6 @@ module IssuesHelper
label = l(:field_parent_issue)
value = "##{detail.value}" unless detail.value.blank?
old_value = "##{detail.old_value}" unless detail.old_value.blank?
when detail.prop_key == 'is_private'
value = l(detail.value == "0" ? :general_text_No : :general_text_Yes) unless detail.value.blank?
old_value = l(detail.old_value == "0" ? :general_text_No : :general_text_Yes) unless detail.old_value.blank?
end
when 'cf'
custom_field = CustomField.find_by_id(detail.prop_key)
@@ -204,11 +151,11 @@ module IssuesHelper
label ||= detail.prop_key
value ||= detail.value
old_value ||= detail.old_value
unless no_html
label = content_tag('strong', label)
old_value = content_tag("i", h(old_value)) if detail.old_value
old_value = content_tag("strike", old_value) if detail.old_value and detail.value.blank?
old_value = content_tag("strike", old_value) if detail.old_value and (!detail.value or detail.value.empty?)
if detail.property == 'attachment' && !value.blank? && a = Attachment.find_by_id(detail.prop_key)
# Link to the attachment if it has not been removed
value = link_to_attachment(a)
@@ -216,17 +163,8 @@ module IssuesHelper
value = content_tag("i", h(value)) if value
end
end
if detail.property == 'attr' && detail.prop_key == 'description'
s = l(:text_journal_changed_no_detail, :label => label)
unless no_html
diff_link = link_to 'diff',
{:controller => 'journals', :action => 'diff', :id => detail.journal_id, :detail_id => detail.id},
:title => l(:label_view_diff)
s << " (#{ diff_link })"
end
s
elsif !detail.value.blank?
if !detail.value.blank?
case detail.property
when 'attr', 'cf'
if !detail.old_value.blank?
@@ -250,7 +188,7 @@ module IssuesHelper
return record.name if record
end
end
# Renders issue children recursively
def render_api_issue_children(issue, api)
return if issue.leaf?
@@ -264,39 +202,60 @@ module IssuesHelper
end
end
end
def issues_to_csv(issues, project, query, options={})
def issues_to_csv(issues, project = nil)
ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
decimal_separator = l(:general_csv_decimal_separator)
encoding = l(:general_csv_encoding)
columns = (options[:columns] == 'all' ? query.available_columns : query.columns)
export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
# csv header fields
csv << [ "#" ] + columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) } +
(options[:description] ? [Redmine::CodesetUtil.from_utf8(l(:field_description), encoding)] : [])
headers = [ "#",
l(:field_status),
l(:field_project),
l(:field_tracker),
l(:field_priority),
l(:field_subject),
l(:field_assigned_to),
l(:field_category),
l(:field_fixed_version),
l(:field_author),
l(:field_start_date),
l(:field_due_date),
l(:field_done_ratio),
l(:field_estimated_hours),
l(:field_parent_issue),
l(:field_created_on),
l(:field_updated_on)
]
# Export project custom fields if project is given
# otherwise export custom fields marked as "For all projects"
custom_fields = project.nil? ? IssueCustomField.for_all : project.all_issue_custom_fields
custom_fields.each {|f| headers << f.name}
# Description in the last column
headers << l(:field_description)
csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
# csv lines
issues.each do |issue|
col_values = columns.collect do |column|
s = if column.is_a?(QueryCustomFieldColumn)
cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id}
show_value(cv)
else
value = issue.send(column.name)
if value.is_a?(Date)
format_date(value)
elsif value.is_a?(Time)
format_time(value)
elsif value.is_a?(Float)
value.to_s.gsub('.', decimal_separator)
else
value
end
end
s.to_s
end
csv << [ issue.id.to_s ] + col_values.collect {|c| Redmine::CodesetUtil.from_utf8(c.to_s, encoding) } +
(options[:description] ? [Redmine::CodesetUtil.from_utf8(issue.description, encoding)] : [])
fields = [issue.id,
issue.status.name,
issue.project.name,
issue.tracker.name,
issue.priority.name,
issue.subject,
issue.assigned_to,
issue.category,
issue.fixed_version,
issue.author.name,
format_date(issue.start_date),
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)
]
custom_fields.each {|f| fields << show_value(issue.custom_value_for(f)) }
fields << issue.description
csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
end
end
export

View File

@@ -1,18 +1,16 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2008 Jean-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.
@@ -26,7 +24,7 @@ module JournalsHelper
links << link_to_remote(image_tag('comment.png'),
{ :url => {:controller => 'journals', :action => 'new', :id => issue, :journal_id => journal} },
:title => l(:button_quote)) if options[:reply_links]
links << link_to_in_place_notes_editor(image_tag('edit.png'), "journal-#{journal.id}-notes",
links << link_to_in_place_notes_editor(image_tag('edit.png'), "journal-#{journal.id}-notes",
{ :controller => 'journals', :action => 'edit', :id => journal },
:title => l(:button_edit)) if editable
end
@@ -36,7 +34,7 @@ module JournalsHelper
css_classes << " editable" if editable
content_tag('div', content, :id => "journal-#{journal.id}-notes", :class => css_classes)
end
def link_to_in_place_notes_editor(text, field_id, url, options={})
onclick = "new Ajax.Request('#{url_for(url)}', {asynchronous:true, evalScripts:true, method:'get'}); return false;"
link_to text, '#', options.merge(:onclick => onclick)

View File

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

View File

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

View File

@@ -1,21 +1,29 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-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 MessagesHelper
def link_to_message(message)
return '' unless message
link_to h(truncate(message.subject, :length => 60)), :controller => 'messages',
: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

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

View File

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

View File

@@ -1,18 +1,16 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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.
@@ -22,7 +20,7 @@ module ProjectsHelper
return '' unless version && version.is_a?(Version)
link_to_if version.visible?, format_version_name(version), { :controller => 'versions', :action => 'show', :id => version }, options
end
def project_settings_tabs
tabs = [{:name => 'info', :action => :edit_project, :partial => 'projects/edit', :label => :label_information_plural},
{:name => 'modules', :action => :select_project_modules, :partial => 'projects/settings/modules', :label => :label_module_plural},
@@ -34,9 +32,9 @@ module ProjectsHelper
{:name => 'boards', :action => :manage_boards, :partial => 'projects/settings/boards', :label => :label_board_plural},
{:name => 'activities', :action => :manage_project_activities, :partial => 'projects/settings/activities', :label => :enumeration_activities}
]
tabs.select {|tab| User.current.allowed_to?(tab[:action], @project)}
tabs.select {|tab| User.current.allowed_to?(tab[:action], @project)}
end
def parent_project_select_tag(project)
selected = project.parent
# retrieve the requested parent project
@@ -44,13 +42,13 @@ module ProjectsHelper
if parent_id
selected = (parent_id.blank? ? nil : Project.find(parent_id))
end
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.html_safe, :name => 'project[parent_id]', :id => '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
# The given collection may be a subset of the whole project tree
# (eg. some intermediate nodes are private and can not be seen)
@@ -67,7 +65,7 @@ module ProjectsHelper
else
ancestors.pop
s << "</li>"
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
ancestors.pop
s << "</ul></li>\n"
end
@@ -82,7 +80,7 @@ module ProjectsHelper
s << ("</li></ul>\n" * ancestors.size)
@project = original_project
end
s.html_safe
s
end
# Returns a set of options for a select field, grouped by project.
@@ -95,7 +93,7 @@ module ProjectsHelper
if selected && !versions.include?(selected)
grouped[selected.project.name] << [selected.name, selected.id]
end
if grouped.keys.size > 1
grouped_options_for_select(grouped, selected && selected.id)
else

View File

@@ -1,37 +1,35 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-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 QueriesHelper
def operators_for_select(filter_type)
Query.operators_by_filter_type[filter_type].collect {|o| [l(Query.operators[o]), o]}
end
def column_header(column)
column.sortable ? sort_header_tag(column.name.to_s, :caption => column.caption,
:default_order => column.default_order) :
content_tag('th', h(column.caption))
:default_order => column.default_order) :
content_tag('th', column.caption)
end
def column_content(column, issue)
value = column.value(issue)
case value.class.name
when 'String'
if column.name == :subject
@@ -47,7 +45,7 @@ module QueriesHelper
if column.name == :done_ratio
progress_bar(value, :width => '80px')
else
h(value.to_s)
value.to_s
end
when 'User'
link_to_user value
@@ -72,34 +70,30 @@ module QueriesHelper
cond = "project_id IS NULL"
cond << " OR project_id = #{@project.id}" if @project
@query = Query.find(params[:query_id], :conditions => cond)
raise ::Unauthorized unless @query.visible?
@query.project = @project
session[:query] = {:id => @query.id, :project_id => @query.project_id}
sort_clear
elsif api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
# Give it a name, required to be valid
@query = Query.new(:name => "_")
@query.project = @project
build_query_from_params
session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
else
# retrieve from session
@query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
@query ||= Query.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
@query.project = @project
end
end
def build_query_from_params
if params[:fields] || params[:f]
@query.filters = {}
@query.add_filters(params[:fields] || params[:f], params[:operators] || params[:op], params[:values] || params[:v])
else
@query.available_filters.keys.each do |field|
@query.add_short_filter(field, params[field]) if params[field]
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
@query.group_by = params[:group_by] || (params[:query] && params[:query][:group_by])
@query.column_names = params[:c] || (params[:query] && params[:query][:column_names])
end
end

View File

@@ -1,24 +1,22 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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 ReportsHelper
def aggregate(data, criteria)
a = 0
data.each { |row|
@@ -30,9 +28,9 @@ module ReportsHelper
} unless data.nil?
a
end
def aggregate_link(data, criteria, *args)
a = aggregate data, criteria
a > 0 ? link_to(h(a), *args) : '-'
end
a > 0 ? link_to(a, *args) : '-'
end
end

View File

@@ -1,24 +1,21 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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.
require 'iconv'
require 'redmine/codeset_util'
module RepositoriesHelper
def format_revision(revision)
@@ -28,31 +25,30 @@ module RepositoriesHelper
revision.to_s
end
end
def truncate_at_line_break(text, length = 255)
if text
text.gsub(%r{^(.{#{length}}[^\n]*)\n.+$}m, '\\1...')
end
end
def render_properties(properties)
unless properties.nil? || properties.empty?
content = ''
properties.keys.sort.each do |property|
content << content_tag('li', "<b>#{h property}</b>: <span>#{h properties[property]}</span>".html_safe)
content << content_tag('li', "<b>#{h property}</b>: <span>#{h properties[property]}</span>")
end
content_tag('ul', content.html_safe, :class => 'properties')
content_tag('ul', content, :class => 'properties')
end
end
def render_changeset_changes
changes = @changeset.changes.find(:all, :limit => 1000, :order => 'path').collect do |change|
case change.action
when 'A'
# Detects moved/copied files
if !change.from_path.blank?
change.action =
@changeset.changes.detect {|c| c.action == 'D' && c.path == change.from_path} ? 'R' : 'C'
change.action = @changeset.changes.detect {|c| c.action == 'D' && c.path == change.from_path} ? 'R' : 'C'
end
change
when 'D'
@@ -60,8 +56,8 @@ module RepositoriesHelper
else
change
end
end.compact
end.compact
tree = { }
changes.each do |change|
p = tree
@@ -76,11 +72,13 @@ module RepositoriesHelper
end
p[:c] = change
end
render_changes_tree(tree[:s])
end
def render_changes_tree(tree)
return '' if tree.nil?
output = ''
output << '<ul>'
tree.keys.sort.each do |file|
@@ -89,7 +87,7 @@ module RepositoriesHelper
if s = tree[file][:s]
style << ' folder'
path_param = to_path_param(@repository.relative_path(file))
text = link_to(h(text), :controller => 'repositories',
text = link_to(text, :controller => 'repositories',
:action => 'show',
:id => @project,
:path => path_param,
@@ -99,215 +97,96 @@ module RepositoriesHelper
elsif c = tree[file][:c]
style << " change-#{c.action}"
path_param = to_path_param(@repository.relative_path(c.path))
text = link_to(h(text), :controller => 'repositories',
text = link_to(text, :controller => 'repositories',
:action => 'entry',
:id => @project,
:path => path_param,
:rev => @changeset.identifier) unless c.action == 'D'
text << " - #{h(c.revision)}" unless c.revision.blank?
text << ' ('.html_safe + link_to(l(:label_diff), :controller => 'repositories',
text << " - #{c.revision}" unless c.revision.blank?
text << ' (' + link_to('diff', :controller => 'repositories',
:action => 'diff',
:id => @project,
:path => path_param,
:rev => @changeset.identifier) + ') '.html_safe if c.action == 'M'
text << ' '.html_safe + content_tag('span', h(c.from_path), :class => 'copied-from') unless c.from_path.blank?
: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
end
output << '</ul>'
output.html_safe
output
end
def repository_field_tags(form, repository)
method = repository.class.name.demodulize.underscore + "_field_tags"
if repository.is_a?(Repository) &&
respond_to?(method) && method != 'repository_field_tags'
send(method, form, repository)
def to_utf8(str)
return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
@encodings ||= Setting.repositories_encodings.split(',').collect(&:strip)
@encodings.each do |encoding|
begin
return Iconv.conv('UTF-8', encoding, str)
rescue Iconv::Failure
# do nothing here and try the next encoding
end
end
str
end
def repository_field_tags(form, repository)
method = repository.class.name.demodulize.underscore + "_field_tags"
send(method, form, repository) if repository.is_a?(Repository) && respond_to?(method) && method != 'repository_field_tags'
end
def scm_select_tag(repository)
scm_options = [["--- #{l(:actionview_instancetag_blank_option)} ---", '']]
Redmine::Scm::Base.all.each do |scm|
if Setting.enabled_scm.include?(scm) ||
(repository && repository.class.name.demodulize == scm)
scm_options << ["Repository::#{scm}".constantize.scm_name, scm]
end
scm_options << ["Repository::#{scm}".constantize.scm_name, scm] if Setting.enabled_scm.include?(scm) || (repository && repository.class.name.demodulize == scm)
end
select_tag('repository_scm',
select_tag('repository_scm',
options_for_select(scm_options, repository.class.name.demodulize),
:disabled => (repository && !repository.new_record?),
:onchange => remote_function(
:url => {
:controller => 'repositories',
:action => 'edit',
:id => @project
},
:method => :get,
:with => "Form.serialize(this.form)")
)
:onchange => remote_function(:url => { :controller => 'repositories', :action => 'edit', :id => @project }, :method => :get, :with => "Form.serialize(this.form)")
)
end
def with_leading_slash(path)
path.to_s.starts_with?('/') ? path : "/#{path}"
end
def without_leading_slash(path)
path.gsub(%r{^/+}, '')
end
def subversion_field_tags(form, repository)
content_tag('p', form.text_field(:url, :size => 60, :required => true,
:disabled => (repository && !repository.root_url.blank?)) +
'<br />'.html_safe +
'(file:///, http://, https://, svn://, svn+[tunnelscheme]://)') +
content_tag('p', form.text_field(:url, :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)) +
'<br />(file:///, http://, https://, svn://, svn+[tunnelscheme]://)') +
content_tag('p', form.text_field(:login, :size => 30)) +
content_tag('p', form.password_field(
:password, :size => 30, :name => 'ignore',
:value => ((repository.new_record? || repository.password.blank?) ? '' : ('x'*15)),
:onfocus => "this.value=''; this.name='repository[password]';",
:onchange => "this.name='repository[password]';"))
content_tag('p', form.password_field(:password, :size => 30, :name => 'ignore',
:value => ((repository.new_record? || repository.password.blank?) ? '' : ('x'*15)),
:onfocus => "this.value=''; this.name='repository[password]';",
:onchange => "this.name='repository[password]';"))
end
def darcs_field_tags(form, repository)
content_tag('p', form.text_field(
:url, :label => l(:field_path_to_repository),
:size => 60, :required => true,
:disabled => (repository && !repository.new_record?))) +
content_tag('p', form.select(
:log_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_commit_logs_encoding), :required => true))
content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.new_record?)))
end
def mercurial_field_tags(form, repository)
content_tag('p', form.text_field(
:url, :label => l(:field_path_to_repository),
:size => 60, :required => true,
:disabled => (repository && !repository.root_url.blank?)
) +
'<br />'.html_safe + l(:text_mercurial_repository_note)) +
content_tag('p', form.select(
:path_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_scm_path_encoding)
) +
'<br />'.html_safe + l(:text_scm_path_encoding_note))
content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
end
def git_field_tags(form, repository)
content_tag('p', form.text_field(
:url, :label => l(:field_path_to_repository),
:size => 60, :required => true,
:disabled => (repository && !repository.root_url.blank?)
) +
'<br />'.html_safe +
l(:text_git_repository_note)) +
content_tag('p', form.select(
:path_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_scm_path_encoding)
) +
'<br />'.html_safe + l(:text_scm_path_encoding_note)) +
content_tag('p', form.check_box(
:extra_report_last_commit,
:label => l(:label_git_report_last_commit)
))
content_tag('p', form.text_field(:url, :label => 'Path to .git directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
end
def cvs_field_tags(form, repository)
content_tag('p', form.text_field(
:root_url,
:label => l(:field_cvsroot),
:size => 60, :required => true,
:disabled => !repository.new_record?)) +
content_tag('p', form.text_field(
:url,
:label => l(:field_cvs_module),
:size => 30, :required => true,
:disabled => !repository.new_record?)) +
content_tag('p', form.select(
:log_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_commit_logs_encoding), :required => true)) +
content_tag('p', form.select(
:path_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_scm_path_encoding)
) +
'<br />'.html_safe + l(:text_scm_path_encoding_note))
content_tag('p', form.text_field(:root_url, :label => 'CVSROOT', :size => 60, :required => true, :disabled => !repository.new_record?)) +
content_tag('p', form.text_field(:url, :label => 'Module', :size => 30, :required => true, :disabled => !repository.new_record?))
end
def bazaar_field_tags(form, repository)
content_tag('p', form.text_field(
:url, :label => l(:field_path_to_repository),
:size => 60, :required => true,
:disabled => (repository && !repository.new_record?))) +
content_tag('p', form.select(
:log_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_commit_logs_encoding), :required => true))
content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.new_record?)))
end
def filesystem_field_tags(form, repository)
content_tag('p', form.text_field(
:url, :label => l(:field_root_directory),
:size => 60, :required => true,
:disabled => (repository && !repository.root_url.blank?))) +
content_tag('p', form.select(
:path_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_scm_path_encoding)
) +
'<br />'.html_safe + l(:text_scm_path_encoding_note))
end
def index_commits(commits, heads, href_proc = nil)
return nil if commits.nil? or commits.first.parents.nil?
map = {}
commit_hashes = []
refs_map = {}
href_proc ||= Proc.new {|x|x}
heads.each{|r| refs_map[r.scmid] ||= []; refs_map[r.scmid] << r}
commits.reverse.each_with_index do |c, i|
h = {}
h[:parents] = c.parents.collect do |p|
[p.scmid, 0, 0]
end
h[:rdmid] = i
h[:space] = 0
h[:refs] = refs_map[c.scmid].join(" ") if refs_map.include? c.scmid
h[:scmid] = c.scmid
h[:href] = href_proc.call(c.scmid)
commit_hashes << h
map[c.scmid] = h
end
heads.sort! do |a,b|
a.to_s <=> b.to_s
end
j = 0
heads.each do |h|
if map.include? h.scmid then
j = mark_chain(j += 1, map[h.scmid], map)
end
end
# when no head matched anything use first commit
if j == 0 then
mark_chain(j += 1, map.values.first, map)
end
map
end
def mark_chain(mark, commit, map)
stack = [[mark, commit]]
markmax = mark
until stack.empty?
current = stack.pop
m, commit = current
commit[:space] = m if commit[:space] == 0
m1 = m - 1
commit[:parents].each_with_index do |p, i|
psha = p[0]
if map.include? psha and map[psha][:space] == 0 then
stack << [m1 += 1, map[psha]] if i == 0
stack = [[m1 += 1, map[psha]]] + stack if i > 0
end
end
markmax = m1 if markmax < m1
end
markmax
content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
end
end

View File

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

View File

@@ -1,18 +1,16 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-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.
@@ -21,7 +19,7 @@ module SearchHelper
def highlight_tokens(text, tokens)
return text unless text && tokens && !tokens.empty?
re_tokens = tokens.collect {|t| Regexp.escape(t)}
regexp = Regexp.new "(#{re_tokens.join('|')})", Regexp::IGNORECASE
regexp = Regexp.new "(#{re_tokens.join('|')})", Regexp::IGNORECASE
result = ''
text.split(regexp).each_with_index do |words, i|
if result.length > 1200
@@ -39,20 +37,19 @@ module SearchHelper
end
result
end
def type_label(t)
l("label_#{t.singularize}_plural", :default => t.to_s.humanize)
end
def project_select_tag
options = [[l(:label_project_all), 'all']]
options << [l(:label_my_projects), 'my_projects'] unless User.current.memberships.empty?
options << [l(:label_and_its_subprojects, @project.name), 'subprojects'] unless @project.nil? || @project.descendants.active.empty?
options << [@project.name, ''] unless @project.nil?
label_tag("scope", l(:description_project_scope), :class => "hidden-for-sighted") +
select_tag('scope', options_for_select(options, params[:scope].to_s)) if options.size > 1
end
def render_results_by_type(results_by_type)
links = []
# Sorts types by results count
@@ -60,8 +57,7 @@ module SearchHelper
c = results_by_type[t]
next if c == 0
text = "#{type_label(t)} (#{c})"
links << link_to(h(text), :q => params[:q], :titles_only => params[:titles_only],
:all_words => params[:all_words], :scope => params[:scope], t => 1)
links << link_to(text, :q => params[:q], :titles_only => params[:title_only], :all_words => params[:all_words], :scope => params[:scope], t => 1)
end
('<ul>' + links.map {|link| content_tag('li', link)}.join(' ') + '</ul>') unless links.empty?
end

View File

@@ -1,18 +1,16 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-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.
@@ -29,56 +27,49 @@ module SettingsHelper
{:name => 'repositories', :partial => 'settings/repositories', :label => :label_repository_plural}
]
end
def setting_select(setting, choices, options={})
if blank_text = options.delete(:blank)
choices = [[blank_text.is_a?(Symbol) ? l(blank_text) : blank_text, '']] + choices
end
setting_label(setting, options).html_safe +
select_tag("settings[#{setting}]",
options_for_select(choices, Setting.send(setting).to_s),
options).html_safe
setting_label(setting, options) +
select_tag("settings[#{setting}]", options_for_select(choices, Setting.send(setting).to_s), options)
end
def setting_multiselect(setting, choices, options={})
setting_values = Setting.send(setting)
setting_values = [] unless setting_values.is_a?(Array)
setting_label(setting, options).html_safe +
hidden_field_tag("settings[#{setting}][]", '').html_safe +
setting_label(setting, options) +
hidden_field_tag("settings[#{setting}][]", '') +
choices.collect do |choice|
text, value = (choice.is_a?(Array) ? choice : [choice, choice])
content_tag(
'label',
check_box_tag(
"settings[#{setting}][]",
value,
Setting.send(setting).include?(value)
) + text.to_s,
text, value = (choice.is_a?(Array) ? choice : [choice, choice])
content_tag('label',
check_box_tag("settings[#{setting}][]", value, Setting.send(setting).include?(value)) + text.to_s,
:class => 'block'
)
end.join.html_safe
)
end.join
end
def setting_text_field(setting, options={})
setting_label(setting, options).html_safe +
text_field_tag("settings[#{setting}]", Setting.send(setting), options).html_safe
setting_label(setting, options) +
text_field_tag("settings[#{setting}]", Setting.send(setting), options)
end
def setting_text_area(setting, options={})
setting_label(setting, options).html_safe +
text_area_tag("settings[#{setting}]", Setting.send(setting), options).html_safe
setting_label(setting, options) +
text_area_tag("settings[#{setting}]", Setting.send(setting), options)
end
def setting_check_box(setting, options={})
setting_label(setting, options).html_safe +
hidden_field_tag("settings[#{setting}]", 0).html_safe +
check_box_tag("settings[#{setting}]", 1, Setting.send("#{setting}?"), options).html_safe
setting_label(setting, options) +
hidden_field_tag("settings[#{setting}]", 0) +
check_box_tag("settings[#{setting}]", 1, Setting.send("#{setting}?"), options)
end
def setting_label(setting, options={})
label = options.delete(:label)
label != false ? content_tag("label", l(label || "setting_#{setting}")).html_safe : ''
label != false ? content_tag("label", l(label || "setting_#{setting}")) : ''
end
# Renders a notification field for a Redmine::Notifiable option
@@ -86,8 +77,8 @@ module SettingsHelper
return content_tag(:label,
check_box_tag('settings[notified_events][]',
notifiable.name,
Setting.notified_events.include?(notifiable.name)).html_safe +
l_or_humanize(notifiable.name, :prefix => 'label_').html_safe,
:class => notifiable.parent.present? ? "parent" : '').html_safe
Setting.notified_events.include?(notifiable.name)) +
l_or_humanize(notifiable.name, :prefix => 'label_'),
:class => notifiable.parent.present? ? "parent" : '')
end
end

View File

@@ -1,5 +1,3 @@
# encoding: utf-8
#
# Helpers to sort tables using clickable column headers.
#
# Author: Stuart Rackham <srackham@methods.co.nz>, March 2005.
@@ -17,18 +15,18 @@
#
# helper :sort
# include SortHelper
#
#
# def list
# sort_init 'last_name'
# sort_update %w(first_name last_name)
# @items = Contact.find_all nil, sort_clause
# end
#
#
# Controller (using Pagination module):
#
# helper :sort
# include SortHelper
#
#
# def list
# sort_init 'last_name'
# sort_update %w(first_name last_name)
@@ -36,9 +34,9 @@
# :order_by => sort_clause,
# :per_page => 10
# end
#
#
# View (table header in list.rhtml):
#
#
# <thead>
# <tr>
# <%= sort_header_tag('id', :title => 'Sort by contact ID') %>
@@ -54,32 +52,32 @@
module SortHelper
class SortCriteria
def initialize
@criteria = []
end
def available_criteria=(criteria)
unless criteria.is_a?(Hash)
criteria = criteria.inject({}) {|h,k| h[k] = k; h}
end
@available_criteria = criteria
end
def from_param(param)
@criteria = param.to_s.split(',').collect {|s| s.split(':')[0..1]}
normalize!
end
def criteria=(arg)
@criteria = arg
normalize!
end
def to_param
@criteria.collect {|k,o| k + (o ? '' : ':desc')}.join(',')
end
def to_sql
sql = @criteria.collect do |k,o|
if s = @available_criteria[k]
@@ -88,33 +86,33 @@ module SortHelper
end.compact.join(', ')
sql.blank? ? nil : sql
end
def add!(key, asc)
@criteria.delete_if {|k,o| k == key}
@criteria = [[key, asc]] + @criteria
normalize!
end
def add(*args)
r = self.class.new.from_param(to_param)
r.add!(*args)
r
end
def first_key
@criteria.first && @criteria.first.first
end
def first_asc?
@criteria.first && @criteria.first.last
end
def empty?
@criteria.empty?
end
private
def normalize!
@criteria ||= []
@criteria = @criteria.collect {|s| s = s.to_a; [s.first, (s.last == false || s.last == 'desc') ? false : true]}
@@ -122,7 +120,7 @@ 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
@@ -132,14 +130,14 @@ module SortHelper
end
end
end
def sort_name
controller_name + '_' + action_name + '_sort'
end
# Initializes the default sort.
# Examples:
#
#
# sort_init 'name'
# sort_init 'id', 'desc'
# sort_init ['name', ['id', 'desc']]
@@ -167,7 +165,7 @@ module SortHelper
@sort_criteria.criteria = @sort_default if @sort_criteria.empty?
session[sort_name] = @sort_criteria.to_param
end
# Clears the sort criteria session data
#
def sort_clear
@@ -189,7 +187,7 @@ module SortHelper
#
def sort_link(column, caption, default_order)
css, order = nil, default_order
if column.to_s == @sort_criteria.first_key
if @sort_criteria.first_asc?
css = 'sort asc'
@@ -200,14 +198,18 @@ module SortHelper
end
end
caption = column.to_s.humanize unless caption
sort_options = { :sort => @sort_criteria.add(column.to_s, order).to_param }
url_options = params.merge(sort_options)
# don't reuse params if filters are present
url_options = params.has_key?(:set_filter) ? sort_options : params.merge(sort_options)
# Add project_id to url_options
url_options = url_options.merge(:project_id => params[:project_id]) if params.has_key?(:project_id)
link_to_content_update(h(caption), url_options, :class => css)
link_to_remote(caption,
{:update => "content", :url => url_options, :method => :get},
{:href => url_for(url_options),
:class => css})
end
# Returns a table header <th> tag with a sort link for the named column

View File

@@ -1,25 +1,23 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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 TimelogHelper
include ApplicationHelper
def render_timelog_breadcrumb
links = []
links << link_to(l(:label_project_all), {:project_id => nil, :issue_id => nil})
@@ -54,15 +52,15 @@ module TimelogHelper
activities.each { |a| collection << [a.name, a.id] }
collection
end
def select_hours(data, criteria, value)
if value.to_s.empty?
data.select {|row| row[criteria].blank? }
else
else
data.select {|row| row[criteria].to_s == value.to_s}
end
end
def sum_hours(data)
sum = 0
data.each do |row|
@@ -70,7 +68,7 @@ module TimelogHelper
end
sum
end
def options_for_period_select(value)
options_for_select([[l(:label_all_time), 'all'],
[l(:label_today), 'today'],
@@ -84,8 +82,9 @@ module TimelogHelper
[l(:label_this_year), 'current_year']],
value)
end
def entries_to_csv(entries)
ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
decimal_separator = l(:general_csv_decimal_separator)
custom_fields = TimeEntryCustomField.find(:all)
export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
@@ -102,10 +101,8 @@ module TimelogHelper
]
# Export custom fields
headers += custom_fields.collect(&:name)
csv << headers.collect {|c| Redmine::CodesetUtil.from_utf8(
c.to_s,
l(:general_csv_encoding) ) }
csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
# csv lines
entries.each do |entry|
fields = [format_date(entry.spent_on),
@@ -119,15 +116,13 @@ module TimelogHelper
entry.comments
]
fields += custom_fields.collect {|f| show_value(entry.custom_value_for(f)) }
csv << fields.collect {|c| Redmine::CodesetUtil.from_utf8(
c.to_s,
l(:general_csv_encoding) ) }
csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
end
end
export
end
def format_criteria_value(criteria, value)
if value.blank?
l(:label_none)
@@ -142,55 +137,54 @@ module TimelogHelper
format_value(value, @available_criterias[criteria][:format])
end
end
def report_to_csv(criterias, periods, hours)
decimal_separator = l(:general_csv_decimal_separator)
export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
# Column headers
headers = criterias.collect {|criteria| l(@available_criterias[criteria][:label]) }
headers += periods
headers << l(:label_total)
csv << headers.collect {|c| Redmine::CodesetUtil.from_utf8(
c.to_s,
l(:general_csv_encoding) ) }
csv << headers.collect {|c| to_utf8(c) }
# Content
report_criteria_to_csv(csv, criterias, periods, hours)
# Total row
str_total = Redmine::CodesetUtil.from_utf8(l(:label_total), l(:general_csv_encoding))
row = [ str_total ] + [''] * (criterias.size - 1)
row = [ l(:label_total) ] + [''] * (criterias.size - 1)
total = 0
periods.each do |period|
sum = sum_hours(select_hours(hours, @columns, period.to_s))
total += sum
row << (sum > 0 ? ("%.2f" % sum).gsub('.',decimal_separator) : '')
row << (sum > 0 ? "%.2f" % sum : '')
end
row << ("%.2f" % total).gsub('.',decimal_separator)
row << "%.2f" %total
csv << row
end
export
end
def report_criteria_to_csv(csv, criterias, periods, hours, level=0)
decimal_separator = l(:general_csv_decimal_separator)
hours.collect {|h| h[criterias[level]].to_s}.uniq.each do |value|
hours_for_value = select_hours(hours, criterias[level], value)
next if hours_for_value.empty?
row = [''] * level
row << Redmine::CodesetUtil.from_utf8(
format_criteria_value(criterias[level], value).to_s,
l(:general_csv_encoding) )
row << to_utf8(format_criteria_value(criterias[level], value))
row += [''] * (criterias.length - level - 1)
total = 0
periods.each do |period|
sum = sum_hours(select_hours(hours_for_value, @columns, period.to_s))
total += sum
row << (sum > 0 ? ("%.2f" % sum).gsub('.',decimal_separator) : '')
row << (sum > 0 ? "%.2f" % sum : '')
end
row << ("%.2f" % total).gsub('.',decimal_separator)
row << "%.2f" %total
csv << row
if criterias.length > level + 1
report_criteria_to_csv(csv, criterias, periods, hours_for_value, level + 1)
end
end
end
def to_utf8(s)
@ic ||= Iconv.new(l(:general_csv_encoding), 'UTF-8')
begin; @ic.iconv(s.to_s); rescue; s.to_s; end
end
end

View File

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

View File

@@ -1,18 +1,16 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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.
@@ -20,12 +18,12 @@
module UsersHelper
def users_status_options_for_select(selected)
user_count_by_status = User.count(:group => 'status').to_hash
options_for_select([[l(:label_all), ''],
options_for_select([[l(:label_all), ''],
["#{l(:status_active)} (#{user_count_by_status[1].to_i})", 1],
["#{l(:status_registered)} (#{user_count_by_status[2].to_i})", 2],
["#{l(:status_locked)} (#{user_count_by_status[3].to_i})", 3]], selected)
end
# Options for the new membership projects combo-box
def options_for_membership_project_select(user, projects)
options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---")
@@ -34,14 +32,14 @@ module UsersHelper
end
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 => '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 => :put, :class => 'icon icon-unlock'
elsif user.registered?
@@ -50,7 +48,7 @@ module UsersHelper
link_to l(:button_lock), url.merge(:user => {:status => User::STATUS_LOCKED}), :method => :put, :class => 'icon icon-lock'
end
end
def user_settings_tabs
tabs = [{:name => 'general', :partial => 'users/general', :label => :label_general},
{:name => 'memberships', :partial => 'users/memberships', :label => :label_project_plural}

View File

@@ -1,29 +1,28 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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 VersionsHelper
STATUS_BY_CRITERIAS = %w(category tracker status priority author assigned_to)
STATUS_BY_CRITERIAS = %w(category tracker priority author assigned_to)
def render_issue_status_by(version, criteria)
criteria = 'category' unless STATUS_BY_CRITERIAS.include?(criteria)
criteria ||= 'category'
raise 'Unknown criteria' unless STATUS_BY_CRITERIAS.include?(criteria)
h = Hash.new {|k,v| k[v] = [0, 0]}
begin
# Total issue count
@@ -38,10 +37,10 @@ module VersionsHelper
end
counts = h.keys.compact.sort.collect {|k| {:group => k, :total => h[k][0], :open => h[k][1], :closed => (h[k][0] - h[k][1])}}
max = counts.collect {|c| c[:total]}.max
render :partial => 'issue_counts', :locals => {:version => version, :criteria => criteria, :counts => counts, :max => max}
end
def status_by_options_for_select(value)
options_for_select(STATUS_BY_CRITERIAS.collect {|criteria| [l("field_#{criteria}".to_sym), criteria]}, value)
end

View File

@@ -1,47 +1,50 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-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 WatchersHelper
def watcher_tag(object, user, options={})
content_tag("span", watcher_link(object, user), :class => watcher_css(object))
# Valid options
# * :id - the element id
# * :replace - a string or array of element ids that will be
# replaced
def watcher_tag(object, user, options={:replace => 'watcher'})
id = options[:id]
id ||= options[:replace] if options[:replace].is_a? String
content_tag("span", watcher_link(object, user, options), :id => id)
end
def watcher_link(object, user)
# Valid options
# * :replace - a string or array of element ids that will be
# replaced
def watcher_link(object, user, options={:replace => 'watcher'})
return '' unless user && user.logged? && object.respond_to?('watched_by?')
watched = object.watched_by?(user)
url = {:controller => 'watchers',
:action => (watched ? 'unwatch' : 'watch'),
:object_type => object.class.to_s.underscore,
:object_id => object.id}
:object_id => object.id,
:replace => options[:replace]}
link_to_remote((watched ? l(:button_unwatch) : l(:button_watch)),
{:url => url},
:href => url_for(url),
:class => (watched ? 'icon icon-fav' : 'icon icon-fav-off'))
end
# Returns the css class used to identify watch links for a given +object+
def watcher_css(object)
"#{object.class.to_s.underscore}-#{object.id}-watcher"
end
# 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)

View File

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

View File

@@ -1,43 +1,69 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-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 WikiHelper
def wiki_page_options_for_select(pages, selected = nil, parent = nil, level = 0)
pages = pages.group_by(&:parent) unless pages.is_a?(Hash)
s = ''
if pages.has_key?(parent)
pages[parent].each do |page|
attrs = "value='#{page.id}'"
attrs << " selected='selected'" if selected == page
indent = (level > 0) ? ('&nbsp;' * level * 2 + '&#187; ') : nil
s << "<option #{attrs}>#{indent}#{h page.pretty_title}</option>\n" +
wiki_page_options_for_select(pages, selected, page, level + 1)
end
pages.select {|p| p.parent == parent}.each do |page|
attrs = "value='#{page.id}'"
attrs << " selected='selected'" if selected == page
indent = (level > 0) ? ('&nbsp;' * level * 2 + '&#187; ') : nil
s << "<option #{attrs}>#{indent}#{h page.pretty_title}</option>\n" +
wiki_page_options_for_select(pages, selected, page, level + 1)
end
s
end
def wiki_page_breadcrumb(page)
breadcrumb(page.ancestors.reverse.collect {|parent|
link_to(h(parent.pretty_title), {:controller => 'wiki', :action => 'show', :id => parent.title, :project_id => parent.project})
})
def html_diff(wdiff)
words = wdiff.words.collect{|word| h(word)}
words_add = 0
words_del = 0
dels = 0
del_off = 0
wdiff.diff.diffs.each do |diff|
add_at = nil
add_to = nil
del_at = nil
deleted = ""
diff.each do |change|
pos = change[1]
if change[0] == "+"
add_at = pos + dels unless add_at
add_to = pos + dels
words_add += 1
else
del_at = pos unless del_at
deleted << ' ' + h(change[2])
words_del += 1
end
end
if add_at
words[add_at] = '<span class="diff_in">' + words[add_at]
words[add_to] = words[add_to] + '</span>'
end
if del_at
words.insert del_at - del_off + dels + words_add, '<span class="diff_out">' + deleted + '</span>'
dels += 1
del_off += words_del
words_del = 0
end
end
simple_format_without_paragraph(words.join(' '))
end
end

View File

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

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-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.
@@ -20,11 +20,10 @@ require "digest/md5"
class Attachment < ActiveRecord::Base
belongs_to :container, :polymorphic => true
belongs_to :author, :class_name => "User", :foreign_key => "author_id"
validates_presence_of :container, :filename, :author
validates_length_of :filename, :maximum => 255
validates_length_of :disk_filename, :maximum => 255
validate :validate_max_file_size
acts_as_event :title => :filename,
:url => Proc.new {|o| {:controller => 'attachments', :action => 'download', :id => o.id, :filename => o.filename}}
@@ -32,24 +31,21 @@ class Attachment < ActiveRecord::Base
acts_as_activity_provider :type => 'files',
:permission => :view_files,
:author_key => :author_id,
:find_options => {:select => "#{Attachment.table_name}.*",
:find_options => {:select => "#{Attachment.table_name}.*",
:joins => "LEFT JOIN #{Version.table_name} ON #{Attachment.table_name}.container_type='Version' AND #{Version.table_name}.id = #{Attachment.table_name}.container_id " +
"LEFT JOIN #{Project.table_name} ON #{Version.table_name}.project_id = #{Project.table_name}.id OR ( #{Attachment.table_name}.container_type='Project' AND #{Attachment.table_name}.container_id = #{Project.table_name}.id )"}
acts_as_activity_provider :type => 'documents',
:permission => :view_documents,
:author_key => :author_id,
:find_options => {:select => "#{Attachment.table_name}.*",
:find_options => {:select => "#{Attachment.table_name}.*",
:joins => "LEFT JOIN #{Document.table_name} ON #{Attachment.table_name}.container_type='Document' AND #{Document.table_name}.id = #{Attachment.table_name}.container_id " +
"LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id"}
cattr_accessor :storage_path
@@storage_path = Redmine::Configuration['attachments_storage_path'] || "#{Rails.root}/files"
before_save :files_to_final_location
after_destroy :delete_from_disk
def validate_max_file_size
@@storage_path = "#{RAILS_ROOT}/files"
def validate
if self.filesize > Setting.attachment_max_size.to_i.kilobytes
errors.add(:base, :too_long, :count => Setting.attachment_max_size.to_i.kilobytes)
end
@@ -76,11 +72,11 @@ class Attachment < ActiveRecord::Base
# Copies the temporary file to its final location
# and computes its MD5 hash
def files_to_final_location
def before_save
if @temp_file && (@temp_file.size > 0)
logger.info("Saving attachment '#{self.diskfile}' (#{@temp_file.size} bytes)")
logger.debug("saving '#{self.diskfile}'")
md5 = Digest::MD5.new
File.open(diskfile, "wb") do |f|
File.open(diskfile, "wb") do |f|
buffer = ""
while (buffer = @temp_file.read(8192))
f.write(buffer)
@@ -89,7 +85,6 @@ class Attachment < ActiveRecord::Base
end
self.digest = md5.hexdigest
end
@temp_file = nil
# Don't save the content type if it's longer than the authorized length
if self.content_type && self.content_type.length > 255
self.content_type = nil
@@ -97,7 +92,7 @@ class Attachment < ActiveRecord::Base
end
# Deletes file on the disk
def delete_from_disk
def after_destroy
File.delete(diskfile) if !filename.blank? && File.exist?(diskfile)
end
@@ -105,7 +100,7 @@ class Attachment < ActiveRecord::Base
def diskfile
"#{@@storage_path}/#{self.disk_filename}"
end
def increment_download
increment!(:downloads)
end
@@ -113,27 +108,27 @@ class Attachment < ActiveRecord::Base
def project
container.project
end
def visible?(user=User.current)
container.attachments_visible?(user)
end
def deletable?(user=User.current)
container.attachments_deletable?(user)
end
def image?
self.filename =~ /\.(bmp|gif|jpg|jpe|jpeg|png)$/i
self.filename =~ /\.(jpe?g|gif|png)$/i
end
def is_text?
Redmine::MimeType.is_type?('text', filename)
end
def is_diff?
self.filename =~ /\.(patch|diff)$/i
end
# Returns true if the file is readable
def readable?
File.readable?(diskfile)
@@ -150,11 +145,10 @@ class Attachment < ActiveRecord::Base
attachments.each_value do |attachment|
file = attachment['file']
next unless file && file.size > 0
a = Attachment.create(:container => obj,
a = Attachment.create(:container => obj,
:file => file,
:description => attachment['description'].to_s.strip,
:author => User.current)
obj.attachments << a
if a.new_record?
obj.unsaved_attachments ||= []
@@ -166,22 +160,18 @@ class Attachment < ActiveRecord::Base
end
{:files => attached, :unsaved => obj.unsaved_attachments}
end
def self.latest_attach(attachments, filename)
attachments.sort_by(&:created_on).reverse.detect {
|att| att.filename.downcase == filename.downcase
}
end
private
def sanitize_filename(value)
# get only the filename, not the whole path
just_filename = value.gsub(/^.*(\\|\/)/, '')
# NOTE: File.basename doesn't work right with Windows paths on Unix
# INCORRECT: just_filename = File.basename(value.gsub('\\\\', '/'))
# Finally, replace invalid characters with underscore
@filename = just_filename.gsub(/[\/\?\%\*\:\|\"\'<>]+/, '_')
# Finally, replace all non alphanumeric, hyphens or periods with underscore
@filename = just_filename.gsub(/[^\w\.\-]/,'_')
end
# Returns an ASCII or hashed filename
def self.disk_filename(filename)
timestamp = DateTime.now.strftime("%y%m%d%H%M%S")

View File

@@ -1,47 +1,37 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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 AuthSource < ActiveRecord::Base
include Redmine::Ciphering
has_many :users
validates_presence_of :name
validates_uniqueness_of :name
validates_length_of :name, :maximum => 60
def authenticate(login, password)
end
def test_connection
end
def auth_method_name
"Abstract"
end
def account_password
read_ciphered_attribute(:account_password)
end
def account_password=(arg)
write_ciphered_attribute(:account_password, arg)
end
def allow_password_changes?
self.class.allow_password_changes?
end

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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.
@@ -18,23 +18,23 @@
require 'net/ldap'
require 'iconv'
class AuthSourceLdap < AuthSource
class AuthSourceLdap < AuthSource
validates_presence_of :host, :port, :attr_login
validates_length_of :name, :host, :maximum => 60, :allow_nil => true
validates_length_of :account, :account_password, :base_dn, :maximum => 255, :allow_nil => true
validates_length_of :name, :host, :account_password, :maximum => 60, :allow_nil => true
validates_length_of :account, :base_dn, :maximum => 255, :allow_nil => true
validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30, :allow_nil => true
validates_numericality_of :port, :only_integer => true
before_validation :strip_ldap_attributes
def after_initialize
self.port = 389 if self.port == 0
end
def authenticate(login, password)
return nil if login.blank? || password.blank?
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)
@@ -50,19 +50,19 @@ class AuthSourceLdap < AuthSource
rescue Net::LDAP::LdapError => text
raise "LdapError: " + text
end
def auth_method_name
"LDAP"
end
private
def strip_ldap_attributes
[:attr_login, :attr_firstname, :attr_lastname, :attr_mail].each do |attr|
write_attribute(attr, read_attribute(attr).strip) unless read_attribute(attr).nil?
end
end
def initialize_ldap_con(ldap_user, ldap_password)
options = { :host => self.host,
:port => self.port,
@@ -102,12 +102,12 @@ class AuthSourceLdap < AuthSource
# 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", "*" )
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,
ldap_con.search( :base => self.base_dn,
:filter => object_filter & login_filter,
:attributes=> search_attributes) do |entry|
if onthefly_register?
@@ -121,7 +121,7 @@ class AuthSourceLdap < AuthSource
attrs
end
def self.get_attr(entry, attr_name)
if !attr_name.blank?
entry[attr_name].is_a?(Array) ? entry[attr_name].first : entry[attr_name]

View File

@@ -1,50 +1,44 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-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 Board < ActiveRecord::Base
include Redmine::SafeAttributes
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 => :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
validates_presence_of :name, :description
validates_length_of :name, :maximum => 30
validates_length_of :description, :maximum => 255
named_scope :visible, lambda {|*args| { :include => :project,
:conditions => Project.allowed_to_condition(args.shift || User.current, :view_messages, *args) } }
safe_attributes 'name', 'description', 'move_to'
def visible?(user=User.current)
!user.nil? && user.allowed_to?(:view_messages, project)
end
def to_s
name
end
def reset_counters!
self.class.reset_counters!(id)
end
# Updates topics_count, messages_count and last_message_id attributes for +board_id+
def self.reset_counters!(board_id)
board_id = board_id.to_i

View File

@@ -1,36 +1,30 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-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 Change < ActiveRecord::Base
belongs_to :changeset
validates_presence_of :changeset_id, :action, :path
before_save :init_path
before_validation :replace_invalid_utf8_of_path
def relative_path
changeset.repository.relative_path(path)
end
def replace_invalid_utf8_of_path
self.path = Redmine::CodesetUtil.replace_invalid_utf8(self.path)
self.from_path = Redmine::CodesetUtil.replace_invalid_utf8(self.from_path)
end
def init_path
self.path ||= ""
end

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 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
# 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.
@@ -22,39 +22,28 @@ class Changeset < ActiveRecord::Base
belongs_to :user
has_many :changes, :dependent => :delete_all
has_and_belongs_to_many :issues
has_and_belongs_to_many :parents,
:class_name => "Changeset",
:join_table => "#{table_name_prefix}changeset_parents#{table_name_suffix}",
:association_foreign_key => 'parent_id', :foreign_key => 'changeset_id'
has_and_belongs_to_many :children,
:class_name => "Changeset",
:join_table => "#{table_name_prefix}changeset_parents#{table_name_suffix}",
:association_foreign_key => 'changeset_id', :foreign_key => 'parent_id'
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.identifier}}
acts_as_searchable :columns => 'comments',
:include => {:repository => :project},
:project_key => "#{Repository.table_name}.project_id",
:date_column => 'committed_on'
acts_as_activity_provider :timestamp => "#{table_name}.committed_on",
:author_key => :user_id,
:find_options => {:include => [:user, {:repository => :project}]}
validates_presence_of :repository_id, :revision, :committed_on, :commit_date
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.shift || User.current, :view_changesets, *args) } }
after_create :scan_for_issues
before_create :before_create_cs
:conditions => Project.allowed_to_condition(args.first || User.current, :view_changesets) } }
def revision=(r)
write_attribute :revision, (r.nil? ? nil : r.to_s)
end
@@ -67,6 +56,10 @@ class Changeset < ActiveRecord::Base
revision.to_s
end
end
def comments=(comment)
write_attribute(:comments, Changeset.normalize_comments(comment))
end
def committed_on=(date)
self.commit_date = date
@@ -81,26 +74,27 @@ class Changeset < ActiveRecord::Base
identifier
end
end
def committer=(arg)
write_attribute(:committer, self.class.to_utf8(arg.to_s))
end
def project
repository.project
end
def author
user || committer.to_s.split('<').first
end
def before_create_cs
self.committer = self.class.to_utf8(self.committer, repository.repo_log_encoding)
self.comments = self.class.normalize_comments(
self.comments, repository.repo_log_encoding)
self.user = repository.find_committer_user(self.committer)
def before_create
self.user = repository.find_committer_user(committer)
end
def scan_for_issues
def after_create
scan_comment_for_issue_ids
end
TIMELOG_RE = /
(
((\d+)(h|hours?))((\d+)(m|min)?)?
@@ -112,7 +106,7 @@ class Changeset < ActiveRecord::Base
(\d+([\.,]\d+)?)h?
)
/x
def scan_comment_for_issue_ids
return if comments.blank?
# keywords used to reference issues
@@ -120,15 +114,15 @@ class Changeset < ActiveRecord::Base
ref_keywords_any = ref_keywords.delete('*')
# keywords used to fix issues
fix_keywords = Setting.commit_fix_keywords.downcase.split(",").collect(&:strip)
kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|")
referenced_issues = []
comments.scan(/([\s\(\[,-]|^)((#{kw_regexp})[\s:]+)?(#\d+(\s+@#{TIMELOG_RE})?([\s,;&]+#\d+(\s+@#{TIMELOG_RE})?)*)(?=[[:punct:]]|\s|<|$)/i) do |match|
action, refs = match[2], 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
@@ -138,56 +132,51 @@ class Changeset < ActiveRecord::Base
end
end
end
referenced_issues.uniq!
self.issues = referenced_issues unless referenced_issues.empty?
end
def short_comments
@short_comments || split_comments.first
end
def long_comments
@long_comments || split_comments.last
end
def text_tag(ref_project=nil)
tag = if scmid?
def text_tag
if scmid?
"commit:#{scmid}"
else
"r#{revision}"
end
if ref_project && project && ref_project != project
tag = "#{project.identifier}:#{tag}"
end
tag
end
# Returns the previous changeset
def previous
@previous ||= Changeset.find(:first,
:conditions => ['id < ? AND repository_id = ?',
self.id, self.repository_id],
:order => 'id DESC')
@previous ||= Changeset.find(:first, :conditions => ['id < ? AND repository_id = ?', self.id, self.repository_id], :order => 'id DESC')
end
# Returns the next changeset
def next
@next ||= Changeset.find(:first,
:conditions => ['id > ? AND repository_id = ?',
self.id, self.repository_id],
:order => 'id ASC')
@next ||= Changeset.find(:first, :conditions => ['id > ? AND repository_id = ?', self.id, self.repository_id], :order => 'id ASC')
end
# Strips and reencodes a commit log before insertion into the database
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],
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
@@ -196,28 +185,26 @@ class Changeset < ActiveRecord::Base
return nil if id.blank?
issue = Issue.find_by_id(id.to_i, :include => :project)
if issue
unless issue.project &&
(project == issue.project || project.is_ancestor_of?(issue.project) ||
project.is_descendant_of?(issue.project))
unless issue.project && (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 matches commit_fix_status_id setting (#{Setting.commit_fix_status_id})") if logger
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.project)))
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
@@ -229,30 +216,29 @@ class Changeset < ActiveRecord::Base
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(issue.project),
:locale => Setting.default_language)
: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
@@ -260,14 +246,22 @@ class Changeset < ActiveRecord::Base
return @short_comments, @long_comments
end
public
# Strips and reencodes a commit log before insertion into the database
def self.normalize_comments(str, encoding)
Changeset.to_utf8(str.to_s.strip, encoding)
end
def self.to_utf8(str, encoding)
Redmine::CodesetUtil.to_utf8(str, encoding)
def self.to_utf8(str)
return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
encoding = Setting.commit_logs_encoding.to_s.strip
unless encoding.blank? || encoding == 'UTF-8'
begin
str = Iconv.conv('UTF-8', encoding, str)
rescue Iconv::Failure
# do nothing here
end
end
# 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

@@ -1,26 +1,23 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# 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 Comment < ActiveRecord::Base
include Redmine::SafeAttributes
belongs_to :commented, :polymorphic => true, :counter_cache => true
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
validates_presence_of :commented, :author, :comments
safe_attributes 'comments'
end

View File

@@ -1,24 +0,0 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# 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 CommentObserver < ActiveRecord::Observer
def after_create(comment)
if comment.commented.is_a?(News) && Setting.notified_events.include?('news_comment_added')
Mailer.deliver_news_comment_added(comment)
end
end
end

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