Compare commits

...

371 Commits

Author SHA1 Message Date
Toshi MARUYAMA
bc79904e4e Merged r4806 from trunk.
scm: change gunzip to tar -z option for scm repository setup in lib/tasks/testing.rake.

Pipe does not work on Mingw Ruby.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@6271 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-15 00:48:46 +00:00
Toshi MARUYAMA
ffc111bd23 Merged r6127 from trunk.
Escape AuthSources in the list.

Contributed by MAEDA, Go

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@6129 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-27 23:21:45 +00:00
Toshi MARUYAMA
2a2e705e9e Merged r5743 from trunk.
Russian "default_issue_status_rejected" and "default_tracker_feature" changed.

Contributed by Dmitry Babenko.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5744 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-11 23:03:23 +00:00
Toshi MARUYAMA
31cd20f2b6 Merged r5740 from trunk.
Hungarian translation for 1.1.3 updated by Gábor Takács.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5742 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-11 22:31:54 +00:00
Toshi MARUYAMA
4fc2e757f6 Merged r5644 from trunk.
scm: git: add comments of revision order in fetch_changesets().

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5646 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-04 23:43:40 +00:00
Toshi MARUYAMA
240254b1a3 Merged r5643 from trunk.
scm: git: rearrange fetch_changesets() comment.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5645 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-04 23:42:58 +00:00
Toshi MARUYAMA
95f3c336d5 Merged r5621 from trunk.
Fix a typo in Czech localization.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5623 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-04 00:43:17 +00:00
Toshi MARUYAMA
d63e3a781f Merged r5620 from trunk.
Simplified Chinese translation updated by Sam Qiu.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5622 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-04 00:42:54 +00:00
Toshi MARUYAMA
0aab60943c Merged r5614 from trunk.
Fix a typo in public/help/wiki_syntax_detailed.html.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5616 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-02 23:17:02 +00:00
Toshi MARUYAMA
935306af80 Merged r5611 from trunk.
Fix potential Execution After Redirect bugs.

Execution After Redirect (EAR) happens when redirect in a controller is
triggered but there still is code that is executed in the action.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5613 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-01 23:20:46 +00:00
Toshi MARUYAMA
6fce2170c4 Merged r5610 from trunk.
Add missing fixture when running tests from scratch in functional RolesControllerTest.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@5612 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-01 23:20:04 +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
Jean-Philippe Lang
dd87ebef58 Makes the png looks more like the html gantt.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4584 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-30 15:04:08 +00:00
Jean-Philippe Lang
82f528ddc8 Makes issue move form similar to bulk edit form.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4583 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-29 20:48:51 +00:00
Jean-Philippe Lang
ad62060753 Disable project completion display on the gantt (#7127).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4582 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-29 20:29:42 +00:00
Jean-Philippe Lang
f2ae2e9239 Simple issue sort method to make sure subtasks appear under their parent on the gantt (#7128).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4581 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-29 20:21:39 +00:00
Jean-Philippe Lang
d2cc2861de Find visible issues only in ContextMenusController#issues.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4580 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-29 19:55:52 +00:00
Jean-Philippe Lang
e9a0e6dbc8 Takes more parameters into account to determine activity freshness (#7188).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4579 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-29 19:37:42 +00:00
Jean-Philippe Lang
af7006dff6 Fixed: partial toc when text contains pre tags (#7172).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4578 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-29 18:21:22 +00:00
Jean-Philippe Lang
0025a66200 Fixed: cvs diff broken by r4539 (#7176).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4577 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-29 17:46:47 +00:00
Jean-Philippe Lang
03d4ecbbff Fixed: CLI-supplied defaults should not be applied when replying to an issue (#7195).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4576 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-29 17:38:57 +00:00
Jean-Philippe Lang
21c0b868ae Fixed: Tracker reset to default when replying to an issue email (#7197).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4575 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-29 17:36:01 +00:00
Jean-Philippe Lang
df9ea24136 Makes Version#start_date return the minimum start_date of its issues.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4574 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 14:58:52 +00:00
Jean-Philippe Lang
07fe46e9df Makes the API accepts the X-Redmine-API-Key header to hold the API key.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4573 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 14:49:14 +00:00
Jean-Philippe Lang
283d25d823 Makes /projects API accept same pagination parameters as other API collection resources.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4572 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 13:34:44 +00:00
Jean-Philippe Lang
d076c19822 Makes API accept offset/limit or page/limit parameters for retrieving collections.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4571 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 13:33:01 +00:00
Jean-Philippe Lang
1ee7f31f4d Set VERSION to 1.0.5.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4568 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 10:04:08 +00:00
Jean-Philippe Lang
d78a510642 Updated INSTALL for 1.0.5
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4567 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 10:03:32 +00:00
Jean-Philippe Lang
3ef41c3131 Updated CHANGELOG for 1.0.5
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4566 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 10:02:08 +00:00
Jean-Philippe Lang
759048e73a Fixes: Mercurial adapter loses seconds of commit times (#6656).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4561 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 09:56:30 +00:00
Jean-Philippe Lang
3b53ec20e6 Adds themes test for when Redmine is in a sub-uri.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4560 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 09:42:33 +00:00
Jean-Philippe Lang
c8dc7fff08 Avoid theme rescan when no theme is selected.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4559 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-22 21:41:08 +00:00
Jean-Philippe Lang
523febf9c1 Support for Javascript in Themes (#2803).
If javascripts/theme.js is found in the theme directory, it will be automatically loaded on each page.

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

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

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

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


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


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


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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
/.project
/.loadpath
/config/additional_environment.rb
/config/database.yml
/config/email.yml

24
.hgignore Normal file
View File

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

View File

@@ -23,7 +23,7 @@ class ActivitiesController < ApplicationController
events = @activity.events(@date_from, @date_to)
if events.empty? || stale?(:etag => [events.first, User.current])
if events.empty? || stale?(:etag => [@activity.scope, @date_to, @date_from, @with_subprojects, @author, events.first, User.current, current_language])
respond_to do |format|
format.html {
@events_by_day = events.group_by(&:event_date)

View File

@@ -22,7 +22,7 @@ class ApplicationController < ActionController::Base
include Redmine::I18n
layout 'base'
exempt_from_layout 'builder'
exempt_from_layout 'builder', 'rsb'
# Remove broken cookie after upgrade from 0.8.x (#4292)
# See https://rails.lighthouseapp.com/projects/8994/tickets/3360
@@ -71,10 +71,10 @@ class ApplicationController < ActionController::Base
elsif params[:format] == 'atom' && params[:key] && accept_key_auth_actions.include?(params[:action])
# RSS key authentication does not start a session
User.find_by_rss_key(params[:key])
elsif Setting.rest_api_enabled? && ['xml', 'json'].include?(params[:format])
if params[:key].present? && accept_key_auth_actions.include?(params[:action])
elsif Setting.rest_api_enabled? && api_request?
if (key = api_key_from_request) && accept_key_auth_actions.include?(params[:action])
# Use API key
User.find_by_api_key(params[:key])
User.find_by_api_key(key)
else
# HTTP Basic, either username/password or API key/random
authenticate_with_http_basic do |username, password|
@@ -154,7 +154,15 @@ class ApplicationController < ActionController::Base
# Authorize the user for the requested action
def authorize(ctrl = params[:controller], action = params[:action], global = false)
allowed = User.current.allowed_to?({:controller => ctrl, :action => action}, @project || @projects, :global => global)
allowed ? true : deny_access
if allowed
true
else
if @project && @project.archived?
render_403 :message => :notice_not_authorized_archived_project
else
deny_access
end
end
end
# Authorize the user for the requested action outside a project
@@ -263,41 +271,36 @@ class ApplicationController < ActionController::Base
end
end
redirect_to default
false
end
def render_403
def render_403(options={})
@project = nil
respond_to do |format|
format.html { render :template => "common/403", :layout => use_layout, :status => 403 }
format.atom { head 403 }
format.xml { head 403 }
format.js { head 403 }
format.json { head 403 }
end
render_error({:message => :notice_not_authorized, :status => 403}.merge(options))
return false
end
def render_404
respond_to do |format|
format.html { render :template => "common/404", :layout => use_layout, :status => 404 }
format.atom { head 404 }
format.xml { head 404 }
format.js { head 404 }
format.json { head 404 }
end
def render_404(options={})
render_error({:message => :notice_file_not_found, :status => 404}.merge(options))
return false
end
def render_error(msg)
# 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 {
flash.now[:error] = msg
render :text => '', :layout => use_layout, :status => 500
format.html {
render :template => 'common/error', :layout => use_layout, :status => @status
}
format.atom { head 500 }
format.xml { head 500 }
format.js { head 500 }
format.json { head 500 }
format.atom { head @status }
format.xml { head @status }
format.js { head @status }
format.json { head @status }
end
end
@@ -347,6 +350,30 @@ class ApplicationController < ActionController::Base
per_page
end
# Returns offset and limit used to retrieve objects
# for an API response based on offset, limit and page parameters
def api_offset_and_limit(options=params)
if options[:offset].present?
offset = options[:offset].to_i
if offset < 0
offset = 0
end
end
limit = options[:limit].to_i
if limit < 1
limit = 25
elsif limit > 100
limit = 100
end
if offset.nil? && options[:page].present?
offset = (options[:page].to_i - 1) * limit
offset = 0 if offset < 0
end
offset ||= 0
[offset, limit]
end
# qvalues http header parser
# code taken from webrick
def parse_qvalues(value)
@@ -376,6 +403,15 @@ class ApplicationController < ActionController::Base
def api_request?
%w(xml json).include? params[:format]
end
# Returns the API key present in the request
def api_key_from_request
if params[:key].present?
params[:key]
elsif request.headers["X-Redmine-API-Key"].present?
request.headers["X-Redmine-API-Key"]
end
end
# Renders a warning flash if obj has unsaved attachments
def render_attachment_warning_if_needed(obj)
@@ -411,5 +447,37 @@ class ApplicationController < ActionController::Base
{ attribute => error }
end.to_json
end
# Renders API response on validation failure
def render_validation_errors(object)
options = { :status => :unprocessable_entity, :layout => false }
options.merge!(case params[:format]
when 'xml'; { :xml => object.errors }
when 'json'; { :json => {'errors' => object.errors} } # ActiveResource client compliance
else
raise "Unknown format #{params[:format]} in #render_validation_errors"
end
)
render options
end
# Overrides #default_template so that the api template
# is used automatically if it exists
def default_template(action_name = self.action_name)
if api_request?
begin
return self.view_paths.find_template(default_template_name(action_name), 'api')
rescue ::ActionView::MissingTemplate
# the api template was not found
# fallback to the default behaviour
end
end
super
end
# Overrides #pick_layout so that #render with no arguments
# doesn't use the layout for api requests
def pick_layout(*args)
api_request? ? nil : super
end
end

View File

@@ -4,12 +4,14 @@ class AutoCompletesController < ApplicationController
def issues
@issues = []
q = params[:q].to_s
query = (params[:scope] == "all" && Setting.cross_project_issue_relations?) ? Issue : @project.issues
if q.match(/^\d+$/)
@issues << @project.issues.visible.find_by_id(q.to_i)
@issues << query.visible.find_by_id(q.to_i)
end
unless q.blank?
@issues += @project.issues.visible.find(:all, :conditions => ["LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%"], :limit => 10)
@issues += query.visible.find(:all, :conditions => ["LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%"], :limit => 10)
end
@issues.compact!
render :layout => false
end

View File

@@ -2,7 +2,8 @@ class ContextMenusController < ApplicationController
helper :watchers
def issues
@issues = Issue.find_all_by_id(params[:ids], :include => :project)
@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)

View File

@@ -38,8 +38,9 @@ class CustomFieldsController < ApplicationController
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
@@ -48,8 +49,9 @@ 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

View File

@@ -75,10 +75,12 @@ class EnumerationsController < ApplicationController
# 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

@@ -65,10 +65,12 @@ class IssueCategoriesController < ApplicationController
# No issue assigned to this category
@category.destroy
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'categories'
return
elsif params[:todo]
reassign_to = @project.issue_categories.find_by_id(params[:reassign_to_id]) if params[:todo] == 'reassign'
@category.destroy(reassign_to)
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'categories'
return
end
@categories = @project.issue_categories - [@category]
end

View File

@@ -18,6 +18,7 @@ class IssueMovesController < ApplicationController
@issues.each do |issue|
issue.reload
issue.init_journal(User.current)
issue.current_journal.notes = @notes if @notes.present?
call_hook(:controller_issues_move_before_save, { :params => params, :issue => issue, :target_project => @target_project, :copy => !!@copy })
if r = issue.move_to_project(@target_project, new_tracker, {:copy => @copy, :attributes => extract_changed_attributes_for_move(params)})
moved_issues << r
@@ -50,11 +51,13 @@ class IssueMovesController < ApplicationController
@target_project ||= @project
@trackers = @target_project.trackers
@available_statuses = Workflow.available_statuses(@project)
@notes = params[:notes]
@notes ||= ''
end
def extract_changed_attributes_for_move(params)
changed_attributes = {}
[:assigned_to_id, :status_id, :start_date, :due_date].each do |valid_attribute|
[:assigned_to_id, :status_id, :start_date, :due_date, :priority_id].each do |valid_attribute|
unless params[valid_attribute].blank?
changed_attributes[valid_attribute] = (params[valid_attribute] == 'none' ? nil : params[valid_attribute])
end

View File

@@ -28,6 +28,7 @@ class IssueRelationsController < ApplicationController
respond_to do |format|
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @issue }
format.js do
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
render :update do |page|
page.replace_html "relations", :partial => 'issues/relations'
if @relation.errors.empty?
@@ -47,7 +48,10 @@ class IssueRelationsController < ApplicationController
end
respond_to do |format|
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @issue }
format.js { render(:update) {|page| page.replace_html "relations", :partial => 'issues/relations'} }
format.js {
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
render(:update) {|page| page.replace_html "relations", :partial => 'issues/relations'}
}
end
end

View File

@@ -27,7 +27,7 @@ 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_key_auth :index, :show
accept_key_auth :index, :show, :create, :update, :destroy
rescue_from Query::StatementInvalid, :with => :query_statement_invalid
@@ -44,6 +44,8 @@ class IssuesController < ApplicationController
include AttachmentsHelper
helper :queries
include QueriesHelper
helper :repositories
include RepositoriesHelper
helper :sort
include SortHelper
include IssuesHelper
@@ -65,27 +67,29 @@ class IssuesController < ApplicationController
sort_update(@query.sortable_columns)
if @query.valid?
limit = case params[:format]
case params[:format]
when 'csv', 'pdf'
Setting.issues_export_limit.to_i
@limit = Setting.issues_export_limit.to_i
when 'atom'
Setting.feeds_limit.to_i
@limit = Setting.feeds_limit.to_i
when 'xml', 'json'
@offset, @limit = api_offset_and_limit
else
per_page_option
@limit = per_page_option
end
@issue_count = @query.issue_count
@issue_pages = Paginator.new self, @issue_count, limit, params['page']
@issue_pages = Paginator.new self, @issue_count, @limit, params['page']
@offset ||= @issue_pages.current.offset
@issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
:order => sort_clause,
:offset => @issue_pages.current.offset,
:limit => limit)
:offset => @offset,
:limit => @limit)
@issue_count_by_group = @query.issue_count_by_group
respond_to do |format|
format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
format.xml { render :layout => false }
format.json { render :text => @issues.to_json, :layout => false }
format.api
format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
format.csv { send_data(issues_to_csv(@issues, @project), :type => 'text/csv; header=present', :filename => 'export.csv') }
format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
@@ -104,14 +108,14 @@ class IssuesController < ApplicationController
@journals.reverse! if User.current.wants_comments_in_reverse_order?
@changesets = @issue.changesets.visible.all
@changesets.reverse! if User.current.wants_comments_in_reverse_order?
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
@edit_allowed = User.current.allowed_to?(:edit_issues, @project)
@priorities = IssuePriority.all
@time_entry = TimeEntry.new
respond_to do |format|
format.html { render :template => 'issues/show.rhtml' }
format.xml { render :layout => false }
format.json { render :text => @issue.to_json, :layout => false }
format.api
format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
end
@@ -138,23 +142,17 @@ class IssuesController < ApplicationController
redirect_to(params[:continue] ? { :action => 'new', :project_id => @project, :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
{ :action => 'show', :id => @issue })
}
format.xml { render :action => 'show', :status => :created, :location => url_for(:controller => 'issues', :action => 'show', :id => @issue) }
format.json { render :text => @issue.to_json, :status => :created, :location => url_for(:controller => 'issues', :action => 'show'), :layout => false }
format.api { render :action => 'show', :status => :created, :location => issue_url(@issue) }
end
return
else
respond_to do |format|
format.html { render :action => 'new' }
format.xml { render(:xml => @issue.errors, :status => :unprocessable_entity); return }
format.json { render :text => object_errors_to_json(@issue), :status => :unprocessable_entity, :layout => false }
format.api { render_validation_errors(@issue) }
end
end
end
# Attributes that can be updated on workflow transition (without :edit permission)
# TODO: make it configurable (at least per role)
UPDATABLE_ATTRS_ON_TRANSITION = %w(status_id assigned_to_id fixed_version_id done_ratio) unless const_defined?(:UPDATABLE_ATTRS_ON_TRANSITION)
def edit
update_issue_from_params
@@ -175,8 +173,7 @@ class IssuesController < ApplicationController
respond_to do |format|
format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
format.xml { head :ok }
format.json { head :ok }
format.api { head :ok }
end
else
render_attachment_warning_if_needed(@issue)
@@ -185,8 +182,7 @@ class IssuesController < ApplicationController
respond_to do |format|
format.html { render :action => 'edit' }
format.xml { render :xml => @issue.errors, :status => :unprocessable_entity }
format.json { render :text => object_errors_to_json(@issue), :status => :unprocessable_entity, :layout => false }
format.api { render_validation_errors(@issue) }
end
end
end
@@ -236,17 +232,20 @@ class IssuesController < ApplicationController
TimeEntry.update_all("issue_id = #{reassign_to.id}", ['issue_id IN (?)', @issues])
end
else
unless params[:format] == 'xml' || params[:format] == 'json'
# display the destroy form if it's a user request
return
end
# display the destroy form if it's a user request
return unless api_request?
end
end
@issues.each do |issue|
begin
issue.reload.destroy
rescue ::ActiveRecord::RecordNotFound # raised by #reload if issue no longer exists
# nothing to do, issue was already deleted (eg. by a parent)
end
end
@issues.each(&:destroy)
respond_to do |format|
format.html { redirect_back_or_default(:action => 'index', :project_id => @project) }
format.xml { head :ok }
format.json { head :ok }
format.api { head :ok }
end
end
@@ -273,17 +272,11 @@ private
@priorities = IssuePriority.all
@edit_allowed = User.current.allowed_to?(:edit_issues, @project)
@time_entry = TimeEntry.new
@time_entry.attributes = params[:time_entry]
@notes = params[:notes] || (params[:issue].present? ? params[:issue][:notes] : nil)
@issue.init_journal(User.current, @notes)
# User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue]
attrs = params[:issue].dup
attrs.delete_if {|k,v| !UPDATABLE_ATTRS_ON_TRANSITION.include?(k) } unless @edit_allowed
attrs.delete(:status_id) unless @allowed_statuses.detect {|s| s.id.to_s == attrs[:status_id].to_s}
@issue.safe_attributes = attrs
end
@issue.safe_attributes = params[:issue]
end
# TODO: Refactor, lots of extra code in here
@@ -304,6 +297,7 @@ private
render_error l(:error_no_tracker_in_project)
return false
end
@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?
@@ -311,7 +305,6 @@ private
end
end
@issue.author = User.current
@issue.start_date ||= Date.today
@priorities = IssuePriority.all
@allowed_statuses = @issue.new_statuses_allowed_to(User.current, true)
end

View File

@@ -19,9 +19,11 @@ class JournalsController < ApplicationController
before_filter :find_journal, :only => [:edit]
before_filter :find_issue, :only => [:new]
before_filter :find_optional_project, :only => [:index]
before_filter :authorize, :only => [:new, :edit]
accept_key_auth :index
helper :issues
helper :custom_fields
helper :queries
include QueriesHelper
helper :sort
@@ -74,6 +76,14 @@ class JournalsController < ApplicationController
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @journal.journalized_id }
format.js { render :action => 'update' }
end
else
respond_to do |format|
format.html {
# TODO: implement non-JS journal update
render :nothing => true
}
format.js
end
end
end

View File

@@ -19,6 +19,7 @@ class MyController < ApplicationController
before_filter :require_login
helper :issues
helper :users
helper :custom_fields
BLOCKS = { 'issuesassignedtome' => :label_assigned_to_me_issues,
@@ -53,21 +54,18 @@ class MyController < ApplicationController
@user = User.current
@pref = @user.pref
if request.post?
@user.attributes = params[:user]
@user.mail_notification = params[:notification_option] || 'only_my_events'
@user.safe_attributes = params[:user]
@user.pref.attributes = params[:pref]
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
if @user.save
@user.pref.save
@user.notified_project_ids = (params[:notification_option] == 'selected' ? params[:notified_project_ids] : [])
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
set_language_if_valid @user.language
flash[:notice] = l(:notice_account_updated)
redirect_to :action => 'account'
return
end
end
@notification_options = @user.valid_notification_options
@notification_option = @user.mail_notification #? ? 'all' : (@user.notified_projects_ids.empty? ? 'none' : 'selected')
end
# Manage user's password

View File

@@ -26,15 +26,26 @@ class NewsController < ApplicationController
accept_key_auth :index
def index
@news_pages, @newss = paginate :news,
:per_page => 10,
:conditions => Project.allowed_to_condition(User.current, :view_news, :project => @project),
:include => [:author, :project],
:order => "#{News.table_name}.created_on DESC"
case params[:format]
when 'xml', 'json'
@offset, @limit = api_offset_and_limit
else
@limit = 10
end
scope = @project ? @project.news.visible : News.visible
@news_count = scope.count
@news_pages = Paginator.new self, @news_count, @limit, params['page']
@offset ||= @news_pages.current.offset
@newss = scope.all(:include => [:author, :project],
:order => "#{News.table_name}.created_on DESC",
:offset => @offset,
:limit => @limit)
respond_to do |format|
format.html { render :layout => false if request.xhr? }
format.xml { render :xml => @newss.to_xml }
format.json { render :json => @newss.to_json }
format.api
format.atom { render_feed(@newss, :title => (@project ? @project.name : Setting.app_title) + ": #{l(:label_news_plural)}") }
end
end

View File

@@ -24,7 +24,7 @@ class ProjectsController < ApplicationController
before_filter :authorize, :except => [ :index, :list, :new, :create, :copy, :archive, :unarchive, :destroy]
before_filter :authorize_global, :only => [:new, :create]
before_filter :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ]
accept_key_auth :index
accept_key_auth :index, :show, :create, :update, :destroy
after_filter :only => [:create, :edit, :update, :archive, :unarchive, :destroy] do |controller|
if controller.request.post?
@@ -32,9 +32,6 @@ class ProjectsController < ApplicationController
end
end
# TODO: convert to PUT only
verify :method => [:post, :put], :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
helper :sort
include SortHelper
helper :custom_fields
@@ -52,8 +49,10 @@ class ProjectsController < ApplicationController
format.html {
@projects = Project.visible.find(:all, :order => 'lft')
}
format.xml {
@projects = Project.visible.find(:all, :order => 'lft')
format.api {
@offset, @limit = api_offset_and_limit
@project_count = Project.visible.count
@projects = Project.visible.all(:offset => @offset, :limit => @limit, :order => 'lft')
}
format.atom {
projects = Project.visible.find(:all, :order => 'created_on DESC',
@@ -67,19 +66,15 @@ class ProjectsController < ApplicationController
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.all
@project = Project.new(params[:project])
@project.identifier = Project.next_identifier if Setting.sequential_project_identifiers?
@project.trackers = Tracker.all
@project.is_public = Setting.default_projects_public?
@project.enabled_module_names = Setting.default_projects_modules
end
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
def create
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.all
@project = Project.new(params[:project])
@project = Project.new
@project.safe_attributes = params[:project]
@project.enabled_module_names = params[:enabled_modules]
if validate_parent_id && @project.save
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
# Add current user as a project member if he is not admin
@@ -93,12 +88,12 @@ class ProjectsController < ApplicationController
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'projects', :action => 'settings', :id => @project
}
format.xml { head :created, :location => url_for(:controller => 'projects', :action => 'show', :id => @project.id) }
format.api { render :action => 'show', :status => :created, :location => url_for(:controller => 'projects', :action => 'show', :id => @project.id) }
end
else
respond_to do |format|
format.html { render :action => 'new' }
format.xml { render :xml => @project.errors, :status => :unprocessable_entity }
format.api { render_validation_errors(@project) }
end
end
@@ -120,18 +115,18 @@ class ProjectsController < ApplicationController
end
else
Mailer.with_deliveries(params[:notifications] == '1') do
@project = Project.new(params[:project])
@project.enabled_module_names = params[:enabled_modules]
@project = Project.new
@project.safe_attributes = params[:project]
if validate_parent_id && @project.copy(@source_project, :only => params[:only])
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'projects', :action => 'settings'
redirect_to :controller => 'projects', :action => 'settings', :id => @project
elsif !@project.new_record?
# Project was created
# But some objects were not copied due to validation failures
# (eg. issues from disabled trackers)
# TODO: inform about that
redirect_to :controller => 'projects', :action => 'settings'
redirect_to :controller => 'projects', :action => 'settings', :id => @project
end
end
end
@@ -169,7 +164,7 @@ class ProjectsController < ApplicationController
respond_to do |format|
format.html
format.xml
format.api
end
end
@@ -185,8 +180,10 @@ class ProjectsController < ApplicationController
def edit
end
# TODO: convert to PUT only
verify :method => [:post, :put], :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
def update
@project.attributes = params[:project]
@project.safe_attributes = params[:project]
if validate_parent_id && @project.save
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
respond_to do |format|
@@ -194,7 +191,7 @@ class ProjectsController < ApplicationController
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'settings', :id => @project
}
format.xml { head :ok }
format.api { head :ok }
end
else
respond_to do |format|
@@ -202,13 +199,14 @@ class ProjectsController < ApplicationController
settings
render :action => 'settings'
}
format.xml { render :xml => @project.errors, :status => :unprocessable_entity }
format.api { render_validation_errors(@project) }
end
end
end
verify :method => :post, :only => :modules, :render => {:nothing => true, :status => :method_not_allowed }
def modules
@project.enabled_module_names = params[:enabled_modules]
@project.enabled_module_names = params[:enabled_module_names]
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'settings', :id => @project, :tab => 'modules'
end
@@ -233,11 +231,11 @@ class ProjectsController < ApplicationController
if request.get?
# display confirmation view
else
if params[:format] == 'xml' || params[:confirm]
if api_request? || params[:confirm]
@project_to_destroy.destroy
respond_to do |format|
format.html { redirect_to :controller => 'admin', :action => 'projects' }
format.xml { head :ok }
format.api { head :ok }
end
end
end

View File

@@ -123,7 +123,7 @@ class RepositoriesController < ApplicationController
(show_error_not_found; return) unless @content
if 'raw' == params[:format] || @content.is_binary_data? || (@entry.size && @entry.size > Setting.file_max_size_displayed.to_i.kilobyte)
# Force the download
send_data @content, :filename => @path.split('/').last
send_data @content, :filename => filename_for_content_disposition(@path.split('/').last)
else
# Prevent empty lines when displaying a file with Windows style eol
@content.gsub!("\r\n", "\n")
@@ -139,6 +139,7 @@ class RepositoriesController < ApplicationController
end
def revision
raise ChangesetNotFound if @rev.nil? || @rev.empty?
@changeset = @repository.find_changeset_by_name(@rev)
raise ChangesetNotFound unless @changeset
@@ -174,6 +175,9 @@ class RepositoriesController < ApplicationController
@diff = @repository.diff(@path, @rev, @rev_to)
show_error_not_found unless @diff
end
@changeset = @repository.find_changeset_by_name(@rev)
@changeset_to = @rev_to ? @repository.find_changeset_by_name(@rev_to) : nil
end
end
@@ -196,7 +200,10 @@ class RepositoriesController < ApplicationController
end
end
private
private
REV_PARAM_RE = %r{\A[a-f0-9]*\Z}i
def find_repository
@project = Project.find(params[:id])
@repository = @project.repository
@@ -205,6 +212,12 @@ private
@path ||= ''
@rev = params[:rev].blank? ? @repository.default_branch : params[:rev].strip
@rev_to = params[:rev_to]
unless @rev.to_s.match(REV_PARAM_RE) && @rev_to.to_s.match(REV_PARAM_RE)
if @repository.branches.blank?
raise InvalidRevisionParam
end
end
rescue ActiveRecord::RecordNotFound
render_404
rescue InvalidRevisionParam
@@ -212,7 +225,7 @@ private
end
def show_error_not_found
render_error l(:error_scm_not_found)
render_error :message => l(:error_scm_not_found), :status => 404
end
# Handler for Redmine::Scm::Adapters::CommandFailed exception

View File

@@ -38,9 +38,10 @@ 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
@@ -48,8 +49,9 @@ 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

View File

@@ -36,14 +36,16 @@ class SettingsController < ApplicationController
end
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'edit', :tab => params[:tab]
return
end
@options = {}
@options[:user_format] = User::USER_FORMATS.keys.collect {|f| [User.current.name(f), f.to_s] }
@deliveries = ActionMailer::Base.perform_deliveries
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?
@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
end
def plugin
@@ -52,9 +54,10 @@ 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,5 +1,5 @@
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
# Redmine - project management software
# Copyright (C) 2006-2010 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,9 +17,12 @@
class TimelogController < ApplicationController
menu_item :issues
before_filter :find_project, :authorize, :only => [:new, :create, :edit, :update, :destroy]
before_filter :find_project, :only => [:new, :create]
before_filter :find_time_entry, :only => [:show, :edit, :update, :destroy]
before_filter :authorize, :except => [:index]
before_filter :find_optional_project, :only => [:index]
accept_key_auth :index, :show, :create, :update, :destroy
helper :sort
include SortHelper
helper :issues
@@ -64,6 +67,16 @@ class TimelogController < ApplicationController
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}],
@@ -83,6 +96,14 @@ class TimelogController < ApplicationController
end
end
end
def show
respond_to do |format|
# TODO: Implement html response
format.html { render :nothing => true, :status => 406 }
format.api
end
end
def new
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
@@ -100,15 +121,22 @@ class TimelogController < ApplicationController
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
if @time_entry.save
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
}
format.api { render :action => 'show', :status => :created, :location => time_entry_url(@time_entry) }
end
else
render :action => 'edit'
respond_to do |format|
format.html { render :action => 'edit' }
format.api { render_validation_errors(@time_entry) }
end
end
end
def edit
(render_403; return) if @time_entry && !@time_entry.editable_by?(User.current)
@time_entry.attributes = params[:time_entry]
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
@@ -116,43 +144,67 @@ class TimelogController < ApplicationController
verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
def update
(render_403; return) if @time_entry && !@time_entry.editable_by?(User.current)
@time_entry.attributes = params[:time_entry]
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
if @time_entry.save
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
}
format.api { head :ok }
end
else
render :action => 'edit'
respond_to do |format|
format.html { render :action => 'edit' }
format.api { render_validation_errors(@time_entry) }
end
end
end
verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
def destroy
(render_404; return) unless @time_entry
(render_403; return) unless @time_entry.editable_by?(User.current)
if @time_entry.destroy && @time_entry.destroyed?
flash[:notice] = l(:notice_successful_delete)
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_delete)
redirect_to :back
}
format.api { head :ok }
end
else
flash[:error] = l(:notice_unable_delete_time_entry)
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
redirect_to :back
rescue ::ActionController::RedirectBackError
redirect_to :action => 'index', :project_id => @time_entry.project
end
private
def find_time_entry
@time_entry = TimeEntry.find(params[:id])
unless @time_entry.editable_by?(User.current)
render_403
return false
end
@project = @time_entry.project
rescue ActiveRecord::RecordNotFound
render_404
end
def find_project
if params[:id]
@time_entry = TimeEntry.find(params[:id])
@project = @time_entry.project
elsif params[:issue_id]
@issue = Issue.find(params[:issue_id])
if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).present?
@issue = Issue.find(issue_id)
@project = @issue.project
elsif params[:project_id]
@project = Project.find(params[:project_id])
elsif (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present?
@project = Project.find(project_id)
else
render_404
return false

View File

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

View File

@@ -109,6 +109,10 @@ class VersionsController < ApplicationController
if @version.update_attributes(attributes)
flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
else
respond_to do |format|
format.html { render :action => 'edit' }
end
end
end
end

View File

@@ -17,20 +17,39 @@
require 'diff'
# The WikiController follows the Rails REST controller pattern but with
# a few differences
#
# * index - shows a list of WikiPages grouped by page or date
# * new - not used
# * create - not used
# * show - will also show the form for creating a new wiki page
# * edit - used to edit an existing or new page
# * update - used to save a wiki page update to the database, including new pages
# * destroy - normal
#
# Other member and collection methods are also used
#
# TODO: still being worked on
class WikiController < ApplicationController
default_search_scope :wiki_pages
before_filter :find_wiki, :authorize
before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy]
verify :method => :post, :only => [:destroy, :protect], :redirect_to => { :action => :show }
verify :method => :post, :only => [:protect], :redirect_to => { :action => :show }
helper :attachments
include AttachmentsHelper
helper :watchers
# List of pages, sorted alphabetically and by parent (hierarchy)
def index
load_pages_grouped_by_date_without_content
end
# display a page (in editing mode if it doesn't exist)
def show
page_title = params[:page]
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?
@@ -63,7 +82,7 @@ class WikiController < ApplicationController
# edit an existing page or a new one
def edit
@page = @wiki.find_or_new_page(params[:page])
@page = @wiki.find_or_new_page(params[:id])
return render_403 unless editable?
@page.content = WikiContent.new(:page => @page) if @page.new_record?
@@ -74,15 +93,12 @@ class WikiController < ApplicationController
# To prevent StaleObjectError exception when reverting to a previous version
@content.version = @page.content.version
rescue ActiveRecord::StaleObjectError
# Optimistic locking exception
flash[:error] = l(:notice_locking_conflict)
end
verify :method => :post, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
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[:page])
@page = @wiki.find_or_new_page(params[:id])
return render_403 unless editable?
@page.content = WikiContent.new(:page => @page) if @page.new_record?
@@ -95,7 +111,7 @@ class WikiController < ApplicationController
attachments = Attachment.attach_files(@page, params[:attachments])
render_attachment_warning_if_needed(@page)
# don't save if text wasn't changed
redirect_to :action => 'show', :project_id => @project, :page => @page.title
redirect_to :action => 'show', :project_id => @project, :id => @page.title
return
end
@content.attributes = params[:content]
@@ -105,12 +121,15 @@ class WikiController < ApplicationController
attachments = Attachment.attach_files(@page, params[:attachments])
render_attachment_warning_if_needed(@page)
call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
redirect_to :action => 'show', :project_id => @project, :page => @page.title
redirect_to :action => 'show', :project_id => @project, :id => @page.title
else
render :action => 'edit'
end
rescue ActiveRecord::StaleObjectError
# Optimistic locking exception
flash[:error] = l(:notice_locking_conflict)
flash.now[:error] = l(:notice_locking_conflict)
render :action => 'edit'
end
# rename a page
@@ -121,13 +140,13 @@ class WikiController < ApplicationController
@original_title = @page.pretty_title
if request.post? && @page.update_attributes(params[:wiki_page])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'show', :project_id => @project, :page => @page.title
redirect_to :action => 'show', :project_id => @project, :id => @page.title
end
end
def protect
@page.update_attribute :protected, params[:protected]
redirect_to :action => 'show', :project_id => @project, :page => @page.title
redirect_to :action => 'show', :project_id => @project, :id => @page.title
end
# show page history
@@ -153,7 +172,8 @@ class WikiController < ApplicationController
@annotate = @page.annotate(params[:version])
render_404 unless @annotate
end
verify :method => :delete, :only => [:destroy], :redirect_to => { :action => :show }
# Removes a wiki page and its history
# Children can be either set as root pages, removed or reassigned to another parent page
def destroy
@@ -180,7 +200,7 @@ class WikiController < ApplicationController
end
end
@page.destroy
redirect_to :action => 'page_index', :project_id => @project
redirect_to :action => 'index', :project_id => @project
end
# Export wiki to a single html file
@@ -190,20 +210,16 @@ class WikiController < ApplicationController
export = render_to_string :action => 'export_multiple', :layout => false
send_data(export, :type => 'text/html', :filename => "wiki.html")
else
redirect_to :action => 'show', :project_id => @project, :page => nil
redirect_to :action => 'show', :project_id => @project, :id => nil
end
end
def page_index
load_pages_grouped_by_date_without_content
end
def date_index
load_pages_grouped_by_date_without_content
end
def preview
page = @wiki.find_page(params[:page])
page = @wiki.find_page(params[:id])
# page is nil when previewing a new page
return render_403 unless page.nil? || editable?(page)
if page
@@ -218,7 +234,7 @@ class WikiController < ApplicationController
return render_403 unless editable?
attachments = Attachment.attach_files(@page, params[:attachments])
render_attachment_warning_if_needed(@page)
redirect_to :action => 'show', :page => @page.title
redirect_to :action => 'show', :id => @page.title, :project_id => @project
end
private
@@ -233,7 +249,7 @@ private
# Finds the requested page and returns a 404 error if it doesn't exist
def find_existing_page
@page = @wiki.find_page(params[:page])
@page = @wiki.find_page(params[:id])
render_404 if @page.nil?
end

View File

@@ -1,5 +1,5 @@
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
# Redmine - project management software
# Copyright (C) 2006-2010 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -104,8 +104,10 @@ module ApplicationHelper
# * :text - Link text (default to the formatted revision)
def link_to_revision(revision, project, options={})
text = options.delete(:text) || format_revision(revision)
rev = revision.respond_to?(:identifier) ? revision.identifier : revision
link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => revision}, :title => l(:label_revision_id, revision))
link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => rev},
:title => l(:label_revision_id, format_revision(revision)))
end
# Generates a link to a project if active
@@ -177,7 +179,7 @@ module ApplicationHelper
content << "<ul class=\"pages-hierarchy\">\n"
pages[node].each do |page|
content << "<li>"
content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :page => page.title},
content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title},
:title => (page.respond_to?(:updated_on) ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
content << "\n" + render_page_hierarchy(pages, page.id) if pages[page.id]
content << "</li>\n"
@@ -238,15 +240,10 @@ module ApplicationHelper
end
# Yields the given block for each project with its level in the tree
#
# Wrapper for Project#project_tree
def project_tree(projects, &block)
ancestors = []
projects.sort_by(&:lft).each do |project|
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
ancestors.pop
end
yield project, ancestors.size
ancestors << project
end
Project.project_tree(projects, &block)
end
def project_nested_ul(projects, &block)
@@ -454,12 +451,19 @@ module ApplicationHelper
only_path = options.delete(:only_path) == false ? false : true
text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) { |macro, args| exec_macro(macro, obj, args) }
parse_non_pre_blocks(text) do |text|
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links].each do |method_name|
@parsed_headings = []
text = parse_non_pre_blocks(text) do |text|
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_headings].each do |method_name|
send method_name, text, project, obj, attr, only_path, options
end
end
if @parsed_headings.any?
replace_toc(text, @parsed_headings)
end
text
end
def parse_non_pre_blocks(text)
@@ -529,7 +533,7 @@ module ApplicationHelper
esc, all, page, title = $1, $2, $3, $5
if esc.nil?
if page =~ /^([^\:]+)\:(.*)$/
link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
link_project = Project.find_by_identifier($1) || Project.find_by_name($1)
page = $2
title ||= $1 if page.blank?
end
@@ -546,7 +550,8 @@ module ApplicationHelper
when :local; "#{title}.html"
when :anchor; "##{title}" # used for single-file wiki export
else
url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project, :page => Wiki.titleize(page), :anchor => anchor)
wiki_page_id = page.present? ? Wiki.titleize(page) : nil
url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project, :id => wiki_page_id, :anchor => anchor)
end
link_to((title || page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
else
@@ -677,6 +682,55 @@ module ApplicationHelper
leading + (link || "#{prefix}#{sep}#{identifier}")
end
end
HEADING_RE = /<h(1|2|3|4)( [^>]+)?>(.+?)<\/h(1|2|3|4)>/i unless const_defined?(:HEADING_RE)
# Headings and TOC
# Adds ids and links to headings unless options[:headings] is set to false
def parse_headings(text, project, obj, attr, only_path, options)
return if options[:headings] == false
text.gsub!(HEADING_RE) do
level, attrs, content = $1.to_i, $2, $3
item = strip_tags(content).strip
anchor = item.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
@parsed_headings << [level, anchor, item]
"<h#{level} #{attrs} id=\"#{anchor}\">#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a></h#{level}>"
end
end
TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
# Renders the TOC with given headings
def replace_toc(text, headings)
text.gsub!(TOC_RE) do
if headings.empty?
''
else
div_class = 'toc'
div_class << ' right' if $1 == '>'
div_class << ' left' if $1 == '<'
out = "<ul class=\"#{div_class}\"><li>"
root = headings.map(&:first).min
current = root
started = false
headings.each do |level, anchor, item|
if level > current
out << '<ul><li>' * (level - current)
elsif level < current
out << "</li></ul>\n" * (current - level) + "</li><li>"
elsif started
out << '</li><li>'
end
out << "<a href=\"##{anchor}\">#{item}</a>"
current = level
started = true
end
out << '</li></ul>' * (current - root)
out << '</li></ul>'
end
end
end
# Same as Rails' simple_format helper without using paragraphs
def simple_format_without_paragraph(text)
@@ -826,7 +880,29 @@ module ApplicationHelper
def favicon
"<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />"
end
# Returns true if arg is expected in the API response
def include_in_api_response?(arg)
unless @included_in_api_response
param = params[:include]
@included_in_api_response = param.is_a?(Array) ? param.collect(&:to_s) : param.to_s.split(',')
@included_in_api_response.collect!(&:strip)
end
@included_in_api_response.include?(arg.to_s)
end
# Returns options or nil if nometa param or X-Redmine-Nometa header
# was set in the request
def api_meta(options)
if params[:nometa].present? || request.headers['X-Redmine-Nometa']
# compatibility mode for activeresource clients that raise
# an error when unserializing an array with attributes
nil
else
options
end
end
private
def wiki_helper

View File

@@ -1,5 +1,5 @@
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
# 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
@@ -37,7 +37,7 @@ module CustomFieldsHelper
field_id = "#{name}_custom_field_values_#{custom_field.id}"
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
case field_format.edit_as
case field_format.try(:edit_as)
when "date"
text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
calendar_for(field_id)
@@ -72,7 +72,7 @@ module CustomFieldsHelper
field_name = "#{name}[custom_field_values][#{custom_field.id}]"
field_id = "#{name}_custom_field_values_#{custom_field.id}"
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
case field_format.edit_as
case field_format.try(:edit_as)
when "date"
text_field_tag(field_name, '', :id => field_id, :size => 10) +
calendar_for(field_id)
@@ -104,4 +104,15 @@ module CustomFieldsHelper
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
custom_values.each do |custom_value|
api.custom_field :id => custom_value.custom_field_id, :name => custom_value.custom_field.name do
api.value custom_value.value
end
end
end unless custom_values.empty?
end
end

View File

@@ -16,6 +16,31 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module GanttHelper
def gantt_zoom_link(gantt, in_or_out)
case in_or_out
when :in
if gantt.zoom < 4
link_to_remote(l(:text_zoom_in),
{:url => gantt.params.merge(:zoom => (gantt.zoom+1)), :method => :get, :update => 'content'},
{:href => url_for(gantt.params.merge(:zoom => (gantt.zoom+1))),
:class => 'icon icon-zoom-in'})
else
content_tag('span', l(:text_zoom_in), :class => 'icon icon-zoom-in')
end
when :out
if gantt.zoom > 1
link_to_remote(l(:text_zoom_out),
{:url => gantt.params.merge(:zoom => (gantt.zoom-1)), :method => :get, :update => 'content'},
{:href => url_for(gantt.params.merge(:zoom => (gantt.zoom-1))),
:class => 'icon icon-zoom-out'})
else
content_tag('span', l(:text_zoom_out), :class => 'icon icon-zoom-out')
end
end
end
def number_of_issues_on_versions(gantt)
versions = gantt.events.collect {|event| (event.is_a? Version) ? event : nil}.compact

View File

@@ -189,6 +189,20 @@ module IssuesHelper
end
end
# Renders issue children recursively
def render_api_issue_children(issue, api)
return if issue.leaf?
api.array :children do
issue.children.each do |child|
api.issue(:id => child.id) do
api.tracker(:id => child.tracker_id, :name => child.tracker.name) unless child.tracker.nil?
api.subject child.subject
render_api_issue_children(child, api)
end
end
end
end
def issues_to_csv(issues, project = nil)
ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
decimal_separator = l(:general_csv_decimal_separator)
@@ -246,30 +260,4 @@ module IssuesHelper
end
export
end
def gantt_zoom_link(gantt, in_or_out)
img_attributes = {:style => 'height:1.4em; width:1.4em; margin-left: 3px;'} # em for accessibility
case in_or_out
when :in
if gantt.zoom < 4
link_to_remote(l(:text_zoom_in) + image_tag('zoom_in.png', img_attributes.merge(:alt => 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)))})
else
l(:text_zoom_in) +
image_tag('zoom_in_g.png', img_attributes.merge(:alt => l(:text_zoom_in)))
end
when :out
if gantt.zoom > 1
link_to_remote(l(:text_zoom_out) + image_tag('zoom_out.png', img_attributes.merge(:alt => 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)))})
else
l(:text_zoom_out) +
image_tag('zoom_out_g.png', img_attributes.merge(:alt => l(:text_zoom_out)))
end
end
end
end

View File

@@ -78,10 +78,9 @@ module QueriesHelper
# Give it a name, required to be valid
@query = Query.new(:name => "_")
@query.project = @project
if params[:fields] and params[:fields].is_a? Array
params[:fields].each do |field|
@query.add_filter(field, params[:operators][field], params[:values][field])
end
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]

View File

@@ -18,8 +18,12 @@
require 'iconv'
module RepositoriesHelper
def format_revision(txt)
txt.to_s[0,8]
def format_revision(revision)
if revision.respond_to? :format_identifier
revision.format_identifier
else
revision.to_s
end
end
def truncate_at_line_break(text, length = 255)
@@ -87,7 +91,7 @@ module RepositoriesHelper
:action => 'show',
:id => @project,
:path => path_param,
:rev => @changeset.revision)
:rev => @changeset.identifier)
output << "<li class='#{style}'>#{text}</li>"
output << render_changes_tree(s)
elsif c = tree[file][:c]
@@ -97,13 +101,13 @@ module RepositoriesHelper
:action => 'entry',
:id => @project,
:path => path_param,
:rev => @changeset.revision) unless c.action == 'D'
:rev => @changeset.identifier) unless c.action == 'D'
text << " - #{c.revision}" unless c.revision.blank?
text << ' (' + link_to('diff', :controller => 'repositories',
:action => 'diff',
:id => @project,
:path => path_param,
:rev => @changeset.revision) + ') ' if c.action == 'M'
:rev => @changeset.identifier) + ') ' if c.action == 'M'
text << ' ' + content_tag('span', c.from_path, :class => 'copied-from') unless c.from_path.blank?
output << "<li class='#{style}'>#{text}</li>"
end

View File

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

View File

@@ -33,6 +33,10 @@ module UsersHelper
options
end
def user_mail_notification_options(user)
user.valid_notification_options.collect {|o| [l(o.last), o.first]}
end
def change_status_link(user)
url = {:controller => 'users', :action => 'update', :id => user, :page => params[:page], :status => params[:status], :tab => nil}

View File

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

View File

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

View File

@@ -23,10 +23,10 @@ class Changeset < ActiveRecord::Base
has_many :changes, :dependent => :delete_all
has_and_belongs_to_many :issues
acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.revision}" + (o.short_comments.blank? ? '' : (': ' + o.short_comments))},
acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.format_identifier}" + (o.short_comments.blank? ? '' : (': ' + o.short_comments))},
:description => :long_comments,
:datetime => :committed_on,
:url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :rev => o.revision}}
:url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :rev => o.identifier}}
acts_as_searchable :columns => 'comments',
:include => {:repository => :project},
@@ -47,6 +47,15 @@ class Changeset < ActiveRecord::Base
def revision=(r)
write_attribute :revision, (r.nil? ? nil : r.to_s)
end
# Returns the identifier of this changeset; depending on repository backends
def identifier
if repository.class.respond_to? :changeset_identifier
repository.class.changeset_identifier self
else
revision.to_s
end
end
def comments=(comment)
write_attribute(:comments, Changeset.normalize_comments(comment))
@@ -56,6 +65,15 @@ class Changeset < ActiveRecord::Base
self.commit_date = date
super
end
# Returns the readable identifier
def format_identifier
if repository.class.respond_to? :format_changeset_identifier
repository.class.format_changeset_identifier self
else
identifier
end
end
def committer=(arg)
write_attribute(:committer, self.class.to_utf8(arg.to_s))
@@ -77,52 +95,42 @@ class Changeset < ActiveRecord::Base
scan_comment_for_issue_ids
end
TIMELOG_RE = /
(
((\d+)(h|hours?))((\d+)(m|min)?)?
|
((\d+)(h|hours?|m|min))
|
(\d+):(\d+)
|
(\d+([\.,]\d+)?)h?
)
/x
def scan_comment_for_issue_ids
return if comments.blank?
# keywords used to reference issues
ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip)
ref_keywords_any = ref_keywords.delete('*')
# keywords used to fix issues
fix_keywords = Setting.commit_fix_keywords.downcase.split(",").collect(&:strip)
kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|")
return if kw_regexp.blank?
referenced_issues = []
if ref_keywords.delete('*')
# find any issue ID in the comments
target_issue_ids = []
comments.scan(%r{([\s\(\[,-]|^)#(\d+)(?=[[:punct:]]|\s|<|$)}).each { |m| target_issue_ids << m[1] }
referenced_issues += find_referenced_issues_by_id(target_issue_ids)
end
comments.scan(Regexp.new("(#{kw_regexp})[\s:]+(([\s,;&]*#?\\d+)+)", Regexp::IGNORECASE)).each do |match|
action = match[0]
target_issue_ids = match[1].scan(/\d+/)
target_issues = find_referenced_issues_by_id(target_issue_ids)
if fix_keywords.include?(action.downcase) && fix_status = IssueStatus.find_by_id(Setting.commit_fix_status_id)
# update status of issues
logger.debug "Issues fixed by changeset #{self.revision}: #{issue_ids.join(', ')}." if logger && logger.debug?
target_issues.each do |issue|
# the issue may have been updated by the closure of another one (eg. duplicate)
issue.reload
# don't change the status is the issue is closed
next if issue.status.is_closed?
csettext = "r#{self.revision}"
if self.scmid && (! (csettext =~ /^r[0-9]+$/))
csettext = "commit:\"#{self.scmid}\""
end
journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, csettext))
issue.status = fix_status
unless Setting.commit_fix_done_ratio.blank?
issue.done_ratio = Setting.commit_fix_done_ratio.to_i
end
Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update,
{ :changeset => self, :issue => issue })
issue.save
comments.scan(/([\s\(\[,-]|^)((#{kw_regexp})[\s:]+)?(#\d+(\s+@#{TIMELOG_RE})?([\s,;&]+#\d+(\s+@#{TIMELOG_RE})?)*)(?=[[:punct:]]|\s|<|$)/i) do |match|
action, refs = match[2], match[3]
next unless action.present? || ref_keywords_any
refs.scan(/#(\d+)(\s+@#{TIMELOG_RE})?/).each do |m|
issue, hours = find_referenced_issue_by_id(m[0].to_i), m[2]
if issue
referenced_issues << issue
fix_issue(issue) if fix_keywords.include?(action.to_s.downcase)
log_time(issue, hours) if hours && Setting.commit_logtime_enabled?
end
end
referenced_issues += target_issues
end
referenced_issues.uniq!
@@ -136,6 +144,14 @@ class Changeset < ActiveRecord::Base
def long_comments
@long_comments || split_comments.last
end
def text_tag
if scmid?
"commit:#{scmid}"
else
"r#{revision}"
end
end
# Returns the previous changeset
def previous
@@ -163,13 +179,64 @@ class Changeset < ActiveRecord::Base
private
# Finds issues that can be referenced by the commit message
# i.e. issues that belong to the repository project, a subproject or a parent project
def find_referenced_issues_by_id(ids)
return [] if ids.compact.empty?
Issue.find_all_by_id(ids, :include => :project).select {|issue|
project == issue.project || project.is_ancestor_of?(issue.project) || project.is_descendant_of?(issue.project)
}
# Finds an issue that can be referenced by the commit message
# i.e. an issue that belong to the repository project, a subproject or a parent project
def find_referenced_issue_by_id(id)
return nil if id.blank?
issue = Issue.find_by_id(id.to_i, :include => :project)
if issue
unless 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 macthes commit_fix_status_id setting (#{Setting.commit_fix_status_id})") if logger
return issue
end
# the issue may have been updated by the closure of another one (eg. duplicate)
issue.reload
# don't change the status is the issue is closed
return if issue.status && issue.status.is_closed?
journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, text_tag))
issue.status = status
unless Setting.commit_fix_done_ratio.blank?
issue.done_ratio = Setting.commit_fix_done_ratio.to_i
end
Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update,
{ :changeset => self, :issue => issue })
unless issue.save
logger.warn("Issue ##{issue.id} could not be saved by changeset #{id}: #{issue.errors.full_messages}") if logger
end
issue
end
def log_time(issue, hours)
time_entry = TimeEntry.new(
:user => user,
:hours => hours,
:issue => issue,
:spent_on => commit_date,
:comments => l(:text_time_logged_by_changeset, :value => text_tag, :locale => Setting.default_language)
)
time_entry.activity = log_time_activity unless log_time_activity.nil?
unless time_entry.save
logger.warn("TimeEntry could not be created by changeset #{id}: #{time_entry.errors.full_messages}") if logger
end
time_entry
end
def log_time_activity
if Setting.commit_logtime_activity_id.to_i > 0
TimeEntryActivity.find_by_id(Setting.commit_logtime_activity_id.to_i)
end
end
def split_comments

View File

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

View File

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

View File

@@ -16,6 +16,8 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class Issue < ActiveRecord::Base
include Redmine::SafeAttributes
belongs_to :project
belongs_to :tracker
belongs_to :status, :class_name => 'IssueStatus', :foreign_key => 'status_id'
@@ -32,7 +34,7 @@ class Issue < ActiveRecord::Base
has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all
has_many :relations_to, :class_name => 'IssueRelation', :foreign_key => 'issue_to_id', :dependent => :delete_all
acts_as_nested_set :scope => 'root_id'
acts_as_nested_set :scope => 'root_id', :dependent => :destroy
acts_as_attachable :after_remove => :attachment_removed
acts_as_customizable
acts_as_watchable
@@ -68,8 +70,7 @@ class Issue < ActiveRecord::Base
:conditions => ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"]
named_scope :for_gantt, lambda {
{
:include => [:tracker, :status, :assigned_to, :priority, :project, :fixed_version],
:order => "#{Issue.table_name}.due_date ASC, #{Issue.table_name}.start_date ASC, #{Issue.table_name}.id ASC"
:include => [:tracker, :status, :assigned_to, :priority, :project, :fixed_version]
}
}
@@ -88,7 +89,6 @@ class Issue < ActiveRecord::Base
before_create :default_assign
before_save :close_duplicates, :update_done_ratio_from_issue_status
after_save :reschedule_following_issues, :update_nested_set_attributes, :update_parent_attributes, :create_journal
after_destroy :destroy_children
after_destroy :update_parent_attributes
# Returns true if usr or current user is allowed to view the issue
@@ -215,32 +215,47 @@ class Issue < ActiveRecord::Base
write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h)
end
SAFE_ATTRIBUTES = %w(
tracker_id
status_id
parent_issue_id
category_id
assigned_to_id
priority_id
fixed_version_id
subject
description
start_date
due_date
done_ratio
estimated_hours
custom_field_values
lock_version
) unless const_defined?(:SAFE_ATTRIBUTES)
safe_attributes 'tracker_id',
'status_id',
'parent_issue_id',
'category_id',
'assigned_to_id',
'priority_id',
'fixed_version_id',
'subject',
'description',
'start_date',
'due_date',
'done_ratio',
'estimated_hours',
'custom_field_values',
'custom_fields',
'lock_version',
:if => lambda {|issue, user| issue.new_record? || user.allowed_to?(:edit_issues, issue.project) }
safe_attributes 'status_id',
'assigned_to_id',
'fixed_version_id',
'done_ratio',
:if => lambda {|issue, user| issue.new_statuses_allowed_to(user).any? }
# Safely sets attributes
# Should be called from controllers instead of #attributes=
# attr_accessible is too rough because we still want things like
# Issue.new(:project => foo) to work
# TODO: move workflow/permission checks from controllers to here
def safe_attributes=(attrs, user=User.current)
return if attrs.nil?
attrs = attrs.reject {|k,v| !SAFE_ATTRIBUTES.include?(k)}
return unless attrs.is_a?(Hash)
# User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
attrs = delete_unsafe_attributes(attrs, user)
return if attrs.empty?
# Tracker must be set before since new_statuses_allowed_to depends on it.
if t = attrs.delete('tracker_id')
self.tracker_id = t
end
if attrs['status_id']
unless new_statuses_allowed_to(user).collect(&:id).include?(attrs['status_id'].to_i)
attrs.delete('status_id')
@@ -255,7 +270,7 @@ class Issue < ActiveRecord::Base
if !user.allowed_to?(:manage_subtasks, project)
attrs.delete('parent_issue_id')
elsif !attrs['parent_issue_id'].blank?
attrs.delete('parent_issue_id') unless Issue.visible(user).exists?(attrs['parent_issue_id'])
attrs.delete('parent_issue_id') unless Issue.visible(user).exists?(attrs['parent_issue_id'].to_i)
end
end
@@ -739,14 +754,6 @@ class Issue < ActiveRecord::Base
end
end
def destroy_children
unless leaf?
children.each do |child|
child.destroy
end
end
end
# Update issues so their versions are not pointing to a
# fixed_version that is not shared with the issue's project
def self.update_versions(conditions=nil)

View File

@@ -84,14 +84,15 @@ class IssueRelation < ActiveRecord::Base
def set_issue_to_dates
soonest_start = self.successor_soonest_start
if soonest_start
if soonest_start && issue_to
issue_to.reschedule_after(soonest_start)
end
end
def successor_soonest_start
return nil unless (TYPE_PRECEDES == self.relation_type) && (issue_from.start_date || issue_from.due_date)
(issue_from.due_date || issue_from.start_date) + 1 + delay
if (TYPE_PRECEDES == self.relation_type) && delay && issue_from && (issue_from.start_date || issue_from.due_date)
(issue_from.due_date || issue_from.start_date) + 1 + delay
end
end
def <=>(relation)

View File

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

View File

@@ -85,7 +85,7 @@ class Mailer < ActionMailer::Base
subject l(:mail_subject_reminder, :count => issues.size, :days => days)
body :issues => issues,
:days => days,
:issues_url => url_for(:controller => 'issues', :action => 'index', :set_filter => 1, :assigned_to_id => user.id, :sort_key => 'due_date', :sort_order => 'asc')
:issues_url => url_for(:controller => 'issues', :action => 'index', :set_filter => 1, :assigned_to_id => user.id, :sort => 'due_date:asc')
render_multipart('reminder', body)
end
@@ -114,11 +114,11 @@ class Mailer < ActionMailer::Base
added_to_url = ''
case container.class.name
when 'Project'
added_to_url = url_for(:controller => 'projects', :action => 'list_files', :id => container)
added_to_url = url_for(:controller => 'files', :action => 'index', :project_id => container)
added_to = "#{l(:label_project)}: #{container}"
recipients container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)}.collect {|u| u.mail}
when 'Version'
added_to_url = url_for(:controller => 'projects', :action => 'list_files', :id => container.project_id)
added_to_url = url_for(:controller => 'files', :action => 'index', :project_id => container.project)
added_to = "#{l(:label_version)}: #{container.name}"
recipients container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)}.collect {|u| u.mail}
when 'Document'
@@ -178,9 +178,9 @@ class Mailer < ActionMailer::Base
message_id wiki_content
recipients wiki_content.recipients
cc(wiki_content.page.wiki.watcher_recipients - recipients)
subject "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_added, :page => wiki_content.page.pretty_title)}"
subject "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_added, :id => wiki_content.page.pretty_title)}"
body :wiki_content => wiki_content,
:wiki_content_url => url_for(:controller => 'wiki', :action => 'index', :id => wiki_content.project, :page => wiki_content.page.title)
:wiki_content_url => url_for(:controller => 'wiki', :action => 'show', :project_id => wiki_content.project, :id => wiki_content.page.title)
render_multipart('wiki_content_added', body)
end
@@ -195,10 +195,10 @@ class Mailer < ActionMailer::Base
message_id wiki_content
recipients wiki_content.recipients
cc(wiki_content.page.wiki.watcher_recipients + wiki_content.page.watcher_recipients - recipients)
subject "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_updated, :page => wiki_content.page.pretty_title)}"
subject "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_updated, :id => wiki_content.page.pretty_title)}"
body :wiki_content => wiki_content,
:wiki_content_url => url_for(:controller => 'wiki', :action => 'index', :id => wiki_content.project, :page => wiki_content.page.title),
:wiki_diff_url => url_for(:controller => 'wiki', :action => 'diff', :id => wiki_content.project, :page => wiki_content.page.title, :version => wiki_content.version)
:wiki_content_url => url_for(:controller => 'wiki', :action => 'show', :project_id => wiki_content.project, :id => wiki_content.page.title),
:wiki_diff_url => url_for(:controller => 'wiki', :action => 'diff', :project_id => wiki_content.project, :id => wiki_content.page.title, :version => wiki_content.version)
render_multipart('wiki_content_updated', body)
end
@@ -326,7 +326,7 @@ class Mailer < ActionMailer::Base
:conditions => s.conditions
).group_by(&:assigned_to)
issues_by_assignee.each do |assignee, issues|
deliver_reminder(assignee, issues, days) unless assignee.nil?
deliver_reminder(assignee, issues, days) if assignee && assignee.active?
end
end

View File

@@ -29,6 +29,11 @@ class News < ActiveRecord::Base
acts_as_activity_provider :find_options => {:include => [:project, :author]},
:author_key => :author_id
named_scope :visible, lambda {|*args| {
:include => :project,
:conditions => Project.allowed_to_condition(args.first || User.current, :view_news)
}}
def visible?(user=User.current)
!user.nil? && user.allowed_to?(:view_news, project)
end

View File

@@ -16,10 +16,15 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class Project < ActiveRecord::Base
include Redmine::SafeAttributes
# Project statuses
STATUS_ACTIVE = 1
STATUS_ARCHIVED = 9
# Maximum length for project identifiers
IDENTIFIER_MAX_LENGTH = 100
# Specific overidden Activities
has_many :time_entry_activities
has_many :members, :include => [:user, :roles], :conditions => "#{User.table_name}.type='User' AND #{User.table_name}.status=#{User::STATUS_ACTIVE}"
@@ -38,7 +43,7 @@ class Project < ActiveRecord::Base
has_many :time_entries, :dependent => :delete_all
has_many :queries, :dependent => :delete_all
has_many :documents, :dependent => :destroy
has_many :news, :dependent => :delete_all, :include => :author
has_many :news, :dependent => :destroy, :include => :author
has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name"
has_many :boards, :dependent => :destroy, :order => "position ASC"
has_one :repository, :dependent => :destroy
@@ -51,7 +56,7 @@ class Project < ActiveRecord::Base
:join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}",
:association_foreign_key => 'custom_field_id'
acts_as_nested_set :order => 'name'
acts_as_nested_set :order => 'name', :dependent => :destroy
acts_as_attachable :view_permission => :view_files,
:delete_permission => :manage_files
@@ -61,26 +66,44 @@ class Project < ActiveRecord::Base
:url => Proc.new {|o| {:controller => 'projects', :action => 'show', :id => o}},
:author => nil
attr_protected :status, :enabled_module_names
attr_protected :status
validates_presence_of :name, :identifier
validates_uniqueness_of :name, :identifier
validates_uniqueness_of :identifier
validates_associated :repository, :wiki
validates_length_of :name, :maximum => 30
validates_length_of :name, :maximum => 255
validates_length_of :homepage, :maximum => 255
validates_length_of :identifier, :in => 1..20
validates_length_of :identifier, :in => 1..IDENTIFIER_MAX_LENGTH
# donwcase letters, digits, dashes but not digits only
validates_format_of :identifier, :with => /^(?!\d+$)[a-z0-9\-]*$/, :if => Proc.new { |p| p.identifier_changed? }
# reserved words
validates_exclusion_of :identifier, :in => %w( new )
before_destroy :delete_all_members, :destroy_children
before_destroy :delete_all_members
named_scope :has_module, lambda { |mod| { :conditions => ["#{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name=?)", mod.to_s] } }
named_scope :active, { :conditions => "#{Project.table_name}.status = #{STATUS_ACTIVE}"}
named_scope :all_public, { :conditions => { :is_public => true } }
named_scope :visible, lambda { { :conditions => Project.visible_by(User.current) } }
def initialize(attributes = nil)
super
initialized = (attributes || {}).stringify_keys
if !initialized.key?('identifier') && Setting.sequential_project_identifiers?
self.identifier = Project.next_identifier
end
if !initialized.key?('is_public')
self.is_public = Setting.default_projects_public?
end
if !initialized.key?('enabled_module_names')
self.enabled_module_names = Setting.default_projects_modules
end
if !initialized.key?('trackers') && !initialized.key?('tracker_ids')
self.trackers = Tracker.all
end
end
def identifier=(identifier)
super unless identifier_frozen?
end
@@ -220,6 +243,10 @@ class Project < ActiveRecord::Base
self.status == STATUS_ACTIVE
end
def archived?
self.status == STATUS_ARCHIVED
end
# Archives the project and its descendants
def archive
# Check that there is no issue of a non descendant project that is assigned
@@ -424,24 +451,20 @@ class Project < ActiveRecord::Base
# The earliest start date of a project, based on it's issues and versions
def start_date
if module_enabled?(:issue_tracking)
[
issues.minimum('start_date'),
shared_versions.collect(&:effective_date),
shared_versions.collect {|v| v.fixed_issues.minimum('start_date')}
].flatten.compact.min
end
[
issues.minimum('start_date'),
shared_versions.collect(&:effective_date),
shared_versions.collect(&:start_date)
].flatten.compact.min
end
# The latest due date of an issue or version
def due_date
if module_enabled?(:issue_tracking)
[
issues.maximum('due_date'),
shared_versions.collect(&:effective_date),
shared_versions.collect {|v| v.fixed_issues.maximum('due_date')}
].flatten.compact.max
end
[
issues.maximum('due_date'),
shared_versions.collect(&:effective_date),
shared_versions.collect {|v| v.fixed_issues.maximum('due_date')}
].flatten.compact.max
end
def overdue?
@@ -485,16 +508,31 @@ class Project < ActiveRecord::Base
def enabled_module_names=(module_names)
if module_names && module_names.is_a?(Array)
module_names = module_names.collect(&:to_s)
# remove disabled modules
enabled_modules.each {|mod| mod.destroy unless module_names.include?(mod.name)}
# add new modules
module_names.reject {|name| module_enabled?(name)}.each {|name| enabled_modules << EnabledModule.new(:name => name)}
module_names = module_names.collect(&:to_s).reject(&:blank?)
self.enabled_modules = module_names.collect {|name| enabled_modules.detect {|mod| mod.name == name} || EnabledModule.new(:name => name)}
else
enabled_modules.clear
end
end
# Returns an array of the enabled modules names
def enabled_module_names
enabled_modules.collect(&:name)
end
safe_attributes 'name',
'description',
'homepage',
'is_public',
'identifier',
'custom_field_values',
'custom_fields',
'tracker_ids',
'issue_custom_field_ids'
safe_attributes 'enabled_module_names',
:if => lambda {|project, user| project.new_record? || user.allowed_to?(:select_project_modules, project) }
# Returns an array of projects that are in this project's hierarchy
#
# Example: parents, children, siblings
@@ -565,16 +603,21 @@ class Project < ActiveRecord::Base
return nil
end
end
private
# Destroys children before destroying self
def destroy_children
children.each do |child|
child.destroy
# Yields the given block for each project with its level in the tree
def self.project_tree(projects, &block)
ancestors = []
projects.sort_by(&:lft).each do |project|
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
ancestors.pop
end
yield project, ancestors.size
ancestors << project
end
end
private
# Copies wiki from +project+
def copy_wiki(project)
# Check that the source project has a wiki first
@@ -650,12 +693,20 @@ class Project < ActiveRecord::Base
end
self.issues << new_issue
issues_map[issue.id] = new_issue
if new_issue.new_record?
logger.info "Project#copy_issues: issue ##{issue.id} could not be copied: #{new_issue.errors.full_messages}" if logger && logger.info
else
issues_map[issue.id] = new_issue unless new_issue.new_record?
end
end
# Relations after in case issues related each other
project.issues.each do |issue|
new_issue = issues_map[issue.id]
unless new_issue
# Issue was not copied
next
end
# Relations
issue.relations_from.each do |source_relation|
@@ -682,7 +733,12 @@ class Project < ActiveRecord::Base
# Copies members from +project+
def copy_members(project)
project.memberships.each do |member|
# Copy users first, then groups to handle members with inherited and given roles
members_to_copy = []
members_to_copy += project.memberships.select {|m| m.principal.is_a?(User)}
members_to_copy += project.memberships.select {|m| !m.principal.is_a?(User)}
members_to_copy.each do |member|
new_member = Member.new
new_member.attributes = member.attributes.dup.except("id", "project_id", "created_on")
# only copy non inherited roles

View File

@@ -187,10 +187,18 @@ class Query < ActiveRecord::Base
if project
user_values += project.users.sort.collect{|s| [s.name, s.id.to_s] }
else
project_ids = Project.all(:conditions => Project.visible_by(User.current)).collect(&:id)
if project_ids.any?
# members of the user's projects
user_values += User.active.find(:all, :conditions => ["#{User.table_name}.id IN (SELECT DISTINCT user_id FROM members WHERE project_id IN (?))", project_ids]).sort.collect{|s| [s.name, s.id.to_s] }
all_projects = Project.visible.all
if all_projects.any?
# members of visible projects
user_values += User.active.find(:all, :conditions => ["#{User.table_name}.id IN (SELECT DISTINCT user_id FROM members WHERE project_id IN (?))", all_projects.collect(&:id)]).sort.collect{|s| [s.name, s.id.to_s] }
# project filter
project_values = []
Project.project_tree(all_projects) do |p, level|
prefix = (level > 0 ? ('--' * level + ' ') : '')
project_values << ["#{prefix}#{p.name}", p.id.to_s]
end
@available_filters["project_id"] = { :type => :list, :order => 1, :values => project_values} unless project_values.empty?
end
end
@available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => user_values } unless user_values.empty?
@@ -225,12 +233,6 @@ class Query < ActiveRecord::Base
@available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => system_shared_versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] } }
end
add_custom_fields_filters(IssueCustomField.find(:all, :conditions => {:is_filter => true, :is_for_all => true}))
# project filter
project_values = Project.all(:conditions => Project.visible_by(User.current), :order => 'lft').map do |p|
pre = (p.level > 0 ? ('--' * p.level + ' ') : '')
["#{pre}#{p.name}",p.id.to_s]
end
@available_filters["project_id"] = { :type => :list, :order => 1, :values => project_values}
end
@available_filters
end
@@ -258,8 +260,10 @@ class Query < ActiveRecord::Base
# Add multiple filters using +add_filter+
def add_filters(fields, operators, values)
fields.each do |field|
add_filter(field, operators[field], values[field])
if fields.is_a?(Array) && operators.is_a?(Hash) && values.is_a?(Hash)
fields.each do |field|
add_filter(field, operators[field], values[field])
end
end
end
@@ -374,15 +378,15 @@ class Query < ActiveRecord::Base
# Returns true if the query is a grouped query
def grouped?
!group_by.blank?
!group_by_column.nil?
end
def group_by_column
groupable_columns.detect {|c| c.name.to_s == group_by}
groupable_columns.detect {|c| c.groupable && c.name.to_s == group_by}
end
def group_by_statement
group_by_column.groupable
group_by_column.try(:groupable)
end
def project_statement
@@ -562,9 +566,19 @@ class Query < ActiveRecord::Base
sql = ''
case operator
when "="
sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
if value.any?
sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
else
# IN an empty set
sql = "1=0"
end
when "!"
sql = "(#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} NOT IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + "))"
if value.any?
sql = "(#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} NOT IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + "))"
else
# NOT IN an empty set
sql = "1=1"
end
when "!*"
sql = "#{db_table}.#{db_field} IS NULL"
sql << " OR #{db_table}.#{db_field} = ''" if is_custom_filter

View File

@@ -91,12 +91,13 @@ class Repository < ActiveRecord::Base
def relative_path(path)
path
end
# Finds and returns a revision with a number or the beginning of a hash
def find_changeset_by_name(name)
return nil if name.blank?
changesets.find(:first, :conditions => (name.match(/^\d*$/) ? ["revision = ?", name.to_s] : ["revision LIKE ?", name + '%']))
end
def latest_changeset
@latest_changeset ||= changesets.find(:first)
end

View File

@@ -29,6 +29,16 @@ class Repository::Git < Repository
'Git'
end
# Returns the identifier for the given git changeset
def self.changeset_identifier(changeset)
changeset.scmid
end
# Returns the readable identifier for the given git changeset
def self.format_changeset_identifier(changeset)
changeset.revision[0, 8]
end
def branches
scm.branches
end
@@ -37,12 +47,31 @@ class Repository::Git < Repository
scm.tags
end
# In Git and Mercurial, revisions are not in date order.
# Mercurial fixed issues.
# * Redmine Takes Too Long On Large Mercurial Repository
# http://www.redmine.org/issues/3449
# * Sorting for changesets might go wrong on Mercurial repos
# http://www.redmine.org/issues/3567
# Database revision column is text, so Redmine can not sort by revision.
# Mercurial has revision number, and revision number guarantees revision order.
# Mercurial adapter uses "hg log -r 0:tip --limit 10"
# to get limited revisions from old to new.
# And Mercurial model stored revisions ordered by database id in database.
# So, Mercurial can use correct order revisions.
#
# But, Git 1.7.3.4 does not support --reverse with -n or --skip.
#
# With SCM's that have a sequential commit numbering, redmine is able to be
# clever and only fetch changesets going forward from the most recent one
# it knows about. However, with git, you never know if people have merged
# it knows about.
# However, with git, you never know if people have merged
# commits into the middle of the repository history, so we should parse
# the entire log. Since it's way too slow for large repositories, we only
# parse 1 week before the last known commit.
# the entire log.
#
# Since it's way too slow for large repositories,
# we only parse 1 week before the last known commit.
#
# The repository can still be fully reloaded by calling #clear_changesets
# before fetching changesets (eg. for offline resync)
def fetch_changesets

View File

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

View File

@@ -27,7 +27,7 @@ class TimeEntry < ActiveRecord::Base
acts_as_customizable
acts_as_event :title => Proc.new {|o| "#{l_hours(o.hours)} (#{(o.issue || o.project).event_title})"},
:url => Proc.new {|o| {:controller => 'timelog', :action => 'details', :project_id => o.project, :issue_id => o.issue}},
:url => Proc.new {|o| {:controller => 'timelog', :action => 'index', :project_id => o.project, :issue_id => o.issue}},
:author => :user,
:description => :comments
@@ -66,6 +66,9 @@ class TimeEntry < ActiveRecord::Base
# these attributes make time aggregations easier
def spent_on=(date)
super
if spent_on.is_a?(Time)
self.spent_on = spent_on.to_date
end
self.tyear = spent_on ? spent_on.year : nil
self.tmonth = spent_on ? spent_on.month : nil
self.tweek = spent_on ? Date.civil(spent_on.year, spent_on.month, spent_on.day).cweek : nil

View File

@@ -18,7 +18,8 @@
require "digest/sha1"
class User < Principal
include Redmine::SafeAttributes
# Account statuses
STATUS_ANONYMOUS = 0
STATUS_ACTIVE = 1
@@ -34,13 +35,13 @@ class User < Principal
}
MAIL_NOTIFICATION_OPTIONS = [
[:all, :label_user_mail_option_all],
[:selected, :label_user_mail_option_selected],
[:none, :label_user_mail_option_none],
[:only_my_events, :label_user_mail_option_only_my_events],
[:only_assigned, :label_user_mail_option_only_assigned],
[:only_owner, :label_user_mail_option_only_owner]
]
['all', :label_user_mail_option_all],
['selected', :label_user_mail_option_selected],
['only_my_events', :label_user_mail_option_only_my_events],
['only_assigned', :label_user_mail_option_only_assigned],
['only_owner', :label_user_mail_option_only_owner],
['none', :label_user_mail_option_none]
]
has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
:after_remove => Proc.new {|user, group| group.user_removed(user)}
@@ -59,7 +60,7 @@ class User < Principal
attr_accessor :password, :password_confirmation
attr_accessor :last_before_login_on
# Prevents unauthorized assignments
attr_protected :login, :admin, :password, :password_confirmation, :hashed_password, :group_ids
attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }, :case_sensitive => false
@@ -72,6 +73,7 @@ class User < Principal
validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
validates_length_of :mail, :maximum => 60, :allow_nil => true
validates_confirmation_of :password, :allow_nil => true
validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
def before_create
self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
@@ -259,12 +261,16 @@ class User < Principal
notified_projects_ids
end
# Only users that belong to more than 1 project can select projects for which they are notified
def valid_notification_options
self.class.valid_notification_options(self)
end
# Only users that belong to more than 1 project can select projects for which they are notified
def self.valid_notification_options(user=nil)
# Note that @user.membership.size would fail since AR ignores
# :include association option when doing a count
if memberships.length < 1
MAIL_NOTIFICATION_OPTIONS.delete_if {|option| option.first == :selected}
if user.nil? || user.memberships.length < 1
MAIL_NOTIFICATION_OPTIONS.reject {|option| option.first == 'selected'}
else
MAIL_NOTIFICATION_OPTIONS
end
@@ -390,32 +396,54 @@ class User < Principal
def allowed_to_globally?(action, options)
allowed_to?(action, nil, options.reverse_merge(:global => true))
end
safe_attributes 'login',
'firstname',
'lastname',
'mail',
'mail_notification',
'language',
'custom_field_values',
'custom_fields',
'identity_url'
safe_attributes 'status',
'auth_source_id',
:if => lambda {|user, current_user| current_user.admin?}
safe_attributes 'group_ids',
:if => lambda {|user, current_user| current_user.admin? && !user.new_record?}
# Utility method to help check if a user should be notified about an
# event.
#
# TODO: only supports Issue events currently
def notify_about?(object)
case mail_notification.to_sym
when :all
case mail_notification
when 'all'
true
when :selected
# Handled by the Project
when :none
false
when :only_my_events
when 'selected'
# user receives notifications for created/assigned issues on unselected projects
if object.is_a?(Issue) && (object.author == self || object.assigned_to == self)
true
else
false
end
when :only_assigned
when 'none'
false
when 'only_my_events'
if object.is_a?(Issue) && (object.author == self || object.assigned_to == self)
true
else
false
end
when 'only_assigned'
if object.is_a?(Issue) && object.assigned_to == self
true
else
false
end
when :only_owner
when 'only_owner'
if object.is_a?(Issue) && object.author == self
true
else

View File

@@ -43,7 +43,7 @@ class Version < ActiveRecord::Base
end
def start_date
effective_date
@start_date ||= fixed_issues.minimum('start_date')
end
def due_date
@@ -77,8 +77,7 @@ class Version < ActiveRecord::Base
def behind_schedule?
if completed_pourcent == 100
return false
elsif due_date && fixed_issues.present? && fixed_issues.minimum('start_date') # TODO: should use #start_date but that method is wrong...
start_date = fixed_issues.minimum('start_date')
elsif due_date && start_date
done_date = start_date + ((due_date - start_date+1)* completed_pourcent/100).floor
return done_date <= Date.today
else

View File

@@ -46,10 +46,10 @@ class Wiki < ActiveRecord::Base
def find_page(title, options = {})
title = start_page if title.blank?
title = Wiki.titleize(title)
page = pages.find_by_title(title)
page = pages.first(:conditions => ["LOWER(title) = LOWER(?)", title])
if !page && !(options[:with_redirect] == false)
# search for a redirect
redirect = redirects.find_by_title(title)
redirect = redirects.first(:conditions => ["LOWER(title) = LOWER(?)", title])
page = find_page(redirect.redirects_to, :with_redirect => false) if redirect
end
page

View File

@@ -54,7 +54,7 @@ class WikiContent < ActiveRecord::Base
:description => :comments,
:datetime => :updated_on,
:type => 'wiki-page',
:url => Proc.new {|o| {:controller => 'wiki', :id => o.page.wiki.project_id, :page => o.page.title, :version => o.version}}
:url => Proc.new {|o| {:controller => 'wiki', :action => 'show', :project_id => o.page.wiki.project, :id => o.page.title, :version => o.version}}
acts_as_activity_provider :type => 'wiki_edits',
:timestamp => "#{WikiContent.versioned_table_name}.updated_on",

View File

@@ -28,7 +28,7 @@ class WikiPage < ActiveRecord::Base
acts_as_event :title => Proc.new {|o| "#{l(:label_wiki)}: #{o.title}"},
:description => :text,
:datetime => :created_on,
:url => Proc.new {|o| {:controller => 'wiki', :id => o.wiki.project, :page => o.title}}
:url => Proc.new {|o| {:controller => 'wiki', :action => 'show', :project_id => o.wiki.project, :id => o.title}}
acts_as_searchable :columns => ['title', 'text'],
:include => [{:wiki => :project}, :content],
@@ -139,7 +139,7 @@ class WikiPage < ActiveRecord::Base
parent_page = t.blank? ? nil : self.wiki.find_page(t)
self.parent = parent_page
end
protected
def validate

View File

@@ -15,10 +15,10 @@
<tbody>
<% for source in @auth_sources %>
<tr class="<%= cycle("odd", "even") %>">
<td><%= link_to source.name, :action => 'edit', :id => source%></td>
<td align="center"><%= source.auth_method_name %></td>
<td align="center"><%= source.host %></td>
<td align="center"><%= source.users.count %></td>
<td><%= link_to(h(source.name), :action => 'edit', :id => source)%></td>
<td align="center"><%= h source.auth_method_name %></td>
<td align="center"><%= h source.host %></td>
<td align="center"><%= h source.users.count %></td>
<td class="buttons">
<%= link_to l(:button_test), :action => 'test_connection', :id => source %>
<%= link_to l(:button_delete), { :action => 'destroy', :id => source },

View File

@@ -69,4 +69,5 @@
<% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@project}: #{@board}") %>
<%= stylesheet_link_tag 'scm' %>
<% end %>

View File

@@ -1,6 +0,0 @@
<h2>403</h2>
<p><%= l(:notice_not_authorized) %></p>
<p><a href="javascript:history.back()">Back</a></p>
<% html_title '403' %>

View File

@@ -1,6 +0,0 @@
<h2>404</h2>
<p><%= l(:notice_file_not_found) %></p>
<p><a href="javascript:history.back()">Back</a></p>
<% html_title '404' %>

View File

@@ -0,0 +1,6 @@
<h2><%=h @status %></h2>
<p id="errorExplanation"><%=h @message %></p>
<p><a href="javascript:history.back()">Back</a></p>
<% html_title @status %>

View File

@@ -9,7 +9,7 @@
:class => 'icon-edit', :disabled => !@can[:edit] %></li>
<% end %>
<% unless @allowed_statuses.empty? %>
<% if @allowed_statuses.present? %>
<li class="folder">
<a href="#" class="submenu" onclick="return false;"><%= l(:field_status) %></a>
<ul>
@@ -57,7 +57,7 @@
</ul>
</li>
<% end %>
<% unless @assignables.nil? || @assignables.empty? -%>
<% if @assignables.present? -%>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_assigned_to) %></a>
<ul>
@@ -98,7 +98,7 @@
<% if !@issue.nil? %>
<% if @can[:log_time] -%>
<li><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue},
<li><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'new', :issue_id => @issue},
:class => 'icon-time-add' %></li>
<% end %>
<% if User.current.logged? %>

View File

@@ -86,10 +86,12 @@ when "IssueCustomField" %>
<% when "UserCustomField" %>
<p><%= f.check_box :is_required %></p>
<p><%= f.check_box :visible %></p>
<p><%= f.check_box :editable %></p>
<% when "ProjectCustomField" %>
<p><%= f.check_box :is_required %></p>
<p><%= f.check_box :visible %></p>
<p><%= f.check_box :searchable %></p>
<% when "TimeEntryCustomField" %>

View File

@@ -10,7 +10,7 @@
</div>
</fieldset>
<p style="float:right;">
<p class="contextual">
<%= gantt_zoom_link(@gantt, :in) %>
<%= gantt_zoom_link(@gantt, :out) %>
</p>
@@ -59,10 +59,19 @@ end
# Width of the entire chart
g_width = (@gantt.date_to - @gantt.date_from + 1)*zoom
# Collect the number of issues on Versions
@gantt.render(:top => headers_height + 8, :zoom => zoom, :g_width => g_width, :subject_width => subject_width)
g_height = [(20 * (@gantt.number_of_rows + 6))+150, 206].max
t_height = g_height + headers_height
%>
<% if @gantt.truncated %>
<p class="warning"><%= l(:notice_gantt_chart_truncated, :max => @gantt.max_rows) %></p>
<% end %>
<table width="100%" style="border:0; border-collapse: collapse;">
<tr>
<td style="width:<%= subject_width %>px; padding:0px;">
@@ -70,9 +79,10 @@ t_height = g_height + headers_height
<div style="position:relative;height:<%= t_height + 24 %>px;width:<%= subject_width + 1 %>px;">
<div style="right:-2px;width:<%= subject_width %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr"></div>
<div style="right:-2px;width:<%= subject_width %>px;height:<%= t_height %>px;border-left: 1px solid #c0c0c0;overflow:hidden;" class="gantt_hdr"></div>
<% top = headers_height + 8 %>
<%= @gantt.subjects(:headers_height => headers_height, :top => top, :g_width => g_width) %>
<div class="gantt_subjects">
<%= @gantt.subjects %>
</div>
</div>
</td>
@@ -151,9 +161,7 @@ if show_days
end
end %>
<% top = headers_height + 10 %>
<%= @gantt.lines(:top => top, :zoom => zoom, :g_width => g_width ) %>
<%= @gantt.lines %>
<%
#

View File

@@ -29,7 +29,7 @@
<% remote_form_for(:group, @group, :url => {:controller => 'groups', :action => 'add_users', :id => @group}, :method => :post) do |f| %>
<fieldset><legend><%=l(:label_user_new)%></legend>
<p><%= text_field_tag 'user_search', nil %></p>
<p><%= label_tag "user_search", l(:label_user_search) %><%= text_field_tag 'user_search', nil %></p>
<%= observe_field(:user_search,
:frequency => 0.5,
:update => :users,

View File

@@ -2,5 +2,5 @@
<div class="box">
<p><%= f.text_field :name, :size => 30, :required => true %></p>
<p><%= f.select :assigned_to_id, @project.users.collect{|u| [u.name, u.id]}, :include_blank => true %></p>
<p><%= f.select :assigned_to_id, @project.users.sort.collect{|u| [u.name, u.id]}, :include_blank => true %></p>
</div>

View File

@@ -10,6 +10,10 @@
<%= @issues.collect {|i| hidden_field_tag('ids[]', i.id)}.join %>
<div class="box tabular">
<fieldset class="attributes">
<legend><%= l(:label_change_properties) %></legend>
<div class="splitcontentleft">
<p><label for="new_project_id"><%=l(:field_project)%>:</label>
<%= select_tag "new_project_id",
project_tree_options_for_select(@allowed_projects, :selected => @target_project),
@@ -21,18 +25,25 @@
<p><label for="new_tracker_id"><%=l(:field_tracker)%>:</label>
<%= select_tag "new_tracker_id", "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@trackers, "id", "name") %></p>
<p>
<label><%= l(:field_status) %></label>
<%= select_tag('status_id', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@available_statuses, :id, :name)) %>
</p>
<p>
<label><%= l(:field_priority) %></label>
<%= select_tag('priority_id', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(IssuePriority.all, :id, :name)) %>
</p>
<p>
<label><%= l(:field_assigned_to) %></label>
<%= select_tag('assigned_to_id', content_tag('option', l(:label_no_change_option), :value => '') +
content_tag('option', l(:label_nobody), :value => 'none') +
options_from_collection_for_select(@target_project.assignable_users, :id, :name)) %>
</p>
</div>
<p>
<label><%= l(:field_status) %></label>
<%= select_tag('status_id', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@available_statuses, :id, :name)) %>
</p>
<div class="splitcontentright">
<p>
<label><%= l(:field_start_date) %></label>
<%= text_field_tag 'start_date', '', :size => 10 %><%= calendar_for('start_date') %>
@@ -42,6 +53,14 @@
<label><%= l(:field_due_date) %></label>
<%= text_field_tag 'due_date', '', :size => 10 %><%= calendar_for('due_date') %>
</p>
</div>
</fieldset>
<fieldset><legend><%= l(:field_notes) %></legend>
<%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %>
<%= wikitoolbar_for 'notes' %>
</fieldset>
<%= call_hook(:view_issues_move_bottom, :issues => @issues, :target_project => @target_project, :copy => !!@copy) %>
</div>

View File

@@ -1,7 +1,9 @@
<%= error_messages_for 'relation' %>
<p><%= f.select :relation_type, collection_for_relation_type_select, {}, :onchange => "setPredecessorFieldsVisibility();" %>
<%= l(:label_issue) %> #<%= f.text_field :issue_to_id, :size => 6 %>
<%= l(:label_issue) %> #<%= f.text_field :issue_to_id, :size => 10 %>
<div id="related_issue_candidates" class="autocomplete"></div>
<%= javascript_tag "observeRelatedIssueField('#{auto_complete_issues_path(:id => @issue, :project_id => @project) }')" %>
<span id="predecessor_fields" style="display:none;">
<%= l(:field_delay) %>: <%= f.text_field :delay, :size => 3 %> <%= l(:label_day_plural) %>
</span>

View File

@@ -1,9 +1,9 @@
<% changesets.each do |changeset| %>
<div class="changeset <%= cycle('odd', 'even') %>">
<p><%= link_to("#{l(:label_revision)} #{changeset.revision}",
:controller => 'repositories', :action => 'revision', :id => changeset.project, :rev => changeset.revision) %><br />
<p><%= link_to_revision(changeset, changeset.project,
:text => "#{l(:label_revision)} #{changeset.format_identifier}") %><br />
<span class="author"><%= authoring(changeset.committed_on, changeset.author) %></span></p>
<div class="changeset-changes">
<div class="wiki">
<%= textilizable(changeset, :comments) %>
</div>
</div>

View File

@@ -8,8 +8,8 @@
<p><%= f.text_field :subject, :size => 80, :required => true %></p>
<% unless (@issue.new_record? && @issue.parent_issue_id.nil?) || !User.current.allowed_to?(:manage_subtasks, @project) %>
<p><%= f.text_field :parent_issue_id, :size => 10 %></p>
<% if User.current.allowed_to?(:manage_subtasks, @project) %>
<p id="parent_issue"><%= f.text_field :parent_issue_id, :size => 10 %></p>
<div id="parent_issue_candidates" class="autocomplete"></div>
<%= javascript_tag "observeParentIssueField('#{auto_complete_issues_path(:id => @issue, :project_id => @project) }')" %>
<% end %>
@@ -26,11 +26,11 @@
</div>
<% if @issue.new_record? %>
<p><%= label_tag('attachments[1][file]', l(:label_attachment_plural))%><%= render :partial => 'attachments/form' %></p>
<p id="attachments_form"><%= label_tag('attachments[1][file]', l(:label_attachment_plural))%><%= render :partial => 'attachments/form' %></p>
<% end %>
<% if @issue.new_record? && User.current.allowed_to?(:add_issue_watchers, @project) -%>
<p><label><%= l(:label_issue_watchers) %></label>
<p id="watchers_form"><label><%= l(:label_issue_watchers) %></label>
<% @issue.project.users.sort.each do |user| -%>
<label class="floating"><%= check_box_tag 'issue[watcher_user_ids][]', user.id, @issue.watched_by?(user) %> <%=h user %></label>
<% end -%>

View File

@@ -6,9 +6,9 @@
<p><strong><%=l(:label_related_issues)%></strong></p>
<% if @issue.relations.any? %>
<% if @relations.present? %>
<table style="width:100%">
<% @issue.relations.select {|r| r.other_issue(@issue).visible? }.each do |relation| %>
<% @relations.each do |relation| %>
<tr>
<td><%= l(relation.label_for(@issue)) %> <%= "(#{l('datetime.distance_in_words.x_days', :count => relation.delay)})" if relation.delay && relation.delay != 0 %>
<%= h(relation.other_issue(@issue).project) + ' - ' if Setting.cross_project_issue_relations? %>

View File

@@ -43,7 +43,7 @@
<label><%= l(:field_fixed_version) %></label>
<%= select_tag('issue[fixed_version_id]', content_tag('option', l(:label_no_change_option), :value => '') +
content_tag('option', l(:label_none), :value => 'none') +
version_options_for_select(@project.shared_versions.open)) %>
version_options_for_select(@project.shared_versions.open.sort)) %>
</p>
<% end %>

View File

@@ -0,0 +1,28 @@
api.array :issues, api_meta(:total_count => @issue_count, :offset => @offset, :limit => @limit) do
@issues.each do |issue|
api.issue do
api.id issue.id
api.project(:id => issue.project_id, :name => issue.project.name) unless issue.project.nil?
api.tracker(:id => issue.tracker_id, :name => issue.tracker.name) unless issue.tracker.nil?
api.status(:id => issue.status_id, :name => issue.status.name) unless issue.status.nil?
api.priority(:id => issue.priority_id, :name => issue.priority.name) unless issue.priority.nil?
api.author(:id => issue.author_id, :name => issue.author.name) unless issue.author.nil?
api.assigned_to(:id => issue.assigned_to_id, :name => issue.assigned_to.name) unless issue.assigned_to.nil?
api.category(:id => issue.category_id, :name => issue.category.name) unless issue.category.nil?
api.fixed_version(:id => issue.fixed_version_id, :name => issue.fixed_version.name) unless issue.fixed_version.nil?
api.parent(:id => issue.parent_id) unless issue.parent.nil?
api.subject issue.subject
api.description issue.description
api.start_date issue.start_date
api.due_date issue.due_date
api.done_ratio issue.done_ratio
api.estimated_hours issue.estimated_hours
render_api_custom_values issue.custom_field_values, api
api.created_on issue.created_on
api.updated_on issue.updated_on
end
end
end

View File

@@ -1,33 +0,0 @@
xml.instruct!
xml.issues :type => 'array' do
@issues.each do |issue|
xml.issue do
xml.id issue.id
xml.project(:id => issue.project_id, :name => issue.project.name) unless issue.project.nil?
xml.tracker(:id => issue.tracker_id, :name => issue.tracker.name) unless issue.tracker.nil?
xml.status(:id => issue.status_id, :name => issue.status.name) unless issue.status.nil?
xml.priority(:id => issue.priority_id, :name => issue.priority.name) unless issue.priority.nil?
xml.author(:id => issue.author_id, :name => issue.author.name) unless issue.author.nil?
xml.assigned_to(:id => issue.assigned_to_id, :name => issue.assigned_to.name) unless issue.assigned_to.nil?
xml.category(:id => issue.category_id, :name => issue.category.name) unless issue.category.nil?
xml.fixed_version(:id => issue.fixed_version_id, :name => issue.fixed_version.name) unless issue.fixed_version.nil?
xml.parent(:id => issue.parent_id) unless issue.parent.nil?
xml.subject issue.subject
xml.description issue.description
xml.start_date issue.start_date
xml.due_date issue.due_date
xml.done_ratio issue.done_ratio
xml.estimated_hours issue.estimated_hours
xml.custom_fields do
issue.custom_field_values.each do |custom_value|
xml.custom_field custom_value.value, :id => custom_value.custom_field_id, :name => custom_value.custom_field.name
end
end
xml.created_on issue.created_on
xml.updated_on issue.updated_on
end
end
end

View File

@@ -1,7 +1,7 @@
<h2><%=l(:label_issue_new)%></h2>
<% labelled_tabular_form_for :issue, @issue, :url => {:controller => 'issues', :action => 'create', :project_id => @project},
:html => {:multipart => true, :id => 'issue-form'} do |f| %>
:html => {:multipart => true, :id => 'issue-form', :class => 'tabular new-issue-form'} do |f| %>
<%= error_messages_for 'issue' %>
<div class="box">
<%= render :partial => 'issues/form', :locals => {:f => f} %>

View File

@@ -0,0 +1,61 @@
api.issue do
api.id @issue.id
api.project(:id => @issue.project_id, :name => @issue.project.name) unless @issue.project.nil?
api.tracker(:id => @issue.tracker_id, :name => @issue.tracker.name) unless @issue.tracker.nil?
api.status(:id => @issue.status_id, :name => @issue.status.name) unless @issue.status.nil?
api.priority(:id => @issue.priority_id, :name => @issue.priority.name) unless @issue.priority.nil?
api.author(:id => @issue.author_id, :name => @issue.author.name) unless @issue.author.nil?
api.assigned_to(:id => @issue.assigned_to_id, :name => @issue.assigned_to.name) unless @issue.assigned_to.nil?
api.category(:id => @issue.category_id, :name => @issue.category.name) unless @issue.category.nil?
api.fixed_version(:id => @issue.fixed_version_id, :name => @issue.fixed_version.name) unless @issue.fixed_version.nil?
api.parent(:id => @issue.parent_id) unless @issue.parent.nil?
api.subject @issue.subject
api.description @issue.description
api.start_date @issue.start_date
api.due_date @issue.due_date
api.done_ratio @issue.done_ratio
api.estimated_hours @issue.estimated_hours
api.spent_hours(@issue.spent_hours) if User.current.allowed_to?(:view_time_entries, @project)
render_api_custom_values @issue.custom_field_values, api
api.created_on @issue.created_on
api.updated_on @issue.updated_on
render_api_issue_children(@issue, api) if include_in_api_response?('children')
api.array :relations do
@relations.each do |relation|
api.relation(:id => relation.id, :issue_id => relation.other_issue(@issue).id, :relation_type => relation.relation_type_for(@issue), :delay => relation.delay)
end
end if include_in_api_response?('relations') && @relations.present?
api.array :changesets do
@issue.changesets.each do |changeset|
api.changeset :revision => changeset.revision do
api.user(:id => changeset.user_id, :name => changeset.user.name) unless changeset.user.nil?
api.comments changeset.comments
api.committed_on changeset.committed_on
end
end
end if include_in_api_response?('changesets') && User.current.allowed_to?(:view_changesets, @project)
api.array :journals do
@issue.journals.each do |journal|
api.journal :id => journal.id do
api.user(:id => journal.user_id, :name => journal.user.name) unless journal.user.nil?
api.notes journal.notes
api.created_on journal.created_on
api.array :details do
journal.details.each do |detail|
api.detail :property => detail.property, :name => detail.prop_key do
api.old_value detail.old_value
api.new_value detail.value
end
end
end
end
end
end if include_in_api_response?('journals')
end

View File

@@ -47,14 +47,16 @@
<% if @issue.description? || @issue.attachments.any? -%>
<hr />
<div class="contextual">
<%= link_to_remote_if_authorized(l(:button_quote), { :url => {:action => 'reply', :id => @issue} }, :class => 'icon icon-comment') if @issue.description? %>
</div>
<div class="wiki">
<%= textilizable @issue, :description, :attachments => @issue.attachments %>
</div>
<% if @issue.description? %>
<div class="contextual">
<%= link_to_remote_if_authorized(l(:button_quote), { :url => {:controller => 'journals', :action => 'new', :id => @issue} }, :class => 'icon icon-comment') %>
</div>
<p><strong><%=l(:field_description)%></strong></p>
<div class="wiki">
<%= textilizable @issue, :description, :attachments => @issue.attachments %>
</div>
<% end %>
<%= link_to_attachments @issue %>
<% end -%>

View File

@@ -1,62 +0,0 @@
xml.instruct!
xml.issue do
xml.id @issue.id
xml.project(:id => @issue.project_id, :name => @issue.project.name) unless @issue.project.nil?
xml.tracker(:id => @issue.tracker_id, :name => @issue.tracker.name) unless @issue.tracker.nil?
xml.status(:id => @issue.status_id, :name => @issue.status.name) unless @issue.status.nil?
xml.priority(:id => @issue.priority_id, :name => @issue.priority.name) unless @issue.priority.nil?
xml.author(:id => @issue.author_id, :name => @issue.author.name) unless @issue.author.nil?
xml.assigned_to(:id => @issue.assigned_to_id, :name => @issue.assigned_to.name) unless @issue.assigned_to.nil?
xml.category(:id => @issue.category_id, :name => @issue.category.name) unless @issue.category.nil?
xml.fixed_version(:id => @issue.fixed_version_id, :name => @issue.fixed_version.name) unless @issue.fixed_version.nil?
xml.parent(:id => @issue.parent_id) unless @issue.parent.nil?
xml.subject @issue.subject
xml.description @issue.description
xml.start_date @issue.start_date
xml.due_date @issue.due_date
xml.done_ratio @issue.done_ratio
xml.estimated_hours @issue.estimated_hours
if User.current.allowed_to?(:view_time_entries, @project)
xml.spent_hours @issue.spent_hours
end
xml.custom_fields do
@issue.custom_field_values.each do |custom_value|
xml.custom_field custom_value.value, :id => custom_value.custom_field_id, :name => custom_value.custom_field.name
end
end unless @issue.custom_field_values.empty?
xml.created_on @issue.created_on
xml.updated_on @issue.updated_on
xml.relations do
@issue.relations.select {|r| r.other_issue(@issue).visible? }.each do |relation|
xml.relation(:id => relation.id, :issue_id => relation.other_issue(@issue).id, :relation_type => relation.relation_type_for(@issue), :delay => relation.delay)
end
end
xml.changesets do
@issue.changesets.each do |changeset|
xml.changeset :revision => changeset.revision do
xml.user(:id => changeset.user_id, :name => changeset.user.name) unless changeset.user.nil?
xml.comments changeset.comments
xml.committed_on changeset.committed_on
end
end
end if User.current.allowed_to?(:view_changesets, @project) && @issue.changesets.any?
xml.journals do
@issue.journals.each do |journal|
xml.journal :id => journal.id do
xml.user(:id => journal.user_id, :name => journal.user.name) unless journal.user.nil?
xml.notes journal.notes
xml.details do
journal.details.each do |detail|
xml.detail :property => detail.property, :name => detail.prop_key, :old => detail.old_value, :new => detail.value
end
end
end
end
end unless @issue.journals.empty?
end

View File

@@ -9,6 +9,7 @@
<%= stylesheet_link_tag 'application', :media => 'all' %>
<%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %>
<%= javascript_include_tag :defaults %>
<%= heads_for_theme %>
<%= heads_for_wiki_formatter %>
<!--[if IE]>
<style type="text/css">
@@ -20,7 +21,7 @@
<!-- page specific tags -->
<%= yield :header_tags -%>
</head>
<body class="<%= body_css_classes %>">
<body class="<%=h body_css_classes %>">
<div id="wrapper">
<div id="wrapper2">
<div id="top-menu">
@@ -68,7 +69,7 @@
<div id="footer">
<div class="bgl"><div class="bgr">
Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> &copy; 2006-2010 Jean-Philippe Lang
Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> &copy; 2006-2011 Jean-Philippe Lang
</div></div>
</div>
</div>

View File

@@ -25,6 +25,7 @@ hr {
</style>
</head>
<body>
<span class="header"><%= Redmine::WikiFormatting.to_html(Setting.text_formatting, Setting.emails_header) %></span>
<%= yield %>
<hr />
<span class="footer"><%= Redmine::WikiFormatting.to_html(Setting.text_formatting, Setting.emails_footer) %></span>

View File

@@ -1,3 +1,4 @@
<%= Setting.emails_header %>
<%= yield %>
--
<%= Setting.emails_footer %>

View File

@@ -1,3 +1,3 @@
<p><%= l(:mail_body_wiki_content_added, :page => link_to(h(@wiki_content.page.pretty_title), @wiki_content_url),
<p><%= l(:mail_body_wiki_content_added, :id => link_to(h(@wiki_content.page.pretty_title), @wiki_content_url),
:author => h(@wiki_content.author)) %><br />
<em><%=h @wiki_content.comments %></em></p>

View File

@@ -1,4 +1,4 @@
<%= l(:mail_body_wiki_content_added, :page => h(@wiki_content.page.pretty_title),
<%= l(:mail_body_wiki_content_added, :id => h(@wiki_content.page.pretty_title),
:author => h(@wiki_content.author)) %>
<%= @wiki_content.comments %>

View File

@@ -1,4 +1,4 @@
<p><%= l(:mail_body_wiki_content_updated, :page => link_to(h(@wiki_content.page.pretty_title), @wiki_content_url),
<p><%= l(:mail_body_wiki_content_updated, :id => link_to(h(@wiki_content.page.pretty_title), @wiki_content_url),
:author => h(@wiki_content.author)) %><br />
<em><%=h @wiki_content.comments %></em></p>

View File

@@ -1,4 +1,4 @@
<%= l(:mail_body_wiki_content_updated, :page => h(@wiki_content.page.pretty_title),
<%= l(:mail_body_wiki_content_updated, :id => h(@wiki_content.page.pretty_title),
:author => h(@wiki_content.author)) %>
<%= @wiki_content.comments %>

View File

@@ -12,3 +12,7 @@
}, :accesskey => accesskey(:preview) %>
<% end %>
<div id="preview" class="wiki"></div>
<% content_for :header_tags do %>
<%= stylesheet_link_tag 'scm' %>
<% end %>

View File

@@ -5,9 +5,9 @@ function recreateSortables() {
Sortable.destroy('list-left');
Sortable.destroy('list-right');
Sortable.create("list-top", {constraint:false, containment:['list-top','list-left','list-right'], dropOnEmpty:true, handle:'handle', onUpdate:function(){new Ajax.Request('/my/order_blocks?group=top', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("list-top")})}, only:'mypage-box', tag:'div'})
Sortable.create("list-left", {constraint:false, containment:['list-top','list-left','list-right'], dropOnEmpty:true, handle:'handle', onUpdate:function(){new Ajax.Request('/my/order_blocks?group=left', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("list-left")})}, only:'mypage-box', tag:'div'})
Sortable.create("list-right", {constraint:false, containment:['list-top','list-left','list-right'], dropOnEmpty:true, handle:'handle', onUpdate:function(){new Ajax.Request('/my/order_blocks?group=right', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("list-right")})}, only:'mypage-box', tag:'div'})
Sortable.create("list-top", {constraint:false, containment:['list-top','list-left','list-right'], dropOnEmpty:true, handle:'handle', onUpdate:function(){new Ajax.Request('<%= url_for(:controller => 'my', :action => 'order_blocks', :group => 'top') %>', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("list-top")})}, only:'mypage-box', tag:'div'})
Sortable.create("list-left", {constraint:false, containment:['list-top','list-left','list-right'], dropOnEmpty:true, handle:'handle', onUpdate:function(){new Ajax.Request('<%= url_for(:controller => 'my', :action => 'order_blocks', :group => 'left') %>', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("list-left")})}, only:'mypage-box', tag:'div'})
Sortable.create("list-right", {constraint:false, containment:['list-top','list-left','list-right'], dropOnEmpty:true, handle:'handle', onUpdate:function(){new Ajax.Request('<%= url_for(:controller => 'my', :action => 'order_blocks', :group => 'right') %>', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("list-right")})}, only:'mypage-box', tag:'div'})
}
function updateSelect() {

View File

@@ -0,0 +1,14 @@
api.array :news, api_meta(:total_count => @news_count, :offset => @offset, :limit => @limit) do
@newss.each do |news|
api.news do
api.id news.id
api.project(:id => news.project_id, :name => news.project.name) unless news.project.nil?
api.author(:id => news.author_id, :name => news.author.name) unless news.author.nil?
api.title news.title
api.summary news.summary
api.description news.description
api.created_on news.created_on
end
end
end

View File

@@ -1,10 +1,14 @@
<div class="contextual">
<%= link_to_if_authorized l(:button_edit),
edit_news_path(@news),
:class => 'icon icon-edit',
:accesskey => accesskey(:edit),
:onclick => 'Element.show("edit-news"); return false;' %>
<%= link_to_if_authorized l(:button_delete), news_path(@news), :confirm => l(:text_are_you_sure), :method => :delete, :class => 'icon icon-del' %>
<%= link_to(l(:button_edit),
edit_news_path(@news),
:class => 'icon icon-edit',
:accesskey => accesskey(:edit),
:onclick => 'Element.show("edit-news"); return false;') if User.current.allowed_to?(:manage_news, @project) %>
<%= link_to(l(:button_delete),
news_path(@news),
:confirm => l(:text_are_you_sure),
:method => :delete,
:class => 'icon icon-del') if User.current.allowed_to?(:manage_news, @project) %>
</div>
<h2><%= avatar(@news.author, :size => "24") %><%=h @news.title %></h2>

View File

@@ -2,16 +2,16 @@
<div class="box">
<!--[form:project]-->
<p><%= f.text_field :name, :required => true, :maxlength => 30 %><br /><em><%= l(:text_caracters_maximum, 30) %></em></p>
<p><%= f.text_field :name, :required => true, :size => 60 %></p>
<% unless @project.allowed_parents.compact.empty? %>
<p><%= label(:project, :parent_id, l(:field_parent)) %><%= parent_project_select_tag(@project) %></p>
<% end %>
<p><%= f.text_area :description, :rows => 5, :class => 'wiki-edit' %></p>
<p><%= f.text_field :identifier, :required => true, :disabled => @project.identifier_frozen?, :maxlength => 20 %>
<p><%= f.text_field :identifier, :required => true, :size => 60, :disabled => @project.identifier_frozen? %>
<% unless @project.identifier_frozen? %>
<br /><em><%= l(:text_length_between, :min => 1, :max => 20) %> <%= l(:text_project_identifier_info) %></em>
<br /><em><%= l(:text_length_between, :min => 1, :max => Project::IDENTIFIER_MAX_LENGTH) %> <%= l(:text_project_identifier_info) %></em>
<% end %></p>
<p><%= f.text_field :homepage, :size => 60 %></p>
<p><%= f.check_box :is_public %></p>
@@ -23,8 +23,22 @@
<%= call_hook(:view_projects_form, :project => @project, :form => f) %>
</div>
<% if @project.new_record? %>
<fieldset class="box"><legend><%= l(:label_module_plural) %></legend>
<% Redmine::AccessControl.available_project_modules.each do |m| %>
<label class="floating">
<%= check_box_tag 'project[enabled_module_names][]', m, @project.module_enabled?(m), :id => "project_enabled_module_names_#{m}" %>
<%= l_or_humanize(m, :prefix => "project_module_") %>
</label>
<% end %>
<%= hidden_field_tag 'project[enabled_module_names][]', '' %>
<%= javascript_tag 'observeProjectModules()' %>
</fieldset>
<% end %>
<% if @project.new_record? || @project.module_enabled?('issue_tracking') %>
<% unless @trackers.empty? %>
<fieldset class="box"><legend><%=l(:label_tracker_plural)%></legend>
<fieldset class="box" id="project_trackers"><legend><%=l(:label_tracker_plural)%></legend>
<% @trackers.each do |tracker| %>
<label class="floating">
<%= check_box_tag 'project[tracker_ids][]', tracker.id, @project.trackers.include?(tracker) %>
@@ -36,7 +50,7 @@
<% end %>
<% unless @issue_custom_fields.empty? %>
<fieldset class="box"><legend><%=l(:label_custom_field_plural)%></legend>
<fieldset class="box" id="project_issue_custom_fields"><legend><%=l(:label_custom_field_plural)%></legend>
<% @issue_custom_fields.each do |custom_field| %>
<label class="floating">
<%= check_box_tag 'project[issue_custom_field_ids][]', custom_field.id, (@project.all_issue_custom_fields.include? custom_field), (custom_field.is_for_all? ? {:disabled => "disabled"} : {}) %>
@@ -46,4 +60,5 @@
<%= hidden_field_tag 'project[issue_custom_field_ids][]', '' %>
</fieldset>
<% end %>
<% end %>
<!--[eoform:project]-->

View File

@@ -3,15 +3,6 @@
<% labelled_tabular_form_for :project, @project, :url => { :action => "copy" } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<fieldset class="box"><legend><%= l(:label_module_plural) %></legend>
<% Redmine::AccessControl.available_project_modules.each do |m| %>
<label class="floating">
<%= check_box_tag 'enabled_modules[]', m, @project.module_enabled?(m) %>
<%= l_or_humanize(m, :prefix => "project_module_") %>
</label>
<% end %>
</fieldset>
<fieldset class="box"><legend><%= l(:button_copy) %></legend>
<label class="block"><%= check_box_tag 'only[]', 'members', true %> <%= l(:label_member_plural) %> (<%= @source_project.members.count %>)</label>
<label class="block"><%= check_box_tag 'only[]', 'versions', true %> <%= l(:label_version_plural) %> (<%= @source_project.versions.count %>)</label>

View File

@@ -0,0 +1,16 @@
api.array :projects, api_meta(:total_count => @project_count, :offset => @offset, :limit => @limit) do
@projects.each do |project|
api.project do
api.id project.id
api.name project.name
api.identifier project.identifier
api.description project.description
api.parent(:id => project.parent_id, :name => project.parent.name) unless project.parent.nil?
render_api_custom_values project.visible_custom_field_values, api
api.created_on project.created_on
api.updated_on project.updated_on
end
end
end

View File

@@ -1,19 +0,0 @@
xml.instruct!
xml.projects :type => 'array' do
@projects.each do |project|
xml.project do
xml.id project.id
xml.name project.name
xml.identifier project.identifier
xml.description project.description
xml.parent(:id => project.parent_id, :name => project.parent.name) unless project.parent.nil?
xml.custom_fields do
project.custom_field_values.each do |custom_value|
xml.custom_field custom_value.value, :id => custom_value.custom_field_id, :name => custom_value.custom_field.name
end
end unless project.custom_field_values.empty?
xml.created_on project.created_on
xml.updated_on project.updated_on
end
end
end

View File

@@ -2,16 +2,6 @@
<% labelled_tabular_form_for :project, @project, :url => { :action => "create" } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<fieldset class="box"><legend><%= l(:label_module_plural) %></legend>
<% Redmine::AccessControl.available_project_modules.each do |m| %>
<label class="floating">
<%= check_box_tag 'enabled_modules[]', m, @project.module_enabled?(m) %>
<%= l_or_humanize(m, :prefix => "project_module_") %>
</label>
<% end %>
</fieldset>
<%= submit_tag l(:button_save) %>
<%= javascript_tag "Form.Element.focus('project_name');" %>
<% end %>

View File

@@ -55,10 +55,12 @@
<div class="splitcontentright">
<% if roles.any? && principals.any? %>
<% remote_form_for(:member, @member, :url => {:controller => 'members', :action => 'new', :id => @project}, :method => :post) do |f| %>
<% remote_form_for(:member, @member, :url => {:controller => 'members', :action => 'new', :id => @project}, :method => :post,
:loading => '$(\'member-add-submit\').disable();',
:complete => 'if($(\'member-add-submit\')) $(\'member-add-submit\').enable();') do |f| %>
<fieldset><legend><%=l(:label_member_new)%></legend>
<p><%= text_field_tag 'principal_search', nil %></p>
<p><%= label_tag "principal_search", l(:label_principal_search) %><%= text_field_tag 'principal_search', nil %></p>
<%= observe_field(:principal_search,
:frequency => 0.5,
:update => :principals,
@@ -75,7 +77,7 @@
<label><%= check_box_tag 'member[role_ids][]', role.id %> <%=h role %></label>
<% end %></p>
<p><%= submit_tag l(:button_add) %></p>
<p><%= submit_tag l(:button_add), :id => 'member-add-submit' %></p>
</fieldset>
<% end %>
<% end %>

View File

@@ -7,7 +7,7 @@
<legend><%= l(:text_select_project_modules) %></legend>
<% Redmine::AccessControl.available_project_modules.each do |m| %>
<p><label><%= check_box_tag 'enabled_modules[]', m, @project.module_enabled?(m) -%>
<p><label><%= check_box_tag 'enabled_module_names[]', m, @project.module_enabled?(m) -%>
<%= l_or_humanize(m, :prefix => "project_module_") %></label></p>
<% end %>
</fieldset>

View File

@@ -6,7 +6,7 @@
<th><%= l(:field_description) %></th>
<th><%= l(:field_status) %></th>
<th><%= l(:field_sharing) %></th>
<th><%= l(:label_wiki_page) unless @project.wiki.nil? %></th>
<th><%= l(:label_wiki_page) %></th>
<th style="width:15%"></th>
</tr></thead>
<tbody>
@@ -17,7 +17,7 @@
<td class="description"><%=h version.description %></td>
<td class="status"><%= l("version_status_#{version.status}") %></td>
<td class="sharing"><%=h format_version_sharing(version.sharing) %></td>
<td><%= link_to(h(version.wiki_page_title), :controller => 'wiki', :page => Wiki.titleize(version.wiki_page_title)) unless version.wiki_page_title.blank? || @project.wiki.nil? %></td>
<td><%= link_to_if_authorized(h(version.wiki_page_title), {:controller => 'wiki', :action => 'show', :project_id => version.project, :id => Wiki.titleize(version.wiki_page_title)}) || h(version.wiki_page_title) unless version.wiki_page_title.blank? || version.project.wiki.nil? %></td>
<td class="buttons">
<% if version.project == @project %>
<%= link_to_if_authorized l(:button_edit), {:controller => 'versions', :action => 'edit', :id => version }, :class => 'icon icon-edit' %>

View File

@@ -0,0 +1,18 @@
api.project do
api.id @project.id
api.name @project.name
api.identifier @project.identifier
api.description @project.description
api.homepage @project.homepage
render_api_custom_values @project.visible_custom_field_values, api
api.created_on @project.created_on
api.updated_on @project.updated_on
api.array :trackers do
@project.trackers.each do |tracker|
api.tracker(:id => tracker.id, :name => tracker.name)
end
end if include_in_api_response?('trackers')
end

View File

@@ -16,7 +16,7 @@
<li><%=l(:label_subproject_plural)%>:
<%= @subprojects.collect{|p| link_to(h(p), :action => 'show', :id => p)}.join(", ") %></li>
<% end %>
<% @project.custom_values.each do |custom_value| %>
<% @project.visible_custom_field_values.each do |custom_value| %>
<% if !custom_value.value.blank? %>
<li><%= custom_value.custom_field.name%>: <%=h show_value(custom_value) %></li>
<% end %>

View File

@@ -1,23 +0,0 @@
xml.instruct!
xml.project do
xml.id @project.id
xml.name @project.name
xml.identifier @project.identifier
xml.description @project.description
xml.homepage @project.homepage
xml.custom_fields do
@project.custom_field_values.each do |custom_value|
xml.custom_field custom_value.value, :id => custom_value.custom_field_id, :name => custom_value.custom_field.name
end
end unless @project.custom_field_values.empty?
xml.created_on @project.created_on
xml.updated_on @project.updated_on
xml.trackers do
@project.trackers.each do |tracker|
xml.tracker(:id => tracker.id, :name => tracker.name)
end
end
end

View File

@@ -114,3 +114,4 @@ Event.observe(document,"dom:loaded", apply_filters_observer);
</td>
</tr>
</table>
<%= hidden_field_tag 'fields[]', '' %>

View File

@@ -17,7 +17,7 @@
</td>
<td class="size"><%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %></td>
<% changeset = @project.repository.changesets.find_by_revision(entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %>
<td class="revision"><%= link_to_revision(changeset.revision, @project) if changeset %></td>
<td class="revision"><%= link_to_revision(changeset, @project) if changeset %></td>
<td class="age"><%= distance_of_time_in_words(entry.lastrev.time, Time.now) if entry.lastrev && entry.lastrev.time %></td>
<td class="author"><%= changeset.nil? ? h(entry.lastrev.author.to_s.split('<').first) : changeset.author if entry.lastrev %></td>
<td class="comments"><%=h truncate(changeset.comments, :length => 50) unless changeset.nil? %></td>

View File

@@ -13,9 +13,9 @@
<% line_num = 1 %>
<% revisions.each do |changeset| %>
<tr class="changeset <%= cycle 'odd', 'even' %>">
<td class="id"><%= link_to_revision(changeset.revision, project) %></td>
<td class="checkbox"><%= radio_button_tag('rev', changeset.revision, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < revisions.size) %></td>
<td class="checkbox"><%= radio_button_tag('rev_to', changeset.revision, (line_num==2), :id => "cbto-#{line_num}", :onclick => "if ($('cb-#{line_num}').checked==true) {$('cb-#{line_num-1}').checked=true;}") if show_diff && (line_num > 1) %></td>
<td class="id"><%= link_to_revision(changeset, project) %></td>
<td class="checkbox"><%= radio_button_tag('rev', changeset.identifier, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < revisions.size) %></td>
<td class="checkbox"><%= radio_button_tag('rev_to', changeset.identifier, (line_num==2), :id => "cbto-#{line_num}", :onclick => "if ($('cb-#{line_num}').checked==true) {$('cb-#{line_num-1}').checked=true;}") if show_diff && (line_num > 1) %></td>
<td class="committed_on"><%= format_time(changeset.committed_on) %></td>
<td class="author"><%=h changeset.author %></td>
<td class="comments"><%= textilizable(truncate_at_line_break(changeset.comments)) %></td>

View File

@@ -19,7 +19,7 @@
<tr class="bloc-<%= revision.nil? ? 0 : colors[revision.identifier || revision.revision] %>">
<th class="line-num" id="L<%= line_num %>"><a href="#L<%= line_num %>"><%= line_num %></a></th>
<td class="revision">
<%= (revision.identifier ? link_to(format_revision(revision.identifier), :action => 'revision', :id => @project, :rev => revision.identifier) : format_revision(revision.revision)) if revision %></td>
<%= (revision.identifier ? link_to_revision(revision, @project) : format_revision(revision)) if revision %></td>
<td class="author"><%= h(revision.author.to_s.split('<').first) if revision %></td>
<td class="line-code"><pre><%= line %></pre></td>
</tr>

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