Compare commits

..
135 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
544 changed files with 5122 additions and 35339 deletions

No files matched your search

-3
View File
@@ -1,7 +1,6 @@
/.project
/.loadpath
/config/additional_environment.rb
/config/configuration.yml
/config/database.yml
/config/email.yml
/config/initializers/session_store.rb
@@ -10,8 +9,6 @@
/db/*.sqlite3
/db/schema.rb
/files/*
/lib/redmine/scm/adapters/mercurial/redminehelper.pyc
/lib/redmine/scm/adapters/mercurial/redminehelper.pyo
/log/*.log*
/log/mongrel_debug
/public/dispatch.*
-5
View File
@@ -3,7 +3,6 @@ syntax: glob
.project
.loadpath
config/additional_environment.rb
config/configuration.yml
config/database.yml
config/email.yml
config/initializers/session_store.rb
@@ -12,8 +11,6 @@ db/*.db
db/*.sqlite3
db/schema.rb
files/*
lib/redmine/scm/adapters/mercurial/redminehelper.pyc
lib/redmine/scm/adapters/mercurial/redminehelper.pyo
log/*.log*
log/mongrel_debug
public/dispatch.*
@@ -25,5 +22,3 @@ tmp/sockets/*
tmp/test/*
vendor/rails
*.rbc
.svn/
.git/
+2 -14
View File
@@ -203,25 +203,13 @@ class AccountController < ApplicationController
self.logged_user = user
# generate a key and set cookie if autologin
if params[:autologin] && Setting.autologin?
set_autologin_cookie(user)
token = Token.create(:user => user, :action => 'autologin')
cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now }
end
call_hook(:controller_account_success_authentication_after, {:user => user })
redirect_back_or_default :controller => 'my', :action => 'page'
end
def set_autologin_cookie(user)
token = Token.create(:user => user, :action => 'autologin')
cookie_name = Redmine::Configuration['autologin_cookie_name'] || 'autologin'
cookie_options = {
:value => token.value,
:expires => 1.year.from_now,
:path => (Redmine::Configuration['autologin_cookie_path'] || '/'),
:secure => (Redmine::Configuration['autologin_cookie_secure'] ? true : false),
:httponly => true
}
cookies[cookie_name] = cookie_options
end
# Onthefly creation failed, display the registration form to fill/fix attributes
def onthefly_creation_failed(user, auth_source_options = { })
@user = user
+2 -18
View File
@@ -1,24 +1,7 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class ActivitiesController < ApplicationController
menu_item :activity
before_filter :find_optional_project
accept_rss_auth :index
accept_key_auth :index
def index
@days = Setting.activity_days_default.to_i
@@ -72,4 +55,5 @@ class ActivitiesController < ApplicationController
rescue ActiveRecord::RecordNotFound
render_404
end
end
+7 -5
View File
@@ -17,7 +17,9 @@
class AdminController < ApplicationController
layout 'admin'
before_filter :require_admin
helper :sort
include SortHelper
@@ -28,10 +30,12 @@ class AdminController < ApplicationController
def projects
@status = params[:status] ? params[:status].to_i : 1
c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
unless params[:name].blank?
name = "%#{params[:name].strip.downcase}%"
c << ["LOWER(identifier) LIKE ? OR LOWER(name) LIKE ?", name, name]
end
@projects = Project.find :all, :order => 'lft',
:conditions => c.conditions
@@ -73,12 +77,10 @@ class AdminController < ApplicationController
def info
@db_adapter_name = ActiveRecord::Base.connection.adapter_name
@checklist = [
[:text_default_administrator_account_changed,
User.find(:first,
:conditions => ["login=? and hashed_password=?", 'admin', User.hash_password('admin')]).nil?],
[:text_default_administrator_account_changed, User.find(:first, :conditions => ["login=? and hashed_password=?", 'admin', User.hash_password('admin')]).nil?],
[:text_file_repository_writable, File.writable?(Attachment.storage_path)],
[:text_plugin_assets_writable, File.writable?(Engines.public_directory)],
[:text_rmagick_available, Object.const_defined?(:Magick)]
[:text_plugin_assets_writable, File.writable?(Engines.public_directory)],
[:text_rmagick_available, Object.const_defined?(:Magick)]
]
end
end
+8 -42
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -18,8 +18,6 @@
require 'uri'
require 'cgi'
class Unauthorized < Exception; end
class ApplicationController < ActionController::Base
include Redmine::I18n
@@ -43,7 +41,6 @@ class ApplicationController < ActionController::Base
protect_from_forgery
rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
rescue_from ::Unauthorized, :with => :deny_access
include Redmine::Search::Controller
include Redmine::MenuManager::MenuController
@@ -71,11 +68,11 @@ class ApplicationController < ActionController::Base
user = User.try_to_autologin(cookies[:autologin])
session[:user_id] = user.id if user
user
elsif params[:format] == 'atom' && params[:key] && request.get? && accept_rss_auth?
elsif params[:format] == 'atom' && params[:key] && accept_key_auth_actions.include?(params[:action])
# RSS key authentication does not start a session
User.find_by_rss_key(params[:key])
elsif Setting.rest_api_enabled? && accept_api_auth?
if (key = api_key_from_request)
elsif Setting.rest_api_enabled? && api_request?
if (key = api_key_from_request) && accept_key_auth_actions.include?(params[:action])
# Use API key
User.find_by_api_key(key)
else
@@ -224,10 +221,6 @@ class ApplicationController < ActionController::Base
def find_issues
@issues = Issue.find_all_by_id(params[:id] || params[:ids])
raise ActiveRecord::RecordNotFound if @issues.empty?
if @issues.detect {|issue| !issue.visible?}
deny_access
return
end
@projects = @issues.collect(&:project).compact.uniq
@project = @projects.first if @projects.size == 1
rescue ActiveRecord::RecordNotFound
@@ -333,40 +326,13 @@ class ApplicationController < ActionController::Base
render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml'
end
# TODO: remove in Redmine 1.4
def self.accept_key_auth(*actions)
ActiveSupport::Deprecation.warn "ApplicationController.accept_key_auth is deprecated and will be removed in Redmine 1.4. Use accept_rss_auth (or accept_api_auth) instead."
accept_rss_auth(*actions)
actions = actions.flatten.map(&:to_s)
write_inheritable_attribute('accept_key_auth_actions', actions)
end
# TODO: remove in Redmine 1.4
def accept_key_auth_actions
ActiveSupport::Deprecation.warn "ApplicationController.accept_key_auth_actions is deprecated and will be removed in Redmine 1.4. Use accept_rss_auth (or accept_api_auth) instead."
self.class.accept_rss_auth
end
def self.accept_rss_auth(*actions)
if actions.any?
write_inheritable_attribute('accept_rss_auth_actions', actions)
else
read_inheritable_attribute('accept_rss_auth_actions') || []
end
end
def accept_rss_auth?(action=action_name)
self.class.accept_rss_auth.include?(action.to_sym)
end
def self.accept_api_auth(*actions)
if actions.any?
write_inheritable_attribute('accept_api_auth_actions', actions)
else
read_inheritable_attribute('accept_api_auth_actions') || []
end
end
def accept_api_auth?(action=action_name)
self.class.accept_api_auth.include?(action.to_sym)
self.class.read_inheritable_attribute('accept_key_auth_actions') || []
end
# Returns the number of objects that should be displayed
+1 -1
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
+3 -3
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -18,7 +18,7 @@
class BoardsController < ApplicationController
default_search_scope :messages
before_filter :find_project, :find_board_if_available, :authorize
accept_rss_auth :index, :show
accept_key_auth :index, :show
helper :messages
include MessagesHelper
+5 -17
View File
@@ -1,20 +1,3 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class CalendarsController < ApplicationController
menu_item :calendar
before_filter :find_optional_project
@@ -53,4 +36,9 @@ class CalendarsController < ApplicationController
render :action => 'show', :layout => false if request.xhr?
end
def update
show
end
end
@@ -1,6 +1,5 @@
class ContextMenusController < ApplicationController
helper :watchers
helper :issues
def issues
@issues = Issue.visible.all(:conditions => {:id => params[:ids]}, :include => :project)
@@ -42,17 +41,4 @@ class ContextMenusController < ApplicationController
render :layout => false
end
def time_entries
@time_entries = TimeEntry.all(
:conditions => {:id => params[:ids]}, :include => :project)
@projects = @time_entries.collect(&:project).compact.uniq
@project = @projects.first if @projects.size == 1
@activities = TimeEntryActivity.shared.active
@can = {:edit => User.current.allowed_to?(:log_time, @projects),
:update => User.current.allowed_to?(:log_time, @projects),
:delete => User.current.allowed_to?(:log_time, @projects)
}
@back = back_url
render :layout => false
end
end
+5 -17
View File
@@ -1,20 +1,3 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class GanttsController < ApplicationController
menu_item :gantt
before_filter :find_optional_project
@@ -45,4 +28,9 @@ class GanttsController < ApplicationController
format.pdf { send_data(@gantt.to_pdf, :type => 'application/pdf', :filename => "#{basename}.pdf") }
end
end
def update
show
end
end
+1 -1
View File
@@ -132,7 +132,7 @@ class GroupsController < ApplicationController
def autocomplete_for_user
@group = Group.find(params[:id])
@users = User.active.not_in_group(@group).like(params[:q]).all(:limit => 100)
@users = User.active.like(params[:q]).find(:all, :limit => 100) - @group.users
render :layout => false
end
@@ -1,4 +1,4 @@
# Redmine - project management software
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
+7 -18
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -27,8 +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_rss_auth :index, :show
accept_api_auth :index, :show, :create, :update, :destroy
accept_key_auth :index, :show, :create, :update, :destroy
rescue_from Query::StatementInvalid, :with => :query_statement_invalid
@@ -107,17 +106,13 @@ class IssuesController < ApplicationController
@journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
@journals.each_with_index {|j,i| j.indice = i+1}
@journals.reverse! if User.current.wants_comments_in_reverse_order?
if User.current.allowed_to?(:view_changesets, @project)
@changesets = @issue.changesets.visible.all
@changesets.reverse! if User.current.wants_comments_in_reverse_order?
end
@changesets = @issue.changesets.visible.all
@changesets.reverse! if User.current.wants_comments_in_reverse_order?
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
@edit_allowed = User.current.allowed_to?(:edit_issues, @project)
@priorities = IssuePriority.all
@time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
@time_entry = TimeEntry.new
respond_to do |format|
format.html { render :template => 'issues/show.rhtml' }
format.api
@@ -256,13 +251,7 @@ class IssuesController < ApplicationController
private
def find_issue
# Issue.visible.find(...) can not be used to redirect user to the login form
# if the issue actually exists but requires authentication
@issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
unless @issue.visible?
deny_access
return
end
@project = @issue.project
rescue ActiveRecord::RecordNotFound
render_404
@@ -282,7 +271,7 @@ private
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
@priorities = IssuePriority.all
@edit_allowed = User.current.allowed_to?(:edit_issues, @project)
@time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
@time_entry = TimeEntry.new
@time_entry.attributes = params[:time_entry]
@notes = params[:notes] || (params[:issue].present? ? params[:issue][:notes] : nil)
@@ -302,7 +291,6 @@ private
end
@issue.project = @project
@issue.author = User.current
# Tracker must be set before custom field values
@issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
if @issue.tracker.nil?
@@ -316,6 +304,7 @@ private
@issue.watcher_user_ids = params[:issue]['watcher_user_ids']
end
end
@issue.author = User.current
@priorities = IssuePriority.all
@allowed_statuses = @issue.new_statuses_allowed_to(User.current, true)
end
+7 -20
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,12 +16,11 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class JournalsController < ApplicationController
before_filter :find_journal, :only => [:edit, :diff]
before_filter :find_journal, :only => [:edit]
before_filter :find_issue, :only => [:new]
before_filter :find_optional_project, :only => [:index]
before_filter :authorize, :only => [:new, :edit, :diff]
accept_rss_auth :index
menu_item :issues
before_filter :authorize, :only => [:new, :edit]
accept_key_auth :index
helper :issues
helper :custom_fields
@@ -45,17 +44,6 @@ class JournalsController < ApplicationController
render_404
end
def diff
@issue = @journal.issue
if params[:detail_id].present?
@detail = @journal.details.find_by_id(params[:detail_id])
else
@detail = @journal.details.detect {|d| d.prop_key == 'description'}
end
(render_404; return false) unless @issue && @detail
@diff = Redmine::Helpers::Diff.new(@detail.value, @detail.old_value)
end
def new
journal = Journal.find(params[:journal_id]) if params[:journal_id]
if journal
@@ -80,7 +68,6 @@ class JournalsController < ApplicationController
end
def edit
(render_403; return false) unless @journal.editable_by?(User.current)
if request.post?
@journal.update_attributes(:notes => params[:notes]) if params[:notes]
@journal.destroy if @journal.details.empty? && @journal.notes.blank?
@@ -100,10 +87,10 @@ class JournalsController < ApplicationController
end
end
private
private
def find_journal
@journal = Journal.find(params[:id])
(render_403; return false) unless @journal.editable_by?(User.current)
@project = @journal.journalized.project
rescue ActiveRecord::RecordNotFound
render_404
+2 -2
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
+3 -6
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -23,10 +23,7 @@ class NewsController < ApplicationController
before_filter :find_project, :only => [:new, :create]
before_filter :authorize, :except => [:index]
before_filter :find_optional_project, :only => :index
accept_rss_auth :index
accept_api_auth :index
helper :watchers
accept_key_auth :index
def index
case params[:format]
-17
View File
@@ -1,20 +1,3 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class PreviewsController < ApplicationController
before_filter :find_project
+7 -7
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -24,8 +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_rss_auth :index
accept_api_auth :index, :show, :create, :update, :destroy
accept_key_auth :index, :show, :create, :update, :destroy
after_filter :only => [:create, :edit, :update, :archive, :unarchive, :destroy] do |controller|
if controller.request.post?
@@ -143,7 +142,7 @@ class ProjectsController < ApplicationController
end
@users_by_role = @project.users_by_role
@subprojects = @project.children.visible.all
@subprojects = @project.children.visible
@news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
@trackers = @project.rolled_up_trackers
@@ -156,10 +155,11 @@ class ProjectsController < ApplicationController
:include => [:project, :status, :tracker],
:conditions => cond)
if User.current.allowed_to?(:view_time_entries, @project)
@total_hours = TimeEntry.visible.sum(:hours, :include => :project, :conditions => cond).to_f
TimeEntry.visible_by(User.current) do
@total_hours = TimeEntry.sum(:hours,
:include => :project,
:conditions => cond).to_f
end
@key = User.current.rss_key
respond_to do |format|
+4 -7
View File
@@ -25,12 +25,11 @@ class QueriesController < ApplicationController
@query.project = params[:query_is_for_all] ? nil : @project
@query.user = User.current
@query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
@query.add_filters(params[:fields] || params[:f], params[:operators] || params[:op], params[:values] || params[:v]) if params[:fields] || params[:f]
@query.group_by ||= params[:group_by]
@query.column_names = params[:c] if params[:c]
@query.column_names = nil if params[:default_columns]
@query.add_filters(params[:fields], params[:operators], params[:values]) if params[:fields]
@query.group_by ||= params[:group_by]
if request.post? && params[:confirm] && @query.save
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'issues', :action => 'index', :project_id => @project, :query_id => @query
@@ -42,12 +41,10 @@ class QueriesController < ApplicationController
def edit
if request.post?
@query.filters = {}
@query.add_filters(params[:fields] || params[:f], params[:operators] || params[:op], params[:values] || params[:v]) if params[:fields] || params[:f]
@query.add_filters(params[:fields], params[:operators], params[:values]) if params[:fields]
@query.attributes = params[:query]
@query.project = nil if params[:query_is_for_all]
@query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
@query.group_by ||= params[:group_by]
@query.column_names = params[:c] if params[:c]
@query.column_names = nil if params[:default_columns]
if @query.save
+24 -63
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -30,36 +30,24 @@ class RepositoriesController < ApplicationController
before_filter :find_repository, :except => :edit
before_filter :find_project, :only => :edit
before_filter :authorize
accept_rss_auth :revisions
accept_key_auth :revisions
rescue_from Redmine::Scm::Adapters::CommandFailed, :with => :show_error_command_failed
def edit
@repository = @project.repository
if !@repository && !params[:repository_scm].blank?
if !@repository
@repository = Repository.factory(params[:repository_scm])
@repository.project = @project if @repository
end
if request.post? && @repository
p1 = params[:repository]
p = {}
p_extra = {}
p1.each do |k, v|
if k =~ /^extra_/
p_extra[k] = v
else
p[k] = v
end
end
@repository.attributes = p
@repository.merge_extra_info(p_extra)
@repository.attributes = params[:repository]
@repository.save
end
render(:update) do |page|
page.replace_html "tab-content-repository",
:partial => 'projects/settings/repository'
page.replace_html "tab-content-repository", :partial => 'projects/settings/repository'
if @repository && !@project.repository
@project.reload # needed to reload association
@project.reload #needed to reload association
page.replace_html "main-menu", render_main_menu(@project)
end
end
@@ -82,17 +70,13 @@ class RepositoriesController < ApplicationController
def destroy
@repository.destroy
redirect_to :controller => 'projects',
:action => 'settings',
:id => @project,
:tab => 'repository'
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
end
def show
@repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty?
@entries = @repository.entries(@path, @rev)
@changeset = @repository.find_changeset_by_name(@rev)
if request.xhr?
@entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
else
@@ -110,18 +94,17 @@ class RepositoriesController < ApplicationController
(show_error_not_found; return) unless @entry
@changesets = @repository.latest_changesets(@path, @rev, Setting.repository_log_display_limit.to_i)
@properties = @repository.properties(@path, @rev)
@changeset = @repository.find_changeset_by_name(@rev)
end
def revisions
@changeset_count = @repository.changesets.count
@changeset_pages = Paginator.new self, @changeset_count,
per_page_option,
params['page']
per_page_option,
params['page']
@changesets = @repository.changesets.find(:all,
:limit => @changeset_pages.items_per_page,
:offset => @changeset_pages.current.offset,
:include => [:user, :repository])
:limit => @changeset_pages.items_per_page,
:offset => @changeset_pages.current.offset,
:include => [:user, :repository])
respond_to do |format|
format.html { render :layout => false if request.xhr? }
@@ -138,46 +121,25 @@ class RepositoriesController < ApplicationController
@content = @repository.cat(@path, @rev)
(show_error_not_found; return) unless @content
if 'raw' == params[:format] ||
(@content.size && @content.size > Setting.file_max_size_displayed.to_i.kilobyte) ||
! is_entry_text_data?(@content, @path)
if 'raw' == params[:format] || @content.is_binary_data? || (@entry.size && @entry.size > Setting.file_max_size_displayed.to_i.kilobyte)
# Force the download
send_opt = { :filename => filename_for_content_disposition(@path.split('/').last) }
send_type = Redmine::MimeType.of(@path)
send_opt[:type] = send_type.to_s if send_type
send_data @content, send_opt
send_data @content, :filename => filename_for_content_disposition(@path.split('/').last)
else
# Prevent empty lines when displaying a file with Windows style eol
# TODO: UTF-16
# Is this needs? AttachmentsController reads file simply.
@content.gsub!("\r\n", "\n")
@changeset = @repository.find_changeset_by_name(@rev)
end
end
end
def is_entry_text_data?(ent, path)
# UTF-16 contains "\x00".
# It is very strict that file contains less than 30% of ascii symbols
# in non Western Europe.
return true if Redmine::MimeType.is_type?('text', path)
# Ruby 1.8.6 has a bug of integer divisions.
# http://apidock.com/ruby/v1_8_6_287/String/is_binary_data%3F
return false if ent.is_binary_data?
true
end
private :is_entry_text_data?
def annotate
@entry = @repository.entry(@path, @rev)
(show_error_not_found; return) unless @entry
@annotate = @repository.scm.annotate(@path, @rev)
(render_error l(:error_scm_annotate); return) if @annotate.nil? || @annotate.empty?
@changeset = @repository.find_changeset_by_name(@rev)
end
def revision
raise ChangesetNotFound if @rev.blank?
raise ChangesetNotFound if @rev.nil? || @rev.empty?
@changeset = @repository.find_changeset_by_name(@rev)
raise ChangesetNotFound unless @changeset
@@ -207,8 +169,8 @@ class RepositoriesController < ApplicationController
User.current.pref[:diff_type] = @diff_type
User.current.preference.save
end
@cache_key = "repositories/diff/#{@repository.id}/" +
Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}-#{current_language}")
@cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}")
unless read_fragment(@cache_key)
@diff = @repository.diff(@path, @rev, @rev_to)
show_error_not_found unless @diff
@@ -216,7 +178,6 @@ class RepositoriesController < ApplicationController
@changeset = @repository.find_changeset_by_name(@rev)
@changeset_to = @rev_to ? @repository.find_changeset_by_name(@rev_to) : nil
@diff_format_revisions = @repository.diff_format_revisions(@changeset, @changeset_to)
end
end
@@ -276,15 +237,11 @@ class RepositoriesController < ApplicationController
@date_to = Date.today
@date_from = @date_to << 11
@date_from = Date.civil(@date_from.year, @date_from.month, 1)
commits_by_day = repository.changesets.count(
:all, :group => :commit_date,
:conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
commits_by_day = repository.changesets.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
commits_by_month = [0] * 12
commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
changes_by_day = repository.changes.count(
:all, :group => :commit_date,
:conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
changes_by_day = repository.changes.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
changes_by_month = [0] * 12
changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
@@ -345,16 +302,20 @@ class RepositoriesController < ApplicationController
:graph_title => l(:label_commits_per_author),
:show_graph_title => true
)
graph.add_data(
:data => commits_data,
:title => l(:label_revision_plural)
)
graph.add_data(
:data => changes_data,
:title => l(:label_change_plural)
)
graph.burn
end
end
class Date
+4 -4
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -24,8 +24,8 @@ class SearchController < ApplicationController
def index
@question = params[:q] || ""
@question.strip!
@all_words = params[:all_words] ? params[:all_words].present? : true
@titles_only = params[:titles_only] ? params[:titles_only].present? : false
@all_words = params[:all_words] || (params[:submit] ? false : true)
@titles_only = !params[:titles_only].nil?
projects_to_search =
case params[:scope]
+2 -2
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
+67 -119
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2010 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -18,12 +18,10 @@
class TimelogController < ApplicationController
menu_item :issues
before_filter :find_project, :only => [:new, :create]
before_filter :find_time_entry, :only => [:show, :edit, :update]
before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
before_filter :find_time_entry, :only => [:show, :edit, :update, :destroy]
before_filter :authorize, :except => [:index]
before_filter :find_optional_project, :only => [:index]
accept_rss_auth :index
accept_api_auth :index, :show, :create, :update, :destroy
accept_key_auth :index, :show, :create, :update, :destroy
helper :sort
include SortHelper
@@ -42,56 +40,60 @@ class TimelogController < ApplicationController
'hours' => 'hours'
cond = ARCondition.new
if @issue
cond << "#{Issue.table_name}.root_id = #{@issue.root_id} AND #{Issue.table_name}.lft >= #{@issue.lft} AND #{Issue.table_name}.rgt <= #{@issue.rgt}"
elsif @project
if @project.nil?
cond << Project.allowed_to_condition(User.current, :view_time_entries)
elsif @issue.nil?
cond << @project.project_condition(Setting.display_subprojects_issues?)
else
cond << "#{Issue.table_name}.root_id = #{@issue.root_id} AND #{Issue.table_name}.lft >= #{@issue.lft} AND #{Issue.table_name}.rgt <= #{@issue.rgt}"
end
retrieve_date_range
cond << ['spent_on BETWEEN ? AND ?', @from, @to]
respond_to do |format|
format.html {
# Paginate results
@entry_count = TimeEntry.visible.count(:include => [:project, :issue], :conditions => cond.conditions)
@entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
@entries = TimeEntry.visible.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
:conditions => cond.conditions,
:order => sort_clause,
:limit => @entry_pages.items_per_page,
:offset => @entry_pages.current.offset)
@total_hours = TimeEntry.visible.sum(:hours, :include => [:project, :issue], :conditions => cond.conditions).to_f
TimeEntry.visible_by(User.current) do
respond_to do |format|
format.html {
# Paginate results
@entry_count = TimeEntry.count(:include => [:project, :issue], :conditions => cond.conditions)
@entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
@entries = TimeEntry.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
:conditions => cond.conditions,
:order => sort_clause,
:limit => @entry_pages.items_per_page,
:offset => @entry_pages.current.offset)
@total_hours = TimeEntry.sum(:hours, :include => [:project, :issue], :conditions => cond.conditions).to_f
render :layout => !request.xhr?
}
format.api {
@entry_count = TimeEntry.visible.count(:include => [:project, :issue], :conditions => cond.conditions)
@offset, @limit = api_offset_and_limit
@entries = TimeEntry.visible.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
:conditions => cond.conditions,
:order => sort_clause,
:limit => @limit,
:offset => @offset)
}
format.atom {
entries = TimeEntry.visible.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
:conditions => cond.conditions,
:order => "#{TimeEntry.table_name}.created_on DESC",
:limit => Setting.feeds_limit.to_i)
render_feed(entries, :title => l(:label_spent_time))
}
format.csv {
# Export all entries
@entries = TimeEntry.visible.find(:all,
:include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}],
:conditions => cond.conditions,
:order => sort_clause)
send_data(entries_to_csv(@entries), :type => 'text/csv; header=present', :filename => 'timelog.csv')
}
render :layout => !request.xhr?
}
format.api {
@entry_count = TimeEntry.count(:include => [:project, :issue], :conditions => cond.conditions)
@entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
@entries = TimeEntry.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
:conditions => cond.conditions,
:order => sort_clause,
:limit => @entry_pages.items_per_page,
:offset => @entry_pages.current.offset)
}
format.atom {
entries = TimeEntry.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
:conditions => cond.conditions,
:order => "#{TimeEntry.table_name}.created_on DESC",
:limit => Setting.feeds_limit.to_i)
render_feed(entries, :title => l(:label_spent_time))
}
format.csv {
# Export all entries
@entries = TimeEntry.find(:all,
:include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}],
:conditions => cond.conditions,
:order => sort_clause)
send_data(entries_to_csv(@entries), :type => 'text/csv; header=present', :filename => 'timelog.csv')
}
end
end
end
@@ -162,55 +164,27 @@ class TimelogController < ApplicationController
end
end
def bulk_edit
@available_activities = TimeEntryActivity.shared.active
@custom_fields = TimeEntry.first.available_custom_fields
end
def bulk_update
attributes = parse_params_for_bulk_time_entry_attributes(params)
unsaved_time_entry_ids = []
@time_entries.each do |time_entry|
time_entry.reload
time_entry.attributes = attributes
call_hook(:controller_time_entries_bulk_edit_before_save, { :params => params, :time_entry => time_entry })
unless time_entry.save
# Keep unsaved time_entry ids to display them in flash error
unsaved_time_entry_ids << time_entry.id
end
end
set_flash_from_bulk_time_entry_save(@time_entries, unsaved_time_entry_ids)
redirect_back_or_default({:controller => 'timelog', :action => 'index', :project_id => @projects.first})
end
verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
def destroy
@time_entries.each do |t|
begin
unless t.destroy && t.destroyed?
respond_to do |format|
format.html {
flash[:error] = l(:notice_unable_delete_time_entry)
redirect_to :back
}
format.api { render_validation_errors(t) }
end
return
end
rescue ::ActionController::RedirectBackError
redirect_to :action => 'index', :project_id => @projects.first
return
if @time_entry.destroy && @time_entry.destroyed?
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_delete)
redirect_to :back
}
format.api { head :ok }
end
else
respond_to do |format|
format.html {
flash[:error] = l(:notice_unable_delete_time_entry)
redirect_to :back
}
format.api { render_validation_errors(@time_entry) }
end
end
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_delete)
redirect_back_or_default(:action => 'index', :project_id => @projects.first)
}
format.api { head :ok }
end
rescue ::ActionController::RedirectBackError
redirect_to :action => 'index', :project_id => @time_entry.project
end
private
@@ -225,26 +199,6 @@ private
render_404
end
def find_time_entries
@time_entries = TimeEntry.find_all_by_id(params[:id] || params[:ids])
raise ActiveRecord::RecordNotFound if @time_entries.empty?
@projects = @time_entries.collect(&:project).compact.uniq
@project = @projects.first if @projects.size == 1
rescue ActiveRecord::RecordNotFound
render_404
end
def set_flash_from_bulk_time_entry_save(time_entries, unsaved_time_entry_ids)
if unsaved_time_entry_ids.empty?
flash[:notice] = l(:notice_successful_update) unless time_entries.empty?
else
flash[:error] = l(:notice_failed_to_save_time_entries,
:count => unsaved_time_entry_ids.size,
:total => time_entries.size,
:ids => '#' + unsaved_time_entry_ids.join(', #'))
end
end
def find_project
if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).present?
@issue = Issue.find(issue_id)
@@ -315,10 +269,4 @@ private
@to ||= (TimeEntry.latest_date_for_project(@project) || Date.today)
end
def parse_params_for_bulk_time_entry_attributes(params)
attributes = (params[:time_entry] || {}).reject {|k,v| v.blank?}
attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
attributes
end
end
+9 -24
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2010 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -19,8 +19,8 @@ class UsersController < ApplicationController
layout 'admin'
before_filter :require_admin, :except => :show
before_filter :find_user, :only => [:show, :edit, :update, :destroy, :edit_membership, :destroy_membership]
accept_api_auth :index, :show, :create, :update, :destroy
before_filter :find_user, :only => [:show, :edit, :update, :edit_membership, :destroy_membership]
accept_key_auth :index, :show, :create, :update
helper :sort
include SortHelper
@@ -38,9 +38,6 @@ class UsersController < ApplicationController
@limit = per_page_option
end
scope = User
scope = scope.in_group(params[:group_id].to_i) if params[:group_id].present?
@status = params[:status] ? params[:status].to_i : 1
c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
@@ -49,27 +46,24 @@ class UsersController < ApplicationController
c << ["LOWER(login) LIKE ? OR LOWER(firstname) LIKE ? OR LOWER(lastname) LIKE ? OR LOWER(mail) LIKE ?", name, name, name, name]
end
@user_count = scope.count(:conditions => c.conditions)
@user_count = User.count(:conditions => c.conditions)
@user_pages = Paginator.new self, @user_count, @limit, params['page']
@offset ||= @user_pages.current.offset
@users = scope.find :all,
@users = User.find :all,
:order => sort_clause,
:conditions => c.conditions,
:limit => @limit,
:offset => @offset
respond_to do |format|
format.html {
@groups = Group.all.sort
render :layout => !request.xhr?
}
respond_to do |format|
format.html { render :layout => !request.xhr? }
format.api
end
end
end
def show
# show projects based on current user visibility
@memberships = @user.memberships.all(:conditions => Project.visible_condition(User.current))
@memberships = @user.memberships.all(:conditions => Project.visible_by(User.current))
events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
@events_by_day = events.group_by(&:event_date)
@@ -183,15 +177,6 @@ class UsersController < ApplicationController
redirect_to :controller => 'users', :action => 'edit', :id => @user
end
verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
def destroy
@user.destroy
respond_to do |format|
format.html { redirect_to(users_url) }
format.api { head :ok }
end
end
def edit_membership
@membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
@membership.save if request.post?
+13 -5
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -76,13 +76,21 @@ private
def set_watcher(user, watching)
@watched.set_watcher(user, watching)
if params[:replace].present?
if params[:replace].is_a? Array
replace_ids = params[:replace]
else
replace_ids = [params[:replace]]
end
else
replace_ids = ['watcher']
end
respond_to do |format|
format.html { redirect_to :back }
format.js do
render(:update) do |page|
c = watcher_css(@watched)
page.select(".#{c}").each do |item|
page.replace_html item, watcher_link(@watched, user)
replace_ids.each do |replace_id|
page.replace_html replace_id, watcher_link(@watched, user, :replace => replace_ids)
end
end
end
+22 -29
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -34,27 +34,23 @@ require 'diff'
class WikiController < ApplicationController
default_search_scope :wiki_pages
before_filter :find_wiki, :authorize
before_filter :find_existing_or_new_page, :only => [:show, :edit, :update]
before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy]
verify :method => :post, :only => [:protect], :redirect_to => { :action => :show }
helper :attachments
include AttachmentsHelper
helper :watchers
# List of pages, sorted alphabetically and by parent (hierarchy)
def index
load_pages_for_index
@pages_by_parent_id = @pages.group_by(&:parent_id)
end
# List of page, by last update
def date_index
load_pages_for_index
@pages_by_date = @pages.group_by {|p| p.updated_on.to_date}
load_pages_grouped_by_date_without_content
end
# display a page (in editing mode if it doesn't exist)
def show
page_title = params[:id]
@page = @wiki.find_or_new_page(page_title)
if @page.new_record?
if User.current.allowed_to?(:edit_wiki_pages, @project) && editable?
edit
@@ -86,6 +82,7 @@ class WikiController < ApplicationController
# edit an existing page or a new one
def edit
@page = @wiki.find_or_new_page(params[:id])
return render_403 unless editable?
@page.content = WikiContent.new(:page => @page) if @page.new_record?
@@ -101,6 +98,7 @@ class WikiController < ApplicationController
verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
# Creates a new page or updates an existing one
def update
@page = @wiki.find_or_new_page(params[:id])
return render_403 unless editable?
@page.content = WikiContent.new(:page => @page) if @page.new_record?
@@ -146,7 +144,6 @@ class WikiController < ApplicationController
end
end
verify :method => :post, :only => :protect, :redirect_to => { :action => :show }
def protect
@page.update_attribute :protected, params[:protected]
redirect_to :action => 'show', :project_id => @project, :id => @page.title
@@ -217,6 +214,10 @@ class WikiController < ApplicationController
end
end
def date_index
load_pages_grouped_by_date_without_content
end
def preview
page = @wiki.find_page(params[:id])
# page is nil when previewing a new page
@@ -246,24 +247,10 @@ private
render_404
end
# Finds the requested page or a new page if it doesn't exist
def find_existing_or_new_page
@page = @wiki.find_or_new_page(params[:id])
if @wiki.page_found_with_redirect?
redirect_to params.update(:id => @page.title)
end
end
# Finds the requested page and returns a 404 error if it doesn't exist
def find_existing_page
@page = @wiki.find_page(params[:id])
if @page.nil?
render_404
return
end
if @wiki.page_found_with_redirect?
redirect_to params.update(:id => @page.title)
end
render_404 if @page.nil?
end
# Returns true if the current user is allowed to edit the page, otherwise false
@@ -278,7 +265,13 @@ private
helper.instance_method(:initial_page_content).bind(self).call(page)
end
def load_pages_for_index
@pages = @wiki.pages.with_updated_on.all(:order => 'title', :include => {:wiki => :project})
# eager load information about last updates, without loading text
def load_pages_grouped_by_date_without_content
@pages = @wiki.pages.find :all, :select => "#{WikiPage.table_name}.*, #{WikiContent.table_name}.updated_on",
:joins => "LEFT JOIN #{WikiContent.table_name} ON #{WikiContent.table_name}.page_id = #{WikiPage.table_name}.id",
:order => 'title'
@pages_by_date = @pages.group_by {|p| p.updated_on.to_date}
@pages_by_parent_id = @pages.group_by(&:parent_id)
end
end
+3 -14
View File
@@ -32,17 +32,14 @@ class WorkflowsController < ApplicationController
if request.post?
Workflow.destroy_all( ["role_id=? and tracker_id=?", @role.id, @tracker.id])
(params[:issue_status] || []).each { |status_id, transitions|
transitions.each { |new_status_id, options|
author = options.is_a?(Array) && options.include?('author') && !options.include?('always')
assignee = options.is_a?(Array) && options.include?('assignee') && !options.include?('always')
@role.workflows.build(:tracker_id => @tracker.id, :old_status_id => status_id, :new_status_id => new_status_id, :author => author, :assignee => assignee)
(params[:issue_status] || []).each { |old, news|
news.each { |new|
@role.workflows.build(:tracker_id => @tracker.id, :old_status_id => old, :new_status_id => new)
}
}
if @role.save
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'edit', :role_id => @role, :tracker_id => @tracker
return
end
end
@@ -51,14 +48,6 @@ class WorkflowsController < ApplicationController
@statuses = @tracker.issue_statuses
end
@statuses ||= IssueStatus.find(:all, :order => 'position')
if @tracker && @role && @statuses.any?
workflows = Workflow.all(:conditions => {:role_id => @role.id, :tracker_id => @tracker.id})
@workflows = {}
@workflows['always'] = workflows.select {|w| !w.author && !w.assignee}
@workflows['author'] = workflows.select {|w| w.author}
@workflows['assignee'] = workflows.select {|w| w.assignee}
end
end
def copy
+51 -81
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2010 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -110,20 +110,6 @@ module ApplicationHelper
:title => l(:label_revision_id, format_revision(revision)))
end
# Generates a link to a message
def link_to_message(message, options={}, html_options = nil)
link_to(
h(truncate(message.subject, :length => 60)),
{ :controller => 'messages', :action => 'show',
:board_id => message.board_id,
:id => message.root,
:r => (message.parent_id && message.id),
:anchor => (message.parent_id ? "message-#{message.id}" : nil)
}.merge(options),
html_options
)
end
# Generates a link to a project if active
# Examples:
#
@@ -187,15 +173,15 @@ module ApplicationHelper
end
end
def render_page_hierarchy(pages, node=nil, options={})
def render_page_hierarchy(pages, node=nil)
content = ''
if pages[node]
content << "<ul class=\"pages-hierarchy\">\n"
pages[node].each do |page|
content << "<li>"
content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title},
:title => (options[:timestamp] && page.updated_on ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
content << "\n" + render_page_hierarchy(pages, page.id, options) if pages[page.id]
:title => (page.respond_to?(:updated_on) ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
content << "\n" + render_page_hierarchy(pages, page.id) if pages[page.id]
content << "</li>\n"
end
content << "</ul>\n"
@@ -223,8 +209,8 @@ module ApplicationHelper
# Renders the project quick-jump box
def render_project_jump_box
return unless User.current.logged?
projects = User.current.memberships.collect(&:project).compact.uniq
# Retrieve them now to avoid a COUNT query
projects = User.current.projects.all
if projects.any?
s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
"<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
@@ -336,18 +322,20 @@ module ApplicationHelper
page_param = options.delete(:page_param) || :page
per_page_links = options.delete(:per_page_links)
url_param = params.dup
# don't reuse query params if filters are present
url_param.merge!(:fields => nil, :values => nil, :operators => nil) if url_param.delete(:set_filter)
html = ''
if paginator.current.previous
html << link_to_content_update('&#171; ' + l(:label_previous), url_param.merge(page_param => paginator.current.previous)) + ' '
html << link_to_remote_content_update('&#171; ' + l(:label_previous), url_param.merge(page_param => paginator.current.previous)) + ' '
end
html << (pagination_links_each(paginator, options) do |n|
link_to_content_update(n.to_s, url_param.merge(page_param => n))
link_to_remote_content_update(n.to_s, url_param.merge(page_param => n))
end || '')
if paginator.current.next
html << ' ' + link_to_content_update((l(:label_next) + ' &#187;'), url_param.merge(page_param => paginator.current.next))
html << ' ' + link_to_remote_content_update((l(:label_next) + ' &#187;'), url_param.merge(page_param => paginator.current.next))
end
unless count.nil?
@@ -361,8 +349,14 @@ module ApplicationHelper
end
def per_page_links(selected=nil)
url_param = params.dup
url_param.clear if url_param.has_key?(:set_filter)
links = Setting.per_page_options_array.collect do |n|
n == selected ? n : link_to_content_update(n, params.merge(:per_page => n))
n == selected ? n : link_to_remote(n, {:update => "content",
:url => params.dup.merge(:per_page => n),
:method => :get},
{:href => url_for(url_param.merge(:per_page => n))})
end
links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
end
@@ -390,7 +384,7 @@ module ApplicationHelper
h(Setting.app_title)
else
b = []
ancestors = (@project.root? ? [] : @project.ancestors.visible.all)
ancestors = (@project.root? ? [] : @project.ancestors.visible)
if ancestors.any?
root = ancestors.shift
b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
@@ -550,20 +544,15 @@ module ApplicationHelper
if page =~ /^(.+?)\#(.+)$/
page, anchor = $1, $2
end
anchor = sanitize_anchor_name(anchor) if anchor.present?
# check if page exists
wiki_page = link_project.wiki.find_page(page)
url = if anchor.present? && wiki_page.present? && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version)) && obj.page == wiki_page
"##{anchor}"
else
case options[:wiki_links]
when :local; "#{page.present? ? Wiki.titleize(page) : ''}.html" + (anchor.present? ? "##{anchor}" : '')
url = case options[:wiki_links]
when :local; "#{title}.html"
when :anchor; "##{title}" # used for single-file wiki export
else
wiki_page_id = page.present? ? Wiki.titleize(page) : nil
url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project, :id => wiki_page_id, :anchor => anchor)
end
end
link_to((title || page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
else
# project or wiki doesn't exist
@@ -599,26 +588,16 @@ module ApplicationHelper
# source:some/file#L120 -> Link to line 120 of the file
# source:some/file@52#L120 -> Link to line 120 of the file's revision 52
# export:some/file -> Force the download of the file
# Forum messages:
# Forum messages:
# message#1218 -> Link to message with id 1218
#
# Links can refer other objects from other projects, using project identifier:
# identifier:r52
# identifier:document:"Some document"
# identifier:version:1.0.0
# identifier:source:some/file
def parse_redmine_links(text, project, obj, attr, only_path, options)
text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-]+):)?(attachment|document|version|commit|source|export|message|project)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|\]|<|$)}) do |m|
leading, esc, project_prefix, project_identifier, prefix, sep, identifier = $1, $2, $3, $4, $5, $7 || $9, $8 || $10
text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(attachment|document|version|commit|source|export|message|project)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|\]|<|$)}) do |m|
leading, esc, prefix, sep, identifier = $1, $2, $3, $5 || $7, $6 || $8
link = nil
if project_identifier
project = Project.visible.find_by_identifier(project_identifier)
end
if esc.nil?
if prefix.nil? && sep == 'r'
# project.changesets.visible raises an SQL error because of a double join on repositories
if project && project.repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(project.repository.id, identifier))
link = link_to("#{project_prefix}r#{identifier}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
if project && (changeset = project.changesets.find_by_revision(identifier))
link = link_to("r#{identifier}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
:class => 'changeset',
:title => truncate_single_line(changeset.comments, :length => 100))
end
@@ -632,18 +611,24 @@ module ApplicationHelper
:title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
end
when 'document'
if document = Document.visible.find_by_id(oid)
if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
:class => 'document'
end
when 'version'
if version = Version.visible.find_by_id(oid)
if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
:class => 'version'
end
when 'message'
if message = Message.visible.find_by_id(oid, :include => :parent)
link = link_to_message(message, {:only_path => only_path}, :class => 'message')
if message = Message.find_by_id(oid, :include => [:parent, {:board => :project}], :conditions => Project.visible_by(User.current))
link = link_to h(truncate(message.subject, :length => 60)), {:only_path => only_path,
:controller => 'messages',
:action => 'show',
:board_id => message.board,
:id => message.root,
:anchor => (message.parent ? "message-#{message.id}" : nil)},
:class => 'message'
end
when 'project'
if p = Project.visible.find_by_id(oid)
@@ -655,26 +640,26 @@ module ApplicationHelper
name = identifier.gsub(%r{^"(.*)"$}, "\\1")
case prefix
when 'document'
if project && document = project.documents.visible.find_by_title(name)
if project && document = project.documents.find_by_title(name)
link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
:class => 'document'
end
when 'version'
if project && version = project.versions.visible.find_by_name(name)
if project && version = project.versions.find_by_name(name)
link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
:class => 'version'
end
when 'commit'
if project && project.repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", project.repository.id, "#{name}%"]))
link = link_to h("#{project_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.identifier},
if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
:class => 'changeset',
:title => truncate_single_line(changeset.comments, :length => 100)
end
when 'source', 'export'
if project && project.repository && User.current.allowed_to?(:browse_repository, project)
if project && project.repository
name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
path, rev, anchor = $1, $3, $5
link = link_to h("#{project_prefix}#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
:path => to_path_param(path),
:rev => rev,
:anchor => anchor,
@@ -694,7 +679,7 @@ module ApplicationHelper
end
end
end
leading + (link || "#{project_prefix}#{prefix}#{sep}#{identifier}")
leading + (link || "#{prefix}#{sep}#{identifier}")
end
end
@@ -708,9 +693,9 @@ module ApplicationHelper
text.gsub!(HEADING_RE) do
level, attrs, content = $1.to_i, $2, $3
item = strip_tags(content).strip
anchor = sanitize_anchor_name(item)
anchor = item.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
@parsed_headings << [level, anchor, item]
"<a name=\"#{anchor}\"></a>\n<h#{level} #{attrs}>#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a></h#{level}>"
"<h#{level} #{attrs} id=\"#{anchor}\">#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a></h#{level}>"
end
end
@@ -852,8 +837,6 @@ module ApplicationHelper
'Calendar._FD = 1;' # Monday
when 7
'Calendar._FD = 0;' # Sunday
when 6
'Calendar._FD = 6;' # Saturday
else
'' # use language
end
@@ -894,27 +877,10 @@ module ApplicationHelper
end
end
def sanitize_anchor_name(anchor)
anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
end
# Returns the javascript tags that are included in the html layout head
def javascript_heads
tags = javascript_include_tag(:defaults)
unless User.current.pref.warn_on_leaving_unsaved == '0'
tags << "\n" + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
end
tags
end
def favicon
"<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />"
end
def robot_exclusion_tag
'<meta name="robots" content="noindex,follow,noarchive" />'
end
# Returns true if arg is expected in the API response
def include_in_api_response?(arg)
unless @included_in_api_response
@@ -945,7 +911,11 @@ module ApplicationHelper
return self
end
def link_to_content_update(text, url_params = {}, html_options = {})
link_to(text, url_params, html_options)
def link_to_remote_content_update(text, url_params)
link_to_remote(text,
{:url => url_params, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'},
{:href => url_for(:params => url_params)}
)
end
end
+3 -15
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -29,18 +29,6 @@ module AttachmentsHelper
end
def to_utf8(str)
if str.respond_to?(:force_encoding)
str.force_encoding('UTF-8')
return str if str.valid_encoding?
else
return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
end
begin
Iconv.conv('UTF-8//IGNORE', 'UTF-8', str + ' ')[0..-3]
rescue Iconv::InvalidEncoding
# "UTF-8//IGNORE" is not supported on some OS
str
end
str
end
end
+9 -1
View File
@@ -32,6 +32,14 @@ module CalendarsHelper
end
def link_to_month(link_name, year, month, options={})
link_to_content_update(link_name, params.merge(:year => year, :month => month))
project_id = options[:project].present? ? options[:project].to_param : nil
link_target = calendar_path(:year => year, :month => month, :project_id => project_id)
link_to_remote(link_name,
{:update => "content", :url => link_target, :method => :put},
{:href => link_target})
end
end
+5 -5
View File
@@ -49,7 +49,7 @@ module CustomFieldsHelper
blank_option = custom_field.is_required? ?
(custom_field.default_value.blank? ? "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" : '') :
'<option></option>'
select_tag(field_name, blank_option + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value), :id => field_id)
select_tag(field_name, blank_option + options_for_select(custom_field.possible_values, custom_value.value), :id => field_id)
else
text_field_tag(field_name, custom_value.value, :id => field_id)
end
@@ -68,7 +68,7 @@ module CustomFieldsHelper
custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
end
def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil)
def custom_field_tag_for_bulk_edit(name, custom_field)
field_name = "#{name}[custom_field_values][#{custom_field.id}]"
field_id = "#{name}_custom_field_values_#{custom_field.id}"
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
@@ -83,7 +83,7 @@ module CustomFieldsHelper
[l(:general_text_yes), '1'],
[l(:general_text_no), '0']]), :id => field_id)
when "list"
select_tag(field_name, options_for_select([[l(:label_no_change_option), '']] + custom_field.possible_values_options(projects)), :id => field_id)
select_tag(field_name, options_for_select([[l(:label_no_change_option), '']] + custom_field.possible_values), :id => field_id)
else
text_field_tag(field_name, '', :id => field_id)
end
@@ -101,8 +101,8 @@ module CustomFieldsHelper
end
# Return an array of custom field formats which can be used in select_tag
def custom_field_formats_for_select(custom_field)
Redmine::CustomFieldFormat.as_select(custom_field.class.customized_class.name)
def custom_field_formats_for_select
Redmine::CustomFieldFormat.as_select
end
# Renders the custom_values in api views
+16 -8
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -21,21 +21,29 @@ module GanttHelper
case in_or_out
when :in
if gantt.zoom < 4
link_to_content_update l(:text_zoom_in),
params.merge(gantt.params.merge(:zoom => (gantt.zoom+1))),
:class => 'icon icon-zoom-in'
link_to_remote(l(:text_zoom_in),
{:url => gantt.params.merge(:zoom => (gantt.zoom+1)), :method => :get, :update => 'content'},
{:href => url_for(gantt.params.merge(:zoom => (gantt.zoom+1))),
:class => 'icon icon-zoom-in'})
else
content_tag('span', l(:text_zoom_in), :class => 'icon icon-zoom-in')
end
when :out
if gantt.zoom > 1
link_to_content_update l(:text_zoom_out),
params.merge(gantt.params.merge(:zoom => (gantt.zoom-1))),
:class => 'icon icon-zoom-out'
link_to_remote(l(:text_zoom_out),
{:url => gantt.params.merge(:zoom => (gantt.zoom-1)), :method => :get, :update => 'content'},
{:href => url_for(gantt.params.merge(:zoom => (gantt.zoom-1))),
:class => 'icon icon-zoom-out'})
else
content_tag('span', l(:text_zoom_out), :class => 'icon icon-zoom-out')
end
end
end
def number_of_issues_on_versions(gantt)
versions = gantt.events.collect {|event| (event.is_a? Version) ? event : nil}.compact
versions.sum {|v| v.fixed_issues.for_gantt.with_query(@query).count}
end
end
+8 -68
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -55,29 +55,19 @@ module IssuesHelper
"<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
end
def issue_heading(issue)
h("#{issue.tracker} ##{issue.id}")
end
def render_issue_subject_with_tree(issue)
s = ''
ancestors = issue.root? ? [] : issue.ancestors.visible.all
ancestors.each do |ancestor|
issue.ancestors.each do |ancestor|
s << '<div>' + content_tag('p', link_to_issue(ancestor))
end
s << '<div>'
subject = h(issue.subject)
if issue.is_private?
subject = content_tag('span', l(:field_is_private), :class => 'private') + ' ' + subject
end
s << content_tag('h3', subject)
s << '</div>' * (ancestors.size + 1)
s << '<div>' + content_tag('h3', h(issue.subject))
s << '</div>' * (issue.ancestors.size + 1)
s
end
def render_descendants_tree(issue)
s = '<form><table class="list issues">'
issue_list(issue.descendants.visible.sort_by(&:lft)) do |child, level|
issue_list(issue.descendants.sort_by(&:lft)) do |child, level|
s << content_tag('tr',
content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
content_tag('td', link_to_issue(child, :truncate => 60), :class => 'subject') +
@@ -109,24 +99,6 @@ module IssuesHelper
s
end
def issues_destroy_confirmation_message(issues)
issues = [issues] unless issues.is_a?(Array)
message = l(:text_issues_destroy_confirmation)
descendant_count = issues.inject(0) {|memo, i| memo += (i.right - i.left - 1)/2}
if descendant_count > 0
issues.each do |issue|
next if issue.root?
issues.each do |other_issue|
descendant_count -= 1 if issue.is_descendant_of?(other_issue)
end
end
if descendant_count > 0
message << "\n" + l(:text_issues_destroy_descendants_confirmation, :count => descendant_count)
end
end
message
end
def sidebar_queries
unless @sidebar_queries
# User can see public queries and his own queries
@@ -134,32 +106,13 @@ module IssuesHelper
# Project specific queries and global queries
visible << (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
@sidebar_queries = Query.find(:all,
:select => 'id, name, is_public',
:select => 'id, name',
:order => "name ASC",
:conditions => visible.conditions)
end
@sidebar_queries
end
def query_links(title, queries)
# links to #index on issues/show
url_params = controller_name == 'issues' ? {:controller => 'issues', :action => 'index', :project_id => @project} : params
content_tag('h3', title) +
queries.collect {|query|
link_to(h(query.name), url_params.merge(:query_id => query))
}.join('<br />')
end
def render_sidebar_queries
out = ''
queries = sidebar_queries.select {|q| !q.is_public?}
out << query_links(l(:label_my_queries), queries) if queries.any?
queries = sidebar_queries.select {|q| q.is_public?}
out << query_links(l(:label_query_plural), queries) if queries.any?
out
end
def show_detail(detail, no_html=false)
case detail.property
when 'attr'
@@ -182,10 +135,6 @@ module IssuesHelper
label = l(:field_parent_issue)
value = "##{detail.value}" unless detail.value.blank?
old_value = "##{detail.old_value}" unless detail.old_value.blank?
when detail.prop_key == 'is_private'
value = l(detail.value == "0" ? :general_text_No : :general_text_Yes) unless detail.value.blank?
old_value = l(detail.old_value == "0" ? :general_text_No : :general_text_Yes) unless detail.old_value.blank?
end
when 'cf'
custom_field = CustomField.find_by_id(detail.prop_key)
@@ -215,16 +164,7 @@ module IssuesHelper
end
end
if detail.property == 'attr' && detail.prop_key == 'description'
s = l(:text_journal_changed_no_detail, :label => label)
unless no_html
diff_link = link_to 'diff',
{:controller => 'journals', :action => 'diff', :id => detail.journal_id, :detail_id => detail.id},
:title => l(:label_view_diff)
s << " (#{ diff_link })"
end
s
elsif !detail.value.blank?
if !detail.value.blank?
case detail.property
when 'attr', 'cf'
if !detail.old_value.blank?
+10
View File
@@ -16,4 +16,14 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module MessagesHelper
def link_to_message(message)
return '' unless message
link_to h(truncate(message.subject, :length => 60)), :controller => 'messages',
:action => 'show',
:board_id => message.board_id,
:id => message.root,
:r => (message.parent_id && message.id),
:anchor => (message.parent_id ? "message-#{message.id}" : nil)
end
end
+2 -2
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
+5 -6
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -70,7 +70,6 @@ module QueriesHelper
cond = "project_id IS NULL"
cond << " OR project_id = #{@project.id}" if @project
@query = Query.find(params[:query_id], :conditions => cond)
raise ::Unauthorized unless @query.visible?
@query.project = @project
session[:query] = {:id => @query.id, :project_id => @query.project_id}
sort_clear
@@ -79,16 +78,16 @@ module QueriesHelper
# Give it a name, required to be valid
@query = Query.new(:name => "_")
@query.project = @project
if params[:fields] || params[:f]
if params[:fields]
@query.filters = {}
@query.add_filters(params[:fields] || params[:f], params[:operators] || params[:op], params[:values] || params[:v])
@query.add_filters(params[:fields], params[:operators], params[:values])
else
@query.available_filters.keys.each do |field|
@query.add_short_filter(field, params[field]) if params[field]
end
end
@query.group_by = params[:group_by]
@query.column_names = params[:c] || (params[:query] && params[:query][:column_names])
@query.column_names = params[:query] && params[:query][:column_names]
session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
else
@query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
+2 -2
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
+24 -116
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'iconv'
require 'redmine/codeset_util'
module RepositoriesHelper
def format_revision(revision)
@@ -49,8 +48,7 @@ module RepositoriesHelper
when 'A'
# Detects moved/copied files
if !change.from_path.blank?
change.action =
@changeset.changes.detect {|c| c.action == 'D' && c.path == change.from_path} ? 'R' : 'C'
change.action = @changeset.changes.detect {|c| c.action == 'D' && c.path == change.from_path} ? 'R' : 'C'
end
change
when 'D'
@@ -58,7 +56,7 @@ module RepositoriesHelper
else
change
end
end.compact
end.compact
tree = { }
changes.each do |change|
@@ -74,11 +72,13 @@ module RepositoriesHelper
end
p[:c] = change
end
render_changes_tree(tree[:s])
end
def render_changes_tree(tree)
return '' if tree.nil?
output = ''
output << '<ul>'
tree.keys.sort.each do |file|
@@ -117,24 +117,7 @@ module RepositoriesHelper
end
def to_utf8(str)
return str if str.nil?
str = to_utf8_internal(str)
if str.respond_to?(:force_encoding)
str.force_encoding('UTF-8')
end
str
end
def to_utf8_internal(str)
return str if str.nil?
if str.respond_to?(:force_encoding)
str.force_encoding('ASCII-8BIT')
end
return str if str.empty?
return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
if str.respond_to?(:force_encoding)
str.force_encoding('UTF-8')
end
@encodings ||= Setting.repositories_encodings.split(',').collect(&:strip)
@encodings.each do |encoding|
begin
@@ -143,38 +126,25 @@ module RepositoriesHelper
# do nothing here and try the next encoding
end
end
str = Redmine::CodesetUtil.replace_invalid_utf8(str)
str
end
private :to_utf8_internal
def repository_field_tags(form, repository)
method = repository.class.name.demodulize.underscore + "_field_tags"
if repository.is_a?(Repository) &&
respond_to?(method) && method != 'repository_field_tags'
send(method, form, repository)
end
send(method, form, repository) if repository.is_a?(Repository) && respond_to?(method) && method != 'repository_field_tags'
end
def scm_select_tag(repository)
scm_options = [["--- #{l(:actionview_instancetag_blank_option)} ---", '']]
Redmine::Scm::Base.all.each do |scm|
if Setting.enabled_scm.include?(scm) ||
(repository && repository.class.name.demodulize == scm)
scm_options << ["Repository::#{scm}".constantize.scm_name, scm]
end
scm_options << ["Repository::#{scm}".constantize.scm_name, scm] if Setting.enabled_scm.include?(scm) || (repository && repository.class.name.demodulize == scm)
end
select_tag('repository_scm',
options_for_select(scm_options, repository.class.name.demodulize),
:disabled => (repository && !repository.new_record?),
:onchange => remote_function(
:url => {
:controller => 'repositories',
:action => 'edit',
:id => @project
},
:method => :get,
:with => "Form.serialize(this.form)")
)
:onchange => remote_function(:url => { :controller => 'repositories', :action => 'edit', :id => @project }, :method => :get, :with => "Form.serialize(this.form)")
)
end
def with_leading_slash(path)
@@ -186,99 +156,37 @@ module RepositoriesHelper
end
def subversion_field_tags(form, repository)
content_tag('p', form.text_field(:url, :size => 60, :required => true,
:disabled => (repository && !repository.root_url.blank?)) +
content_tag('p', form.text_field(:url, :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)) +
'<br />(file:///, http://, https://, svn://, svn+[tunnelscheme]://)') +
content_tag('p', form.text_field(:login, :size => 30)) +
content_tag('p', form.password_field(
:password, :size => 30, :name => 'ignore',
:value => ((repository.new_record? || repository.password.blank?) ? '' : ('x'*15)),
:onfocus => "this.value=''; this.name='repository[password]';",
:onchange => "this.name='repository[password]';"))
content_tag('p', form.password_field(:password, :size => 30, :name => 'ignore',
:value => ((repository.new_record? || repository.password.blank?) ? '' : ('x'*15)),
:onfocus => "this.value=''; this.name='repository[password]';",
:onchange => "this.name='repository[password]';"))
end
def darcs_field_tags(form, repository)
content_tag('p', form.text_field(
:url, :label => l(:field_path_to_repository),
:size => 60, :required => true,
:disabled => (repository && !repository.new_record?))) +
content_tag('p', form.select(
:log_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_commit_logs_encoding), :required => true))
content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.new_record?)))
end
def mercurial_field_tags(form, repository)
content_tag('p', form.text_field(
:url, :label => l(:field_path_to_repository),
:size => 60, :required => true,
:disabled => (repository && !repository.root_url.blank?)
) +
'<br />' + l(:text_mercurial_repository_note)) +
content_tag('p', form.select(
:path_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_scm_path_encoding)
) +
'<br />' + l(:text_scm_path_encoding_note))
content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
end
def git_field_tags(form, repository)
content_tag('p', form.text_field(
:url, :label => l(:field_path_to_repository),
:size => 60, :required => true,
:disabled => (repository && !repository.root_url.blank?)
) +
'<br />' + l(:text_git_repository_note)) +
content_tag('p', form.select(
:path_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_scm_path_encoding)
) +
'<br />' + l(:text_scm_path_encoding_note)) +
content_tag('p', form.check_box(
:extra_report_last_commit,
:label => l(:label_git_report_last_commit)
))
content_tag('p', form.text_field(:url, :label => 'Path to .git directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
end
def cvs_field_tags(form, repository)
content_tag('p', form.text_field(
:root_url,
:label => l(:field_cvsroot),
:size => 60, :required => true,
:disabled => !repository.new_record?)) +
content_tag('p', form.text_field(
:url,
:label => l(:field_cvs_module),
:size => 30, :required => true,
:disabled => !repository.new_record?)) +
content_tag('p', form.select(
:log_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_commit_logs_encoding), :required => true)) +
content_tag('p', form.select(
:path_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_scm_path_encoding)
) +
'<br />' + l(:text_scm_path_encoding_note))
content_tag('p', form.text_field(:root_url, :label => 'CVSROOT', :size => 60, :required => true, :disabled => !repository.new_record?)) +
content_tag('p', form.text_field(:url, :label => 'Module', :size => 30, :required => true, :disabled => !repository.new_record?))
end
def bazaar_field_tags(form, repository)
content_tag('p', form.text_field(
:url, :label => l(:field_path_to_repository),
:size => 60, :required => true,
:disabled => (repository && !repository.new_record?))) +
content_tag('p', form.select(
:log_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_commit_logs_encoding), :required => true))
content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.new_record?)))
end
def filesystem_field_tags(form, repository)
content_tag('p', form.text_field(
:url, :label => l(:field_root_directory),
:size => 60, :required => true,
:disabled => (repository && !repository.root_url.blank?))) +
content_tag('p', form.select(
:path_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_scm_path_encoding)
) +
'<br />' + l(:text_scm_path_encoding_note))
content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
end
end
+3 -3
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -57,7 +57,7 @@ module SearchHelper
c = results_by_type[t]
next if c == 0
text = "#{type_label(t)} (#{c})"
links << link_to(text, :q => params[:q], :titles_only => params[:titles_only], :all_words => params[:all_words], :scope => params[:scope], t => 1)
links << link_to(text, :q => params[:q], :titles_only => params[:title_only], :all_words => params[:all_words], :scope => params[:scope], t => 1)
end
('<ul>' + links.map {|link| content_tag('li', link)}.join(' ') + '</ul>') unless links.empty?
end
+6 -13
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -33,9 +33,7 @@ module SettingsHelper
choices = [[blank_text.is_a?(Symbol) ? l(blank_text) : blank_text, '']] + choices
end
setting_label(setting, options) +
select_tag("settings[#{setting}]",
options_for_select(choices, Setting.send(setting).to_s),
options)
select_tag("settings[#{setting}]", options_for_select(choices, Setting.send(setting).to_s), options)
end
def setting_multiselect(setting, choices, options={})
@@ -46,15 +44,10 @@ module SettingsHelper
hidden_field_tag("settings[#{setting}][]", '') +
choices.collect do |choice|
text, value = (choice.is_a?(Array) ? choice : [choice, choice])
content_tag(
'label',
check_box_tag(
"settings[#{setting}][]",
value,
Setting.send(setting).include?(value)
) + text.to_s,
content_tag('label',
check_box_tag("settings[#{setting}][]", value, Setting.send(setting).include?(value)) + text.to_s,
:class => 'block'
)
)
end.join
end
@@ -71,7 +64,7 @@ module SettingsHelper
def setting_check_box(setting, options={})
setting_label(setting, options) +
hidden_field_tag("settings[#{setting}]", 0) +
check_box_tag("settings[#{setting}]", 1, Setting.send("#{setting}?"), options)
check_box_tag("settings[#{setting}]", 1, Setting.send("#{setting}?"), options)
end
def setting_label(setting, options={})
+6 -2
View File
@@ -200,12 +200,16 @@ module SortHelper
caption = column.to_s.humanize unless caption
sort_options = { :sort => @sort_criteria.add(column.to_s, order).to_param }
url_options = params.merge(sort_options)
# don't reuse params if filters are present
url_options = params.has_key?(:set_filter) ? sort_options : params.merge(sort_options)
# Add project_id to url_options
url_options = url_options.merge(:project_id => params[:project_id]) if params.has_key?(:project_id)
link_to_content_update(caption, url_options, :class => css)
link_to_remote(caption,
{:update => "content", :url => url_options, :method => :get},
{:href => url_for(url_options),
:class => css})
end
# Returns a table header <th> tag with a sort link for the named column
+5 -4
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,10 +17,11 @@
module VersionsHelper
STATUS_BY_CRITERIAS = %w(category tracker status priority author assigned_to)
STATUS_BY_CRITERIAS = %w(category tracker priority author assigned_to)
def render_issue_status_by(version, criteria)
criteria = 'category' unless STATUS_BY_CRITERIAS.include?(criteria)
criteria ||= 'category'
raise 'Unknown criteria' unless STATUS_BY_CRITERIAS.include?(criteria)
h = Hash.new {|k,v| k[v] = [0, 0]}
begin
+16 -11
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,17 +17,27 @@
module WatchersHelper
def watcher_tag(object, user, options={})
content_tag("span", watcher_link(object, user), :class => watcher_css(object))
# Valid options
# * :id - the element id
# * :replace - a string or array of element ids that will be
# replaced
def watcher_tag(object, user, options={:replace => 'watcher'})
id = options[:id]
id ||= options[:replace] if options[:replace].is_a? String
content_tag("span", watcher_link(object, user, options), :id => id)
end
def watcher_link(object, user)
# Valid options
# * :replace - a string or array of element ids that will be
# replaced
def watcher_link(object, user, options={:replace => 'watcher'})
return '' unless user && user.logged? && object.respond_to?('watched_by?')
watched = object.watched_by?(user)
url = {:controller => 'watchers',
:action => (watched ? 'unwatch' : 'watch'),
:object_type => object.class.to_s.underscore,
:object_id => object.id}
:object_id => object.id,
:replace => options[:replace]}
link_to_remote((watched ? l(:button_unwatch) : l(:button_watch)),
{:url => url},
:href => url_for(url),
@@ -35,11 +45,6 @@ module WatchersHelper
end
# Returns the css class used to identify watch links for a given +object+
def watcher_css(object)
"#{object.class.to_s.underscore}-#{object.id}-watcher"
end
# Returns a comma separated list of users watching the given object
def watchers_list(object)
remove_allowed = User.current.allowed_to?("delete_#{object.class.name.underscore}_watchers".to_sym, object.project)
+45 -11
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -18,18 +18,52 @@
module WikiHelper
def wiki_page_options_for_select(pages, selected = nil, parent = nil, level = 0)
pages = pages.group_by(&:parent) unless pages.is_a?(Hash)
s = ''
if pages.has_key?(parent)
pages[parent].each do |page|
attrs = "value='#{page.id}'"
attrs << " selected='selected'" if selected == page
indent = (level > 0) ? ('&nbsp;' * level * 2 + '&#187; ') : nil
pages.select {|p| p.parent == parent}.each do |page|
attrs = "value='#{page.id}'"
attrs << " selected='selected'" if selected == page
indent = (level > 0) ? ('&nbsp;' * level * 2 + '&#187; ') : nil
s << "<option #{attrs}>#{indent}#{h page.pretty_title}</option>\n" +
wiki_page_options_for_select(pages, selected, page, level + 1)
end
s << "<option #{attrs}>#{indent}#{h page.pretty_title}</option>\n" +
wiki_page_options_for_select(pages, selected, page, level + 1)
end
s
end
def html_diff(wdiff)
words = wdiff.words.collect{|word| h(word)}
words_add = 0
words_del = 0
dels = 0
del_off = 0
wdiff.diff.diffs.each do |diff|
add_at = nil
add_to = nil
del_at = nil
deleted = ""
diff.each do |change|
pos = change[1]
if change[0] == "+"
add_at = pos + dels unless add_at
add_to = pos + dels
words_add += 1
else
del_at = pos unless del_at
deleted << ' ' + h(change[2])
words_del += 1
end
end
if add_at
words[add_at] = '<span class="diff_in">' + words[add_at]
words[add_to] = words[add_to] + '</span>'
end
if del_at
words.insert del_at - del_off + dels + words_add, '<span class="diff_out">' + deleted + '</span>'
dels += 1
del_off += words_del
words_del = 0
end
end
simple_format_without_paragraph(words.join(' '))
end
end
+3 -3
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -43,7 +43,7 @@ class Attachment < ActiveRecord::Base
"LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id"}
cattr_accessor :storage_path
@@storage_path = Redmine::Configuration['attachments_storage_path'] || "#{RAILS_ROOT}/files"
@@storage_path = "#{RAILS_ROOT}/files"
def validate
if self.filesize > Setting.attachment_max_size.to_i.kilobytes
-10
View File
@@ -16,8 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class AuthSource < ActiveRecord::Base
include Redmine::Ciphering
has_many :users
validates_presence_of :name
@@ -34,14 +32,6 @@ class AuthSource < ActiveRecord::Base
"Abstract"
end
def account_password
read_ciphered_attribute(:account_password)
end
def account_password=(arg)
write_ciphered_attribute(:account_password, arg)
end
def allow_password_changes?
self.class.allow_password_changes?
end
+2 -2
View File
@@ -20,8 +20,8 @@ require 'iconv'
class AuthSourceLdap < AuthSource
validates_presence_of :host, :port, :attr_login
validates_length_of :name, :host, :maximum => 60, :allow_nil => true
validates_length_of :account, :account_password, :base_dn, :maximum => 255, :allow_nil => true
validates_length_of :name, :host, :account_password, :maximum => 60, :allow_nil => true
validates_length_of :account, :base_dn, :maximum => 255, :allow_nil => true
validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30, :allow_nil => true
validates_numericality_of :port, :only_integer => true
+2 -7
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -25,11 +25,6 @@ class Change < ActiveRecord::Base
changeset.repository.relative_path(path)
end
def before_validation
self.path = Redmine::CodesetUtil.replace_invalid_utf8(self.path)
self.from_path = Redmine::CodesetUtil.replace_invalid_utf8(self.from_path)
end
def init_path
self.path ||= ""
end
+39 -63
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2010 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -42,7 +42,7 @@ class Changeset < ActiveRecord::Base
validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
named_scope :visible, lambda {|*args| { :include => {:repository => :project},
:conditions => Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args) } }
:conditions => Project.allowed_to_condition(args.first || User.current, :view_changesets) } }
def revision=(r)
write_attribute :revision, (r.nil? ? nil : r.to_s)
@@ -57,6 +57,10 @@ class Changeset < ActiveRecord::Base
end
end
def comments=(comment)
write_attribute(:comments, Changeset.normalize_comments(comment))
end
def committed_on=(date)
self.commit_date = date
super
@@ -71,6 +75,10 @@ class Changeset < ActiveRecord::Base
end
end
def committer=(arg)
write_attribute(:committer, self.class.to_utf8(arg.to_s))
end
def project
repository.project
end
@@ -80,10 +88,7 @@ class Changeset < ActiveRecord::Base
end
def before_create
self.committer = self.class.to_utf8(self.committer, repository.repo_log_encoding)
self.comments = self.class.normalize_comments(
self.comments, repository.repo_log_encoding)
self.user = repository.find_committer_user(self.committer)
self.user = repository.find_committer_user(committer)
end
def after_create
@@ -150,26 +155,25 @@ class Changeset < ActiveRecord::Base
# Returns the previous changeset
def previous
@previous ||= Changeset.find(:first,
:conditions => ['id < ? AND repository_id = ?',
self.id, self.repository_id],
:order => 'id DESC')
@previous ||= Changeset.find(:first, :conditions => ['id < ? AND repository_id = ?', self.id, self.repository_id], :order => 'id DESC')
end
# Returns the next changeset
def next
@next ||= Changeset.find(:first,
:conditions => ['id > ? AND repository_id = ?',
self.id, self.repository_id],
:order => 'id ASC')
@next ||= Changeset.find(:first, :conditions => ['id > ? AND repository_id = ?', self.id, self.repository_id], :order => 'id ASC')
end
# Strips and reencodes a commit log before insertion into the database
def self.normalize_comments(str)
to_utf8(str.to_s.strip)
end
# Creates a new Change from it's common parameters
def create_change(change)
Change.create(:changeset => self,
:action => change[:action],
:path => change[:path],
:from_path => change[:from_path],
Change.create(:changeset => self,
:action => change[:action],
:path => change[:path],
:from_path => change[:from_path],
:from_revision => change[:from_revision])
end
@@ -181,9 +185,7 @@ class Changeset < ActiveRecord::Base
return nil if id.blank?
issue = Issue.find_by_id(id.to_i, :include => :project)
if issue
unless issue.project &&
(project == issue.project || project.is_ancestor_of?(issue.project) ||
project.is_descendant_of?(issue.project))
unless issue.project && (project == issue.project || project.is_ancestor_of?(issue.project) || project.is_descendant_of?(issue.project))
issue = nil
end
end
@@ -193,7 +195,7 @@ class Changeset < ActiveRecord::Base
def fix_issue(issue)
status = IssueStatus.find_by_id(Setting.commit_fix_status_id.to_i)
if status.nil?
logger.warn("No status matches commit_fix_status_id setting (#{Setting.commit_fix_status_id})") if logger
logger.warn("No status macthes commit_fix_status_id setting (#{Setting.commit_fix_status_id})") if logger
return issue
end
@@ -221,8 +223,7 @@ class Changeset < ActiveRecord::Base
:hours => hours,
:issue => issue,
:spent_on => commit_date,
:comments => l(:text_time_logged_by_changeset, :value => text_tag,
:locale => Setting.default_language)
:comments => l(:text_time_logged_by_changeset, :value => text_tag, :locale => Setting.default_language)
)
time_entry.activity = log_time_activity unless log_time_activity.nil?
@@ -245,47 +246,22 @@ class Changeset < ActiveRecord::Base
return @short_comments, @long_comments
end
public
# Strips and reencodes a commit log before insertion into the database
def self.normalize_comments(str, encoding)
Changeset.to_utf8(str.to_s.strip, encoding)
end
def self.to_utf8(str, encoding)
return str if str.nil?
str.force_encoding("ASCII-8BIT") if str.respond_to?(:force_encoding)
if str.empty?
str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
return str
end
enc = encoding.blank? ? "UTF-8" : encoding
if str.respond_to?(:force_encoding)
if enc.upcase != "UTF-8"
str.force_encoding(enc)
str = str.encode("UTF-8", :invalid => :replace,
:undef => :replace, :replace => '?')
else
str.force_encoding("UTF-8")
if ! str.valid_encoding?
str = str.encode("US-ASCII", :invalid => :replace,
:undef => :replace, :replace => '?').encode("UTF-8")
end
end
else
ic = Iconv.new('UTF-8', enc)
txtar = ""
def self.to_utf8(str)
return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
encoding = Setting.commit_logs_encoding.to_s.strip
unless encoding.blank? || encoding == 'UTF-8'
begin
txtar += ic.iconv(str)
rescue Iconv::IllegalSequence
txtar += $!.success
str = '?' + $!.failed[1,$!.failed.length]
retry
rescue
txtar += $!.success
str = Iconv.conv('UTF-8', encoding, str)
rescue Iconv::Failure
# do nothing here
end
str = txtar
end
str
# removes invalid UTF8 sequences
begin
Iconv.conv('UTF-8//IGNORE', 'UTF-8', str + ' ')[0..-3]
rescue Iconv::InvalidEncoding
# "UTF-8//IGNORE" is not supported on some OS
str
end
end
end
-24
View File
@@ -1,24 +0,0 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class CommentObserver < ActiveRecord::Observer
def after_create(comment)
if comment.commented.is_a?(News) && Setting.notified_events.include?('news_comment_added')
Mailer.deliver_news_comment_added(comment)
end
end
end
+3 -41
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -23,6 +23,7 @@ class CustomField < ActiveRecord::Base
validates_presence_of :name, :field_format
validates_uniqueness_of :name, :scope => :type
validates_length_of :name, :maximum => 30
validates_format_of :name, :with => /^[\w\s\.\'\-]*$/i
validates_inclusion_of :field_format, :in => Redmine::CustomFieldFormat.available_formats
def initialize(attributes = nil)
@@ -42,49 +43,12 @@ class CustomField < ActiveRecord::Base
errors.add(:possible_values, :invalid) unless self.possible_values.is_a? Array
end
if regexp.present?
begin
Regexp.new(regexp)
rescue
errors.add(:regexp, :invalid)
end
end
# validate default value
v = CustomValue.new(:custom_field => self.clone, :value => default_value, :customized => nil)
v.custom_field.is_required = false
errors.add(:default_value, :invalid) unless v.valid?
end
def possible_values_options(obj=nil)
case field_format
when 'user', 'version'
if obj.respond_to?(:project) && obj.project
case field_format
when 'user'
obj.project.users.sort.collect {|u| [u.to_s, u.id.to_s]}
when 'version'
obj.project.shared_versions.sort.collect {|u| [u.to_s, u.id.to_s]}
end
elsif obj.is_a?(Array)
obj.collect {|o| possible_values_options(o)}.inject {|memo, v| memo & v}
else
[]
end
else
read_attribute :possible_values
end
end
def possible_values(obj=nil)
case field_format
when 'user', 'version'
possible_values_options(obj).collect(&:last)
else
read_attribute :possible_values
end
end
# Makes possible_values accept a multiline string
def possible_values=(arg)
if arg.is_a?(Array)
@@ -108,8 +72,6 @@ class CustomField < ActiveRecord::Base
casted = value.to_i
when 'float'
casted = value.to_f
when 'user', 'version'
casted = (value.blank? ? nil : field_format.classify.constantize.find_by_id(value.to_i))
end
end
casted
+2 -5
View File
@@ -1,5 +1,5 @@
# RedMine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -29,9 +29,6 @@ class Document < ActiveRecord::Base
validates_presence_of :project, :title, :category
validates_length_of :title, :maximum => 60
named_scope :visible, lambda {|*args| { :include => :project,
:conditions => Project.allowed_to_condition(args.shift || User.current, :view_documents, *args) } }
def visible?(user=User.current)
!user.nil? && user.allowed_to?(:view_documents, project)
end
+2 -2
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
+3 -2
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -20,3 +20,4 @@ class DocumentCategoryCustomField < CustomField
:enumeration_doc_categories
end
end
+2 -2
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
+1 -1
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
-1
View File
@@ -32,7 +32,6 @@ class Enumeration < ActiveRecord::Base
named_scope :shared, :conditions => { :project_id => nil }
named_scope :active, :conditions => { :active => true }
named_scope :named, lambda {|arg| { :conditions => ["LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip]}}
def self.default
# Creates a fake default scope so Enumeration.default will check
+31 -89
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -60,7 +60,7 @@ class Issue < ActiveRecord::Base
validates_numericality_of :estimated_hours, :allow_nil => true
named_scope :visible, lambda {|*args| { :include => :project,
:conditions => Issue.visible_condition(args.shift || User.current, *args) } }
:conditions => Project.allowed_to_condition(args.first || User.current, :view_issues) } }
named_scope :open, :conditions => ["#{IssueStatus.table_name}.is_closed = ?", false], :include => :status
@@ -68,6 +68,11 @@ class Issue < ActiveRecord::Base
named_scope :with_limit, lambda { |limit| { :limit => limit} }
named_scope :on_active_project, :include => [:status, :project, :tracker],
:conditions => ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"]
named_scope :for_gantt, lambda {
{
:include => [:tracker, :status, :assigned_to, :priority, :project, :fixed_version]
}
}
named_scope :without_version, lambda {
{
@@ -86,36 +91,9 @@ class Issue < ActiveRecord::Base
after_save :reschedule_following_issues, :update_nested_set_attributes, :update_parent_attributes, :create_journal
after_destroy :update_parent_attributes
# Returns a SQL conditions string used to find all issues visible by the specified user
def self.visible_condition(user, options={})
Project.allowed_to_condition(user, :view_issues, options) do |role, user|
case role.issues_visibility
when 'all'
nil
when 'default'
"(#{table_name}.is_private = #{connection.quoted_false} OR #{table_name}.author_id = #{user.id} OR #{table_name}.assigned_to_id = #{user.id})"
when 'own'
"(#{table_name}.author_id = #{user.id} OR #{table_name}.assigned_to_id = #{user.id})"
else
'1=0'
end
end
end
# Returns true if usr or current user is allowed to view the issue
def visible?(usr=nil)
(usr || User.current).allowed_to?(:view_issues, self.project) do |role, user|
case role.issues_visibility
when 'all'
true
when 'default'
!self.is_private? || self.author == user || self.assigned_to == user
when 'own'
self.author == user || self.assigned_to == user
else
false
end
end
(usr || User.current).allowed_to?(:view_issues, self.project)
end
def after_initialize
@@ -128,7 +106,7 @@ class Issue < ActiveRecord::Base
# Overrides Redmine::Acts::Customizable::InstanceMethods#available_custom_fields
def available_custom_fields
(project && tracker) ? (project.all_issue_custom_fields & tracker.custom_fields.all) : []
(project && tracker) ? project.all_issue_custom_fields.select {|c| tracker.custom_fields.include? c } : []
end
def copy_from(arg)
@@ -175,7 +153,6 @@ class Issue < ActiveRecord::Base
issue.reset_custom_values!
end
if options[:copy]
issue.author = User.current
issue.custom_field_values = self.custom_field_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h}
issue.status = if options[:attributes] && options[:attributes][:status_id]
IssueStatus.find_by_id(options[:attributes][:status_id])
@@ -188,13 +165,7 @@ class Issue < ActiveRecord::Base
issue.attributes = options[:attributes]
end
if issue.save
if options[:copy]
if current_journal && current_journal.notes.present?
issue.init_journal(current_journal.user, current_journal.notes)
issue.current_journal.notify = false
issue.save
end
else
unless options[:copy]
# Manually update project_id on related time entries
TimeEntry.update_all("project_id = #{new_project.id}", {:issue_id => id})
@@ -228,13 +199,6 @@ class Issue < ActiveRecord::Base
result
end
def description=(arg)
if arg.is_a?(String)
arg = arg.gsub(/(\r\n|\n|\r)/, "\r\n")
end
write_attribute(:description, arg)
end
# Overrides attributes= so that tracker_id gets assigned first
def attributes_with_tracker_first=(new_attributes, *args)
return if new_attributes.nil?
@@ -275,12 +239,6 @@ class Issue < ActiveRecord::Base
'done_ratio',
:if => lambda {|issue, user| issue.new_statuses_allowed_to(user).any? }
safe_attributes 'is_private',
:if => lambda {|issue, user|
user.allowed_to?(:set_issues_private, issue.project) ||
(issue.author == user && user.allowed_to?(:set_own_issues_private, issue.project))
}
# Safely sets attributes
# Should be called from controllers instead of #attributes=
# attr_accessible is too rough because we still want things like
@@ -449,7 +407,6 @@ class Issue < ActiveRecord::Base
def assignable_users
users = project.assignable_users
users << author if author
users << assigned_to if assigned_to
users.uniq.sort
end
@@ -465,12 +422,7 @@ class Issue < ActiveRecord::Base
# Returns an array of status that user is able to apply
def new_statuses_allowed_to(user, include_default=false)
statuses = status.find_new_statuses_allowed_to(
user.roles_for_project(project),
tracker,
author == user,
assigned_to_id_changed? ? assigned_to_id_was == user.id : assigned_to_id == user.id
)
statuses = status.find_new_statuses_allowed_to(user.roles_for_project(project), tracker)
statuses << status unless statuses.empty?
statuses << IssueStatus.default if include_default
statuses = statuses.uniq.sort
@@ -503,14 +455,11 @@ class Issue < ActiveRecord::Base
(relations_from + relations_to).sort
end
def all_dependent_issues(except=[])
except << self
def all_dependent_issues
dependencies = []
relations_from.each do |relation|
if relation.issue_to && !except.include?(relation.issue_to)
dependencies << relation.issue_to
dependencies += relation.issue_to.all_dependent_issues(except)
end
dependencies << relation.issue_to
dependencies += relation.issue_to.all_dependent_issues
end
dependencies
end
@@ -575,9 +524,6 @@ class Issue < ActiveRecord::Base
s = "issue status-#{status.position} priority-#{priority.position}"
s << ' closed' if closed?
s << ' overdue' if overdue?
s << ' child' if child?
s << ' parent' unless leaf?
s << ' private' if is_private?
s << ' created-by-me' if User.current.logged? && author_id == User.current.id
s << ' assigned-to-me' if User.current.logged? && assigned_to_id == User.current.id
s
@@ -587,7 +533,7 @@ class Issue < ActiveRecord::Base
# Returns false if save fails
def save_issue_with_child_records(params, existing_time_entry=nil)
Issue.transaction do
if params[:time_entry] && (params[:time_entry][:hours].present? || params[:time_entry][:comments].present?) && User.current.allowed_to?(:log_time, project)
if params[:time_entry] && params[:time_entry][:hours].present? && User.current.allowed_to?(:log_time, project)
@time_entry = existing_time_entry || TimeEntry.new
@time_entry.project = project
@time_entry.issue = self
@@ -691,16 +637,14 @@ class Issue < ActiveRecord::Base
def self.by_subproject(project)
ActiveRecord::Base.connection.select_all("select s.id as status_id,
s.is_closed as closed,
#{Issue.table_name}.project_id as project_id,
count(#{Issue.table_name}.id) as total
i.project_id as project_id,
count(i.id) as total
from
#{Issue.table_name}, #{Project.table_name}, #{IssueStatus.table_name} s
#{Issue.table_name} i, #{IssueStatus.table_name} s
where
#{Issue.table_name}.status_id=s.id
and #{Issue.table_name}.project_id = #{Project.table_name}.id
and #{visible_condition(User.current, :project => project, :with_subprojects => true)}
and #{Issue.table_name}.project_id <> #{project.id}
group by s.id, s.is_closed, #{Issue.table_name}.project_id") if project.descendants.active.any?
i.status_id=s.id
and i.project_id IN (#{project.descendants.active.collect{|p| p.id}.join(',')})
group by s.id, s.is_closed, i.project_id") if project.descendants.active.any?
end
# End ReportsController extraction
@@ -877,14 +821,11 @@ class Issue < ActiveRecord::Base
def create_journal
if @current_journal
# attributes changes
(Issue.column_names - %w(id root_id lft rgt lock_version created_on updated_on)).each {|c|
before = @issue_before_change.send(c)
after = send(c)
next if before == after || (before.blank? && after.blank?)
(Issue.column_names - %w(id description root_id lft rgt lock_version created_on updated_on)).each {|c|
@current_journal.details << JournalDetail.new(:property => 'attr',
:prop_key => c,
:old_value => @issue_before_change.send(c),
:value => send(c))
:value => send(c)) unless send(c)==@issue_before_change.send(c)
}
# custom fields changes
custom_values.each {|c|
@@ -913,19 +854,20 @@ class Issue < ActiveRecord::Base
select_field = options.delete(:field)
joins = options.delete(:joins)
where = "#{Issue.table_name}.#{select_field}=j.id"
where = "i.#{select_field}=j.id"
ActiveRecord::Base.connection.select_all("select s.id as status_id,
s.is_closed as closed,
j.id as #{select_field},
count(#{Issue.table_name}.id) as total
count(i.id) as total
from
#{Issue.table_name}, #{Project.table_name}, #{IssueStatus.table_name} s, #{joins} j
#{Issue.table_name} i, #{IssueStatus.table_name} s, #{joins} j
where
#{Issue.table_name}.status_id=s.id
i.status_id=s.id
and #{where}
and #{Issue.table_name}.project_id=#{Project.table_name}.id
and #{visible_condition(User.current, :project => project)}
and i.project_id=#{project.id}
group by s.id, s.is_closed, j.id")
end
end
+2 -4
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -24,8 +24,6 @@ class IssueCategory < ActiveRecord::Base
validates_uniqueness_of :name, :scope => [:project_id]
validates_length_of :name, :maximum => 30
named_scope :named, lambda {|arg| { :conditions => ["LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip]}}
alias :destroy_without_reassign :destroy
# Destroy the category
+1 -6
View File
@@ -47,12 +47,7 @@ class IssueRelation < ActiveRecord::Base
if issue_from && issue_to
errors.add :issue_to_id, :invalid if issue_from_id == issue_to_id
errors.add :issue_to_id, :not_same_project unless issue_from.project_id == issue_to.project_id || Setting.cross_project_issue_relations?
#detect circular dependencies depending wether the relation should be reversed
if TYPES.has_key?(relation_type) && TYPES[relation_type][:reverse]
errors.add_to_base :circular_dependency if issue_from.all_dependent_issues.include? issue_to
else
errors.add_to_base :circular_dependency if issue_to.all_dependent_issues.include? issue_from
end
errors.add_to_base :circular_dependency if issue_to.all_dependent_issues.include? issue_from
errors.add_to_base :cant_link_an_issue_with_a_descendant if issue_from.is_descendant_of?(issue_to) || issue_from.is_ancestor_of?(issue_to)
end
end
+16 -20
View File
@@ -25,10 +25,9 @@ class IssueStatus < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name
validates_length_of :name, :maximum => 30
validates_format_of :name, :with => /^[\w\s\'\-]*$/i
validates_inclusion_of :default_done_ratio, :in => 0..100, :allow_nil => true
named_scope :named, lambda {|arg| { :conditions => ["LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip]}}
def after_save
IssueStatus.update_all("is_default=#{connection.quoted_false}", ['id <> ?', id]) if self.is_default?
end
@@ -52,15 +51,10 @@ class IssueStatus < ActiveRecord::Base
# Returns an array of all statuses the given role can switch to
# Uses association cache when called more than one time
def new_statuses_allowed_to(roles, tracker, author=false, assignee=false)
def new_statuses_allowed_to(roles, tracker)
if roles && tracker
role_ids = roles.collect(&:id)
transitions = workflows.select do |w|
role_ids.include?(w.role_id) &&
w.tracker_id == tracker.id &&
((!w.author && !w.assignee) || (author && w.author) || (assignee && w.assignee))
end
transitions.collect{|w| w.new_status}.compact.sort
new_statuses = workflows.select {|w| role_ids.include?(w.role_id) && w.tracker_id == tracker.id}.collect{|w| w.new_status}.compact.sort
else
[]
end
@@ -68,23 +62,25 @@ class IssueStatus < ActiveRecord::Base
# Same thing as above but uses a database query
# More efficient than the previous method if called just once
def find_new_statuses_allowed_to(roles, tracker, author=false, assignee=false)
if roles.present? && tracker
conditions = "(author = :false AND assignee = :false)"
conditions << " OR author = :true" if author
conditions << " OR assignee = :true" if assignee
def find_new_statuses_allowed_to(roles, tracker)
if roles && tracker
workflows.find(:all,
:include => :new_status,
:conditions => ["role_id IN (:role_ids) AND tracker_id = :tracker_id AND (#{conditions})",
{:role_ids => roles.collect(&:id), :tracker_id => tracker.id, :true => true, :false => false}
]
).collect{|w| w.new_status}.compact.sort
:include => :new_status,
:conditions => { :role_id => roles.collect(&:id),
:tracker_id => tracker.id}).collect{ |w| w.new_status }.compact.sort
else
[]
end
end
def new_status_allowed_to?(status, roles, tracker)
if status && roles && tracker
!workflows.find(:first, :conditions => {:new_status_id => status.id, :role_id => roles.collect(&:id), :tracker_id => tracker.id}).nil?
else
false
end
end
def <=>(status)
position <=> status.position
end
+3 -15
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -32,16 +32,12 @@ class Journal < ActiveRecord::Base
:url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.issue.id, :anchor => "change-#{o.id}"}}
acts_as_activity_provider :type => 'issues',
:permission => :view_issues,
:author_key => :user_id,
:find_options => {:include => [{:issue => :project}, :details, :user],
:conditions => "#{Journal.table_name}.journalized_type = 'Issue' AND" +
" (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')"}
named_scope :visible, lambda {|*args| {
:include => {:issue => :project},
:conditions => Issue.visible_condition(args.shift || User.current, *args)
}}
def save(*args)
# Do not save an empty journal
(details.empty? && notes.blank?) ? false : super
@@ -77,12 +73,4 @@ class Journal < ActiveRecord::Base
s << ' has-details' unless details.blank?
s
end
def notify?
@notify != false
end
def notify=(arg)
@notify = arg
end
end
+5 -18
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,22 +17,9 @@
class JournalDetail < ActiveRecord::Base
belongs_to :journal
before_save :normalize_values
private
def normalize_values
self.value = normalize(value)
self.old_value = normalize(old_value)
end
def normalize(v)
if v == true
"1"
elsif v == false
"0"
else
v
end
def before_save
self.value = value[0..254] if value && value.is_a?(String)
self.old_value = old_value[0..254] if old_value && old_value.is_a?(String)
end
end
+6 -8
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,12 +17,10 @@
class JournalObserver < ActiveRecord::Observer
def after_create(journal)
if journal.notify? &&
(Setting.notified_events.include?('issue_updated') ||
(Setting.notified_events.include?('issue_note_added') && journal.notes.present?) ||
(Setting.notified_events.include?('issue_status_updated') && journal.new_status.present?) ||
(Setting.notified_events.include?('issue_priority_updated') && journal.new_value_for('priority_id').present?)
)
if Setting.notified_events.include?('issue_updated') ||
(Setting.notified_events.include?('issue_note_added') && journal.notes.present?) ||
(Setting.notified_events.include?('issue_status_updated') && journal.new_status.present?) ||
(Setting.notified_events.include?('issue_priority_updated') && journal.new_value_for('priority_id').present?)
Mailer.deliver_issue_edit(journal)
end
end
+10 -10
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -198,7 +198,7 @@ class MailHandler < ActionMailer::Base
end
def add_attachments(obj)
if email.attachments && email.attachments.any?
if email.has_attachments?
email.attachments.each do |attachment|
Attachment.create(:container => obj,
:file => attachment,
@@ -240,8 +240,8 @@ class MailHandler < ActionMailer::Base
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 && user.language.present?
keys << l("field_#{attr}", :default => '', :locale => Setting.default_language) if Setting.default_language.present?
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)}
@@ -265,12 +265,12 @@ class MailHandler < ActionMailer::Base
assigned_to = nil if assigned_to && !issue.assignable_users.include?(assigned_to)
attrs = {
'tracker_id' => (k = get_keyword(:tracker)) && issue.project.trackers.named(k).first.try(:id),
'status_id' => (k = get_keyword(:status)) && IssueStatus.named(k).first.try(:id),
'priority_id' => (k = get_keyword(:priority)) && IssuePriority.named(k).first.try(:id),
'category_id' => (k = get_keyword(:category)) && issue.project.issue_categories.named(k).first.try(:id),
'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.named(k).first.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),
+3 -21
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -149,24 +149,6 @@ class Mailer < ActionMailer::Base
render_multipart('news_added', body)
end
# Builds a tmail object used to email recipients of a news' project when a news comment is added.
#
# Example:
# news_comment_added(comment) => tmail object
# Mailer.news_comment_added(comment) => sends an email to the news' project recipients
def news_comment_added(comment)
news = comment.commented
redmine_headers 'Project' => news.project.identifier
message_id comment
recipients news.recipients
cc news.watcher_recipients
subject "Re: [#{news.project.name}] #{l(:label_news)}: #{news.title}"
body :news => news,
:comment => comment,
:news_url => url_for(:controller => 'news', :action => 'show', :id => news)
render_multipart('news_comment_added', body)
end
# Builds a tmail object used to email the recipients of the specified message that was posted.
#
# Example:
@@ -314,7 +296,7 @@ class Mailer < ActionMailer::Base
if raise_errors
raise e
elsif mylogger
mylogger.error "The following error occured while sending email notification: \"#{e.message}\". Check your configuration in config/configuration.yml."
mylogger.error "The following error occured while sending email notification: \"#{e.message}\". Check your configuration in config/email.yml."
end
ensure
self.class.raise_delivery_errors = raise_errors
+3 -6
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -24,7 +24,7 @@ class Message < ActiveRecord::Base
acts_as_searchable :columns => ['subject', 'content'],
:include => {:board => :project},
:project_key => "#{Board.table_name}.project_id",
:project_key => 'project_id',
:date_column => "#{table_name}.created_on"
acts_as_event :title => Proc.new {|o| "#{o.board.name}: #{o.subject}"},
:description => :content,
@@ -42,9 +42,6 @@ class Message < ActiveRecord::Base
after_create :add_author_as_watcher
named_scope :visible, lambda {|*args| { :include => {:board => :project},
:conditions => Project.allowed_to_condition(args.shift || User.current, :view_messages, *args) } }
def visible?(user=User.current)
!user.nil? && user.allowed_to?(:view_messages, project)
end
+2 -2
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
+2 -11
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -28,13 +28,10 @@ class News < ActiveRecord::Base
acts_as_event :url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.id}}
acts_as_activity_provider :find_options => {:include => [:project, :author]},
:author_key => :author_id
acts_as_watchable
after_create :add_author_as_watcher
named_scope :visible, lambda {|*args| {
:include => :project,
:conditions => Project.allowed_to_condition(args.shift || User.current, :view_news, *args)
:conditions => Project.allowed_to_condition(args.first || User.current, :view_news)
}}
def visible?(user=User.current)
@@ -45,10 +42,4 @@ class News < ActiveRecord::Base
def self.latest(user = User.current, count = 5)
find(:all, :limit => count, :conditions => Project.allowed_to_condition(user, :view_news), :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
end
private
def add_author_as_watcher
Watcher.create(:watchable => self, :user => author)
end
end
+30 -81
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -84,7 +84,7 @@ class Project < ActiveRecord::Base
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 {|*args| {:conditions => Project.visible_condition(args.shift || User.current, *args) }}
named_scope :visible, lambda { { :conditions => Project.visible_by(User.current) } }
def initialize(attributes = nil)
super
@@ -115,36 +115,27 @@ class Project < ActiveRecord::Base
# returns latest created projects
# non public projects will be returned only if user is a member of those
def self.latest(user=nil, count=5)
visible(user).find(:all, :limit => count, :order => "created_on DESC")
find(:all, :limit => count, :conditions => visible_by(user), :order => "created_on DESC")
end
# Returns true if the project is visible to +user+ or to the current user.
def visible?(user=User.current)
user.allowed_to?(:view_project, self)
end
def self.visible_by(user=nil)
ActiveSupport::Deprecation.warn "Project.visible_by is deprecated and will be removed in Redmine 1.3.0. Use Project.visible_condition instead."
visible_condition(user || User.current)
end
# Returns a SQL conditions string used to find all projects visible by the specified user.
# Returns a SQL :conditions string used to find all active projects for the specified user.
#
# Examples:
# Project.visible_condition(admin) => "projects.status = 1"
# Project.visible_condition(normal_user) => "((projects.status = 1) AND (projects.is_public = 1 OR projects.id IN (1,3,4)))"
# Project.visible_condition(anonymous) => "((projects.status = 1) AND (projects.is_public = 1))"
def self.visible_condition(user, options={})
allowed_to_condition(user, :view_project, options)
# Projects.visible_by(admin) => "projects.status = 1"
# Projects.visible_by(normal_user) => "projects.status = 1 AND projects.is_public = 1"
def self.visible_by(user=nil)
user ||= User.current
if user && user.admin?
return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"
elsif user && user.memberships.any?
return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND (#{Project.table_name}.is_public = #{connection.quoted_true} or #{Project.table_name}.id IN (#{user.memberships.collect{|m| m.project_id}.join(',')}))"
else
return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND #{Project.table_name}.is_public = #{connection.quoted_true}"
end
end
# Returns a SQL conditions string used to find all projects for which +user+ has the given +permission+
#
# Valid options:
# * :project => limit the condition to project
# * :with_subprojects => limit the condition to project and its subprojects
# * :member => limit the condition to the user projects
def self.allowed_to_condition(user, permission, options={})
statements = []
base_statement = "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"
if perm = Redmine::AccessControl.permission(permission)
unless perm.project_module.nil?
@@ -157,37 +148,24 @@ class Project < ActiveRecord::Base
project_statement << " OR (#{Project.table_name}.lft > #{options[:project].lft} AND #{Project.table_name}.rgt < #{options[:project].rgt})" if options[:with_subprojects]
base_statement = "(#{project_statement}) AND (#{base_statement})"
end
if user.admin?
base_statement
# no restriction
else
statement_by_role = {}
unless options[:member]
role = user.logged? ? Role.non_member : Role.anonymous
if role.allowed_to?(permission)
statement_by_role[role] = "#{Project.table_name}.is_public = #{connection.quoted_true}"
end
end
statements << "1=0"
if user.logged?
user.projects_by_role.each do |role, projects|
if role.allowed_to?(permission)
statement_by_role[role] = "#{Project.table_name}.id IN (#{projects.collect(&:id).join(',')})"
end
if Role.non_member.allowed_to?(permission) && !options[:member]
statements << "#{Project.table_name}.is_public = #{connection.quoted_true}"
end
end
if statement_by_role.empty?
"1=0"
allowed_project_ids = user.memberships.select {|m| m.roles.detect {|role| role.allowed_to?(permission)}}.collect {|m| m.project_id}
statements << "#{Project.table_name}.id IN (#{allowed_project_ids.join(',')})" if allowed_project_ids.any?
else
if block_given?
statement_by_role.each do |role, statement|
if s = yield(role, user)
statement_by_role[role] = "(#{statement} AND (#{s}))"
end
end
if Role.anonymous.allowed_to?(permission) && !options[:member]
# anonymous user allowed on public project
statements << "#{Project.table_name}.is_public = #{connection.quoted_true}"
end
"((#{base_statement}) AND (#{statement_by_role.values.join(' OR ')}))"
end
end
statements.empty? ? base_statement : "((#{base_statement}) AND (#{statements.join(' OR ')}))"
end
# Returns the Systemwide and project specific activities
@@ -369,7 +347,7 @@ class Project < ActiveRecord::Base
# Returns an array of the trackers used by the project and its active sub projects
def rolled_up_trackers
@rolled_up_trackers ||=
Tracker.find(:all, :joins => :projects,
Tracker.find(:all, :include => :projects,
:select => "DISTINCT #{Tracker.table_name}.*",
:conditions => ["#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status = #{STATUS_ACTIVE}", lft, rgt],
:order => "#{Tracker.table_name}.position")
@@ -395,17 +373,15 @@ class Project < ActiveRecord::Base
# Returns a scope of the Versions used by the project
def shared_versions
@shared_versions ||= begin
r = root? ? self : root
@shared_versions ||=
Version.scoped(:include => :project,
:conditions => "#{Project.table_name}.id = #{id}" +
" OR (#{Project.table_name}.status = #{Project::STATUS_ACTIVE} AND (" +
" #{Version.table_name}.sharing = 'system'" +
" OR (#{Project.table_name}.lft >= #{r.lft} AND #{Project.table_name}.rgt <= #{r.rgt} AND #{Version.table_name}.sharing = 'tree')" +
" OR (#{Project.table_name}.lft >= #{root.lft} AND #{Project.table_name}.rgt <= #{root.rgt} AND #{Version.table_name}.sharing = 'tree')" +
" OR (#{Project.table_name}.lft < #{lft} AND #{Project.table_name}.rgt > #{rgt} AND #{Version.table_name}.sharing IN ('hierarchy', 'descendants'))" +
" OR (#{Project.table_name}.lft > #{lft} AND #{Project.table_name}.rgt < #{rgt} AND #{Version.table_name}.sharing = 'hierarchy')" +
"))")
end
end
# Returns a hash of project users grouped by role
@@ -448,12 +424,6 @@ class Project < ActiveRecord::Base
@all_issue_custom_fields ||= (IssueCustomField.for_all + issue_custom_fields).uniq.sort
end
# Returns an array of all custom fields enabled for project time entries
# (explictly associated custom fields and custom fields enabled for all projects)
def all_time_entry_custom_fields
@all_time_entry_custom_fields ||= (TimeEntryCustomField.for_all + time_entry_custom_fields).uniq.sort
end
def project
self
end
@@ -550,26 +520,6 @@ class Project < ActiveRecord::Base
enabled_modules.collect(&:name)
end
# Enable a specific module
#
# Examples:
# project.enable_module!(:issue_tracking)
# project.enable_module!("issue_tracking")
def enable_module!(name)
enabled_modules << EnabledModule.new(:name => name.to_s) unless module_enabled?(name)
end
# Disable a module if it exists
#
# Examples:
# project.disable_module!(:issue_tracking)
# project.disable_module!("issue_tracking")
# project.disable_module!(project.enabled_modules.first)
def disable_module!(target)
target = enabled_modules.detect{|mod| target.to_s == mod.name} unless enabled_modules.include?(target)
target.destroy unless target.blank?
end
safe_attributes 'name',
'description',
'homepage',
@@ -714,7 +664,6 @@ class Project < ActiveRecord::Base
end
# Copies issues from +project+
# Note: issues assigned to a closed version won't be copied due to validation rules
def copy_issues(project)
# Stores the source issue id as a key and the copied issues as the
# value. Used to map the two togeather for issue relations.
+30 -54
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -42,10 +42,6 @@ class QueryColumn
def value(issue)
issue.send name
end
def css_classes
name
end
end
class QueryCustomFieldColumn < QueryColumn
@@ -72,10 +68,6 @@ class QueryCustomFieldColumn < QueryColumn
cv = issue.custom_values.detect {|v| v.custom_field_id == @cf.id}
cv && @cf.cast_value(cv.value)
end
def css_classes
@css_classes ||= "#{name} #{@cf.field_format}"
end
end
class Query < ActiveRecord::Base
@@ -166,11 +158,6 @@ class Query < ActiveRecord::Base
end if filters
end
# Returns true if the query is visible to +user+ or the current user.
def visible?(user=User.current)
self.is_public? || self.user_id == user.id
end
def editable_by?(user)
return false unless user
# Admin can edit them all and regular users can edit their private queries
@@ -229,19 +216,14 @@ class Query < ActiveRecord::Base
if project
# project specific filters
categories = @project.issue_categories.all
unless categories.empty?
@available_filters["category_id"] = { :type => :list_optional, :order => 6, :values => categories.collect{|s| [s.name, s.id.to_s] } }
unless @project.issue_categories.empty?
@available_filters["category_id"] = { :type => :list_optional, :order => 6, :values => @project.issue_categories.collect{|s| [s.name, s.id.to_s] } }
end
versions = @project.shared_versions.all
unless versions.empty?
@available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] } }
unless @project.shared_versions.empty?
@available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => @project.shared_versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] } }
end
unless @project.leaf?
subprojects = @project.descendants.visible.all
unless subprojects.empty?
@available_filters["subproject_id"] = { :type => :list_subprojects, :order => 13, :values => subprojects.collect{|s| [s.name, s.id.to_s] } }
end
unless @project.descendants.active.empty?
@available_filters["subproject_id"] = { :type => :list_subprojects, :order => 13, :values => @project.descendants.visible.collect{|s| [s.name, s.id.to_s] } }
end
add_custom_fields_filters(@project.all_issue_custom_fields)
else
@@ -333,17 +315,14 @@ class Query < ActiveRecord::Base
end
def columns
# preserve the column_names order
(has_default_columns? ? default_columns_names : column_names).collect do |name|
available_columns.find { |col| col.name == name }
end.compact
end
def default_columns_names
@default_columns_names ||= begin
default_columns = Setting.issue_list_default_columns.map(&:to_sym)
project.present? ? default_columns : [:project] | default_columns
if has_default_columns?
available_columns.select do |c|
# Adds the project column by default for cross-project lists
Setting.issue_list_default_columns.include?(c.name.to_s) || (c.name == :project && project.nil?)
end
else
# preserve the column_names order
column_names.collect {|name| available_columns.find {|col| col.name == name}}.compact
end
end
@@ -352,7 +331,7 @@ class Query < ActiveRecord::Base
names = names.select {|n| n.is_a?(Symbol) || !n.blank? }
names = names.collect {|n| n.is_a?(Symbol) ? n : n.to_sym }
# Set column_names to nil if default columns
if names == default_columns_names
if names.map(&:to_s) == Setting.issue_list_default_columns
names = nil
end
end
@@ -432,7 +411,8 @@ class Query < ActiveRecord::Base
elsif project
project_clauses << "#{Project.table_name}.id = %d" % project.id
end
project_clauses.any? ? project_clauses.join(' AND ') : nil
project_clauses << Project.allowed_to_condition(User.current, :view_issues)
project_clauses.join(' AND ')
end
def statement
@@ -513,15 +493,12 @@ class Query < ActiveRecord::Base
end if filters and valid?
filters_clauses << project_statement
filters_clauses.reject!(&:blank?)
filters_clauses.any? ? filters_clauses.join(' AND ') : nil
(filters_clauses << project_statement).join(' AND ')
end
# Returns the issue count
def issue_count
Issue.visible.count(:include => [:status, :project], :conditions => statement)
Issue.count(:include => [:status, :project], :conditions => statement)
rescue ::ActiveRecord::StatementInvalid => e
raise StatementInvalid.new(e.message)
end
@@ -532,7 +509,7 @@ class Query < ActiveRecord::Base
if grouped?
begin
# Rails will raise an (unexpected) RecordNotFound if there's only a nil group value
r = Issue.visible.count(:group => group_by_statement, :include => [:status, :project], :conditions => statement)
r = Issue.count(:group => group_by_statement, :include => [:status, :project], :conditions => statement)
rescue ActiveRecord::RecordNotFound
r = {nil => issue_count}
end
@@ -552,7 +529,7 @@ class Query < ActiveRecord::Base
order_option = [group_by_sort_order, options[:order]].reject {|s| s.blank?}.join(',')
order_option = nil if order_option.blank?
Issue.visible.find :all, :include => ([:status, :project] + (options[:include] || [])).uniq,
Issue.find :all, :include => ([:status, :project] + (options[:include] || [])).uniq,
:conditions => Query.merge_conditions(statement, options[:conditions]),
:order => order_option,
:limit => options[:limit],
@@ -564,7 +541,7 @@ class Query < ActiveRecord::Base
# Returns the journals
# Valid options are :order, :offset, :limit
def journals(options={})
Journal.visible.find :all, :include => [:details, :user, {:issue => [:project, :author, :tracker, :status]}],
Journal.find :all, :include => [:details, :user, {:issue => [:project, :author, :tracker, :status]}],
:conditions => statement,
:order => options[:order],
:limit => options[:limit],
@@ -576,7 +553,7 @@ class Query < ActiveRecord::Base
# Returns the versions
# Valid options are :conditions
def versions(options={})
Version.visible.find :all, :include => :project,
Version.find :all, :include => :project,
:conditions => Query.merge_conditions(project_statement, options[:conditions])
rescue ::ActiveRecord::StatementInvalid => e
raise StatementInvalid.new(e.message)
@@ -631,10 +608,12 @@ class Query < ActiveRecord::Base
when "t"
sql = date_range_clause(db_table, db_field, 0, 0)
when "w"
first_day_of_week = l(:general_first_day_of_week).to_i
day_of_week = Date.today.cwday
days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week)
sql = date_range_clause(db_table, db_field, - days_ago, - days_ago + 6)
from = l(:general_first_day_of_week) == '7' ?
# week starts on sunday
((Date.today.cwday == 7) ? Time.now.at_beginning_of_day : Time.now.at_beginning_of_week - 1.day) :
# week starts on monday (Rails default)
Time.now.at_beginning_of_week
sql = "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date(from), connection.quoted_date(from + 7.days)]
when "~"
sql = "LOWER(#{db_table}.#{db_field}) LIKE '%#{connection.quote_string(value.first.to_s.downcase)}%'"
when "!~"
@@ -657,9 +636,6 @@ class Query < ActiveRecord::Base
options = { :type => :date, :order => 20 }
when "bool"
options = { :type => :list, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]], :order => 20 }
when "user", "version"
next unless project
options = { :type => :list_optional, :values => field.possible_values_options(project), :order => 20}
else
options = { :type => :string, :order => 20 }
end
+16 -120
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,30 +16,17 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class Repository < ActiveRecord::Base
include Redmine::Ciphering
belongs_to :project
has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
has_many :changes, :through => :changesets
serialize :extra_info
# Raw SQL to delete changesets and changes in the database
# has_many :changesets, :dependent => :destroy is too slow for big repositories
before_destroy :clear_changesets
validates_length_of :password, :maximum => 255, :allow_nil => true
# Checks if the SCM is enabled when creating a repository
validate_on_create { |r| r.errors.add(:type, :invalid) unless Setting.enabled_scm.include?(r.class.name.demodulize) }
def self.human_attribute_name(attribute_key_name)
attr_name = attribute_key_name
if attr_name == "log_encoding"
attr_name = "commit_logs_encoding"
end
super(attr_name)
end
# Removes leading and trailing whitespace
def url=(arg)
write_attribute(:url, arg ? arg.to_s.strip : nil)
@@ -50,21 +37,8 @@ class Repository < ActiveRecord::Base
write_attribute(:root_url, arg ? arg.to_s.strip : nil)
end
def password
read_ciphered_attribute(:password)
end
def password=(arg)
write_ciphered_attribute(:password, arg)
end
def scm_adapter
self.class.scm_adapter_class
end
def scm
@scm ||= self.scm_adapter.new(url, root_url,
login, password, path_encoding)
@scm ||= self.scm_adapter.new url, root_url, login, password
update_attribute(:root_url, @scm.root_url) if root_url.blank?
@scm
end
@@ -73,17 +47,6 @@ class Repository < ActiveRecord::Base
self.class.scm_name
end
def merge_extra_info(arg)
h = extra_info || {}
return h if arg.nil?
h.merge!(arg)
write_attribute(:extra_info, h)
end
def report_last_commit
true
end
def supports_cat?
scm.supports_cat?
end
@@ -92,14 +55,6 @@ class Repository < ActiveRecord::Base
scm.supports_annotate?
end
def supports_all_revisions?
true
end
def supports_directory_revisions?
false
end
def entry(path=nil, identifier=nil)
scm.entry(path, identifier)
end
@@ -117,7 +72,7 @@ class Repository < ActiveRecord::Base
end
def default_branch
nil
scm.default_branch
end
def properties(path, identifier=nil)
@@ -132,13 +87,6 @@ class Repository < ActiveRecord::Base
scm.diff(path, rev, rev_to)
end
def diff_format_revisions(cs, cs_to, sep=':')
text = ""
text << cs_to.format_identifier + sep if cs_to
text << cs.format_identifier if cs
text
end
# Returns a path relative to the url of the repository
def relative_path(path)
path
@@ -147,8 +95,7 @@ class Repository < ActiveRecord::Base
# 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 + '%']))
changesets.find(:first, :conditions => (name.match(/^\d*$/) ? ["revision = ?", name.to_s] : ["revision LIKE ?", name + '%']))
end
def latest_changeset
@@ -159,19 +106,14 @@ class Repository < ActiveRecord::Base
# Default behaviour is to search in cached changesets
def latest_changesets(path, rev, limit=10)
if path.blank?
changesets.find(
:all,
:include => :user,
:order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
:limit => limit)
changesets.find(:all, :include => :user,
:order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
:limit => limit)
else
changes.find(
:all,
:include => {:changeset => :user},
:conditions => ["path = ?", path.with_leading_slash],
:order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
:limit => limit
).collect(&:changeset)
changes.find(:all, :include => {:changeset => :user},
:conditions => ["path = ?", path.with_leading_slash],
:order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
:limit => limit).collect(&:changeset)
end
end
@@ -181,8 +123,7 @@ class Repository < ActiveRecord::Base
# Returns an array of committers usernames and associated user_id
def committers
@committers ||= Changeset.connection.select_rows(
"SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
@committers ||= Changeset.connection.select_rows("SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
end
# Maps committers username to a user ids
@@ -192,12 +133,10 @@ class Repository < ActiveRecord::Base
new_user_id = h[committer]
if new_user_id && (new_user_id.to_i != user_id.to_i)
new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil)
Changeset.update_all(
"user_id = #{ new_user_id.nil? ? 'NULL' : new_user_id }",
["repository_id = ? AND committer = ?", id, committer])
Changeset.update_all("user_id = #{ new_user_id.nil? ? 'NULL' : new_user_id }", ["repository_id = ? AND committer = ?", id, committer])
end
end
@committers = nil
@committers = nil
@found_committer_users = nil
true
else
@@ -228,22 +167,13 @@ class Repository < ActiveRecord::Base
end
end
def repo_log_encoding
encoding = log_encoding.to_s.strip
encoding.blank? ? 'UTF-8' : encoding
end
# Fetches new changesets for all repositories of active projects
# Can be called periodically by an external script
# eg. ruby script/runner "Repository.fetch_changesets"
def self.fetch_changesets
Project.active.has_module(:repository).find(:all, :include => :repository).each do |project|
if project.repository
begin
project.repository.fetch_changesets
rescue Redmine::Scm::Adapters::CommandFailed => e
logger.error "scm: error during fetching changesets: #{e.message}"
end
project.repository.fetch_changesets
end
end
end
@@ -268,40 +198,6 @@ class Repository < ActiveRecord::Base
nil
end
def self.scm_adapter_class
nil
end
def self.scm_command
ret = ""
begin
ret = self.scm_adapter_class.client_command if self.scm_adapter_class
rescue Exception => e
logger.error "scm: error during get command: #{e.message}"
end
ret
end
def self.scm_version_string
ret = ""
begin
ret = self.scm_adapter_class.client_version_string if self.scm_adapter_class
rescue Exception => e
logger.error "scm: error during get version string: #{e.message}"
end
ret
end
def self.scm_available
ret = false
begin
ret = self.scm_adapter_class.client_available if self.scm_adapter_class
rescue Exception => e
logger.error "scm: error during get scm available: #{e.message}"
end
ret
end
private
def before_save
+18 -31
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -19,17 +19,9 @@ require 'redmine/scm/adapters/bazaar_adapter'
class Repository::Bazaar < Repository
attr_protected :root_url
validates_presence_of :url, :log_encoding
validates_presence_of :url
def self.human_attribute_name(attribute_key_name)
attr_name = attribute_key_name
if attr_name == "url"
attr_name = "path_to_repository"
end
super(attr_name)
end
def self.scm_adapter_class
def scm_adapter
Redmine::Scm::Adapters::BazaarAdapter
end
@@ -47,19 +39,14 @@ class Repository::Bazaar < Repository
full_path = File.join(root_url, e.path)
e.size = File.stat(full_path).size if File.file?(full_path)
end
c = Change.find(
:first,
:include => :changeset,
:conditions => [
"#{Change.table_name}.revision = ? and #{Changeset.table_name}.repository_id = ?",
e.lastrev.revision,
id
],
:order => "#{Changeset.table_name}.revision DESC")
c = Change.find(:first,
:include => :changeset,
:conditions => ["#{Change.table_name}.revision = ? and #{Changeset.table_name}.repository_id = ?", e.lastrev.revision, id],
:order => "#{Changeset.table_name}.revision DESC")
if c
e.lastrev.identifier = c.changeset.revision
e.lastrev.name = c.changeset.revision
e.lastrev.author = c.changeset.committer
e.lastrev.name = c.changeset.revision
e.lastrev.author = c.changeset.committer
end
end
end
@@ -81,18 +68,18 @@ class Repository::Bazaar < Repository
revisions = scm.revisions('', identifier_to, identifier_from, :with_paths => true)
transaction do
revisions.reverse_each do |revision|
changeset = Changeset.create(:repository => self,
:revision => revision.identifier,
:committer => revision.author,
changeset = Changeset.create(:repository => self,
:revision => revision.identifier,
:committer => revision.author,
:committed_on => revision.time,
:scmid => revision.scmid,
:comments => revision.message)
:scmid => revision.scmid,
:comments => revision.message)
revision.paths.each do |change|
Change.create(:changeset => changeset,
:action => change[:action],
:path => change[:path],
:revision => change[:revision])
:action => change[:action],
:path => change[:path],
:revision => change[:revision])
end
end
end unless revisions.nil?
+58 -102
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -19,19 +19,9 @@ require 'redmine/scm/adapters/cvs_adapter'
require 'digest/sha1'
class Repository::Cvs < Repository
validates_presence_of :url, :root_url, :log_encoding
validates_presence_of :url, :root_url
def self.human_attribute_name(attribute_key_name)
attr_name = attribute_key_name
if attr_name == "root_url"
attr_name = "cvsroot"
elsif attr_name == "url"
attr_name = "cvs_module"
end
super(attr_name)
end
def self.scm_adapter_class
def scm_adapter
Redmine::Scm::Adapters::CvsAdapter
end
@@ -45,23 +35,17 @@ class Repository::Cvs < Repository
end
def entries(path=nil, identifier=nil)
rev = nil
if ! identifier.nil?
rev = changesets.find_by_revision(identifier)
return nil if rev.nil?
end
rev = identifier.nil? ? nil : changesets.find_by_revision(identifier)
entries = scm.entries(path, rev.nil? ? nil : rev.committed_on)
if entries
entries.each() do |entry|
if ( ! entry.lastrev.nil? ) && ( ! entry.lastrev.revision.nil? )
change=changes.find_by_revision_and_path(
entry.lastrev.revision,
scm.with_leading_slash(entry.path) )
unless entry.lastrev.nil? || entry.lastrev.identifier
change=changes.find_by_revision_and_path( entry.lastrev.revision, scm.with_leading_slash(entry.path) )
if change
entry.lastrev.identifier = change.changeset.revision
entry.lastrev.revision = change.changeset.revision
entry.lastrev.author = change.changeset.committer
# entry.lastrev.branch = change.branch
entry.lastrev.identifier=change.changeset.revision
entry.lastrev.author=change.changeset.committer
entry.lastrev.revision=change.revision
entry.lastrev.branch=change.branch
end
end
end
@@ -70,44 +54,32 @@ class Repository::Cvs < Repository
end
def cat(path, identifier=nil)
rev = nil
if ! identifier.nil?
rev = changesets.find_by_revision(identifier)
return nil if rev.nil?
end
rev = identifier.nil? ? nil : changesets.find_by_revision(identifier)
scm.cat(path, rev.nil? ? nil : rev.committed_on)
end
def annotate(path, identifier=nil)
rev = nil
if ! identifier.nil?
rev = changesets.find_by_revision(identifier)
return nil if rev.nil?
end
scm.annotate(path, rev.nil? ? nil : rev.committed_on)
end
def diff(path, rev, rev_to)
# convert rev to revision. CVS can't handle changesets here
#convert rev to revision. CVS can't handle changesets here
diff=[]
changeset_from = changesets.find_by_revision(rev)
changeset_from=changesets.find_by_revision(rev)
if rev_to.to_i > 0
changeset_to = changesets.find_by_revision(rev_to)
changeset_to=changesets.find_by_revision(rev_to)
end
changeset_from.changes.each() do |change_from|
revision_from = nil
revision_to = nil
if path.nil? || (change_from.path.starts_with? scm.with_leading_slash(path))
revision_from = change_from.revision
end
revision_from=nil
revision_to=nil
revision_from=change_from.revision if path.nil? || (change_from.path.starts_with? scm.with_leading_slash(path))
if revision_from
if changeset_to
changeset_to.changes.each() do |change_to|
revision_to = change_to.revision if change_to.path == change_from.path
revision_to=change_to.revision if change_to.path==change_from.path
end
end
unless revision_to
revision_to = scm.get_previous_revision(revision_from)
revision_to=scm.get_previous_revision(revision_from)
end
file_diff = scm.diff(change_from.path, revision_from, revision_to)
diff = diff + file_diff unless file_diff.nil?
@@ -118,78 +90,64 @@ class Repository::Cvs < Repository
def fetch_changesets
# some nifty bits to introduce a commit-id with cvs
# natively cvs doesn't provide any kind of changesets,
# there is only a revision per file.
# natively cvs doesn't provide any kind of changesets, there is only a revision per file.
# we now take a guess using the author, the commitlog and the commit-date.
# last one is the next step to take. the commit-date is not equal for all
# commits in one changeset. cvs update the commit-date when the *,v file was touched. so
# we use a small delta here, to merge all changes belonging to _one_ changeset
time_delta = 10.seconds
time_delta=10.seconds
fetch_since = latest_changeset ? latest_changeset.committed_on : nil
transaction do
tmp_rev_num = 1
scm.revisions('', fetch_since, nil, :log_encoding => repo_log_encoding) do |revision|
scm.revisions('', fetch_since, nil, :with_paths => true) do |revision|
# only add the change to the database, if it doen't exists. the cvs log
# is not exclusive at all.
tmp_time = revision.time.clone
unless changes.find_by_path_and_revision(
scm.with_leading_slash(revision.paths[0][:path]),
revision.paths[0][:revision]
)
cmt = Changeset.normalize_comments(revision.message, repo_log_encoding)
author_utf8 = Changeset.to_utf8(revision.author, repo_log_encoding)
cs = changesets.find(
:first,
:conditions => {
:committed_on => tmp_time - time_delta .. tmp_time + time_delta,
:committer => author_utf8,
:comments => cmt
}
)
unless changes.find_by_path_and_revision(scm.with_leading_slash(revision.paths[0][:path]), revision.paths[0][:revision])
revision
cs = changesets.find(:first, :conditions=>{
:committed_on=>revision.time-time_delta..revision.time+time_delta,
:committer=>revision.author,
:comments=>Changeset.normalize_comments(revision.message)
})
# create a new changeset....
unless cs
# we use a temporaray revision number here (just for inserting)
# later on, we calculate a continous positive number
tmp_time2 = tmp_time.clone.gmtime
branch = revision.paths[0][:branch]
scmid = branch + "-" + tmp_time2.strftime("%Y%m%d-%H%M%S")
cs = Changeset.create(:repository => self,
:revision => "tmp#{tmp_rev_num}",
:scmid => scmid,
:committer => revision.author,
:committed_on => tmp_time,
:comments => revision.message)
latest = changesets.find(:first, :order => 'id DESC')
cs = Changeset.create(:repository => self,
:revision => "_#{tmp_rev_num}",
:committer => revision.author,
:committed_on => revision.time,
:comments => revision.message)
tmp_rev_num += 1
end
# convert CVS-File-States to internal Action-abbrevations
# default action is (M)odified
action = "M"
if revision.paths[0][:action] == "Exp" && revision.paths[0][:revision] == "1.1"
action = "A" # add-action always at first revision (= 1.1)
elsif revision.paths[0][:action] == "dead"
action = "D" # dead-state is similar to Delete
#convert CVS-File-States to internal Action-abbrevations
#default action is (M)odified
action="M"
if revision.paths[0][:action]=="Exp" && revision.paths[0][:revision]=="1.1"
action="A" #add-action always at first revision (= 1.1)
elsif revision.paths[0][:action]=="dead"
action="D" #dead-state is similar to Delete
end
Change.create(
:changeset => cs,
:action => action,
:path => scm.with_leading_slash(revision.paths[0][:path]),
:revision => revision.paths[0][:revision],
:branch => revision.paths[0][:branch]
)
Change.create(:changeset => cs,
:action => action,
:path => scm.with_leading_slash(revision.paths[0][:path]),
:revision => revision.paths[0][:revision],
:branch => revision.paths[0][:branch]
)
end
end
# Renumber new changesets in chronological order
changesets.find(
:all,
:order => 'committed_on ASC, id ASC',
:conditions => "revision LIKE 'tmp%'"
).each do |changeset|
changesets.find(:all, :order => 'committed_on ASC, id ASC', :conditions => "revision LIKE '_%'").each do |changeset|
changeset.update_attribute :revision, next_revision_number
end
end # transaction
@current_revision_number = nil
end
private
@@ -197,9 +155,7 @@ class Repository::Cvs < Repository
# Returns the next revision number to assign to a CVS changeset
def next_revision_number
# Need to retrieve existing revision numbers to sort them as integers
sql = "SELECT revision FROM #{Changeset.table_name} "
sql << "WHERE repository_id = #{id} AND revision NOT LIKE 'tmp%'"
@current_revision_number ||= (connection.select_values(sql).collect(&:to_i).max || 0)
@current_revision_number ||= (connection.select_values("SELECT revision FROM #{Changeset.table_name} WHERE repository_id = #{id} AND revision NOT LIKE '_%'").collect(&:to_i).max || 0)
@current_revision_number += 1
end
end
+16 -33
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -18,17 +18,9 @@
require 'redmine/scm/adapters/darcs_adapter'
class Repository::Darcs < Repository
validates_presence_of :url, :log_encoding
validates_presence_of :url
def self.human_attribute_name(attribute_key_name)
attr_name = attribute_key_name
if attr_name == "url"
attr_name = "path_to_repository"
end
super(attr_name)
end
def self.scm_adapter_class
def scm_adapter
Redmine::Scm::Adapters::DarcsAdapter
end
@@ -36,33 +28,23 @@ class Repository::Darcs < Repository
'Darcs'
end
def supports_directory_revisions?
true
end
def entry(path=nil, identifier=nil)
patch = identifier.nil? ? nil : changesets.find_by_revision(identifier)
scm.entry(path, patch.nil? ? nil : patch.scmid)
end
def entries(path=nil, identifier=nil)
patch = nil
if ! identifier.nil?
patch = changesets.find_by_revision(identifier)
return nil if patch.nil?
end
patch = identifier.nil? ? nil : changesets.find_by_revision(identifier)
entries = scm.entries(path, patch.nil? ? nil : patch.scmid)
if entries
entries.each do |entry|
# Search the DB for the entry's last change
if entry.lastrev && !entry.lastrev.scmid.blank?
changeset = changesets.find_by_scmid(entry.lastrev.scmid)
end
changeset = changesets.find_by_scmid(entry.lastrev.scmid) if entry.lastrev && !entry.lastrev.scmid.blank?
if changeset
entry.lastrev.identifier = changeset.revision
entry.lastrev.name = changeset.revision
entry.lastrev.time = changeset.committed_on
entry.lastrev.author = changeset.committer
entry.lastrev.name = changeset.revision
entry.lastrev.time = changeset.committed_on
entry.lastrev.author = changeset.committer
end
end
end
@@ -88,19 +70,20 @@ class Repository::Darcs < Repository
scm_info = scm.info
if scm_info
db_last_id = latest_changeset ? latest_changeset.scmid : nil
next_rev = latest_changeset ? latest_changeset.revision.to_i + 1 : 1
next_rev = latest_changeset ? latest_changeset.revision.to_i + 1 : 1
# latest revision in the repository
scm_revision = scm_info.lastrev.scmid
unless changesets.find_by_scmid(scm_revision)
revisions = scm.revisions('', db_last_id, nil, :with_path => true)
transaction do
revisions.reverse_each do |revision|
changeset = Changeset.create(:repository => self,
:revision => next_rev,
:scmid => revision.scmid,
:committer => revision.author,
changeset = Changeset.create(:repository => self,
:revision => next_rev,
:scmid => revision.scmid,
:committer => revision.author,
:committed_on => revision.time,
:comments => revision.message)
:comments => revision.message)
revision.paths.each do |change|
changeset.create_change(change)
end
+4 -15
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# FileSystem adapter
# File written by Paul Rivier, at Demotera.
@@ -24,15 +24,7 @@ class Repository::Filesystem < Repository
attr_protected :root_url
validates_presence_of :url
def self.human_attribute_name(attribute_key_name)
attr_name = attribute_key_name
if attr_name == "url"
attr_name = "root_directory"
end
super(attr_name)
end
def self.scm_adapter_class
def scm_adapter
Redmine::Scm::Adapters::FilesystemAdapter
end
@@ -40,10 +32,6 @@ class Repository::Filesystem < Repository
'Filesystem'
end
def supports_all_revisions?
false
end
def entries(path=nil, identifier=nil)
scm.entries(path, identifier)
end
@@ -51,4 +39,5 @@ class Repository::Filesystem < Repository
def fetch_changesets
nil
end
end
+34 -115
View File
@@ -1,7 +1,6 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
# Copyright (C) 2007 Patrick Aljord patcito@ŋmail.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
@@ -22,15 +21,7 @@ class Repository::Git < Repository
attr_protected :root_url
validates_presence_of :url
def self.human_attribute_name(attribute_key_name)
attr_name = attribute_key_name
if attr_name == "url"
attr_name = "path_to_repository"
end
super(attr_name)
end
def self.scm_adapter_class
def scm_adapter
Redmine::Scm::Adapters::GitAdapter
end
@@ -38,25 +29,6 @@ class Repository::Git < Repository
'Git'
end
def report_last_commit
extra_report_last_commit
end
def extra_report_last_commit
return false if extra_info.nil?
v = extra_info["extra_report_last_commit"]
return false if v.nil?
v.to_s != '0'
end
def supports_directory_revisions?
true
end
def repo_log_encoding
'UTF-8'
end
# Returns the identifier for the given git changeset
def self.changeset_identifier(changeset)
changeset.scmid
@@ -75,105 +47,52 @@ class Repository::Git < Repository
scm.tags
end
def default_branch
scm.default_branch
end
def find_changeset_by_name(name)
return nil if name.nil? || name.empty?
e = changesets.find(:first, :conditions => ['revision = ?', name.to_s])
return e if e
changesets.find(:first, :conditions => ['scmid LIKE ?', "#{name}%"])
end
def entries(path=nil, identifier=nil)
scm.entries(path,
identifier,
options = {:report_last_commit => extra_report_last_commit})
end
# With SCMs that have a sequential commit numbering,
# such as Subversion and Mercurial,
# Redmine is able to be clever and only fetch changesets
# going forward from the most recent one it knows about.
#
# However, Git does not have a sequential commit numbering.
#
# In order to fetch only new adding revisions,
# Redmine needs to parse revisions per branch.
# Branch "last_scmid" is for this requirement.
#
# In Git and Mercurial, revisions are not in date order.
# Redmine Mercurial fixed issues.
# 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.
# Redmine Mercurial model stored revisions ordered by database id to database.
# So, Redmine Mercurial model can use correct ordering revisions.
#
# Redmine Mercurial adapter uses "hg log -r 0:tip --limit 10"
# 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
# commits into the middle of the repository history, so we should parse
# the entire log.
#
# Since it's way too slow for large repositories,
# we only parse 1 week before the last known commit.
#
# The repository can still be fully reloaded by calling #clear_changesets
# before fetching changesets (eg. for offline resync)
def fetch_changesets
scm_brs = branches
return if scm_brs.nil? || scm_brs.empty?
h1 = extra_info || {}
h = h1.dup
h["branches"] ||= {}
h["db_consistent"] ||= {}
if changesets.count == 0
h["db_consistent"]["ordering"] = 1
merge_extra_info(h)
self.save
elsif ! h["db_consistent"].has_key?("ordering")
h["db_consistent"]["ordering"] = 0
merge_extra_info(h)
self.save
end
scm_brs.each do |br|
from_scmid = nil
from_scmid = h["branches"][br]["last_scmid"] if h["branches"][br]
h["branches"][br] ||= {}
scm.revisions('', from_scmid, br, {:reverse => true}) do |rev|
db_rev = find_changeset_by_name(rev.revision)
transaction do
if db_rev.nil?
save_revision(rev)
end
h["branches"][br]["last_scmid"] = rev.scmid
merge_extra_info(h)
self.save
end
end
end
end
c = changesets.find(:first, :order => 'committed_on DESC')
since = (c ? c.committed_on - 7.days : nil)
def save_revision(rev)
changeset = Changeset.new(
:repository => self,
:revision => rev.identifier,
:scmid => rev.scmid,
:committer => rev.author,
:committed_on => rev.time,
:comments => rev.message
)
if changeset.save
rev.paths.each do |file|
Change.create(
:changeset => changeset,
:action => file[:action],
:path => file[:path])
end
end
revisions = scm.revisions('', nil, nil, :all => true, :since => since)
return if revisions.nil? || revisions.empty?
recent_changesets = changesets.find(:all, :conditions => ['committed_on >= ?', since])
# Clean out revisions that are no longer in git
recent_changesets.each {|c| c.destroy unless revisions.detect {|r| r.scmid.to_s == c.scmid.to_s }}
# Subtract revisions that redmine already knows about
recent_revisions = recent_changesets.map{|c| c.scmid}
revisions.reject!{|r| recent_revisions.include?(r.scmid)}
# Save the remaining ones to the database
revisions.each{|r| r.save(self)} unless revisions.nil?
end
private :save_revision
def latest_changesets(path,rev,limit=10)
revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false)
+69 -111
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -24,17 +24,7 @@ class Repository::Mercurial < Repository
attr_protected :root_url
validates_presence_of :url
FETCH_AT_ONCE = 100 # number of changesets to fetch at once
def self.human_attribute_name(attribute_key_name)
attr_name = attribute_key_name
if attr_name == "url"
attr_name = "path_to_repository"
end
super(attr_name)
end
def self.scm_adapter_class
def scm_adapter
Redmine::Scm::Adapters::MercurialAdapter
end
@@ -42,106 +32,74 @@ class Repository::Mercurial < Repository
'Mercurial'
end
def supports_directory_revisions?
true
end
def repo_log_encoding
'UTF-8'
end
# Returns the readable identifier for the given mercurial changeset
def self.format_changeset_identifier(changeset)
"#{changeset.revision}:#{changeset.scmid}"
end
# Returns the identifier for the given Mercurial changeset
def self.changeset_identifier(changeset)
changeset.scmid
end
def diff_format_revisions(cs, cs_to, sep=':')
super(cs, cs_to, ' ')
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.nil? || name.empty?
if /[^\d]/ =~ name or name.to_s.size > 8
e = changesets.find(:first, :conditions => ['scmid = ?', name.to_s])
else
e = changesets.find(:first, :conditions => ['revision = ?', name.to_s])
end
return e if e
changesets.find(:first, :conditions => ['scmid LIKE ?', "#{name}%"]) # last ditch
end
# Returns the latest changesets for +path+; sorted by revision number
#
# 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?
def latest_changesets(path, rev, limit=10)
changesets.find(:all, :include => :user,
:conditions => latest_changesets_cond(path, rev, limit),
:limit => limit, :order => "#{Changeset.table_name}.id DESC")
end
def latest_changesets_cond(path, rev, limit)
cond, args = [], []
if scm.branchmap.member? rev
# Mercurial named branch is *stable* in each revision.
# So, named branch can be stored in database.
# Mercurial provides *bookmark* which is equivalent with git branch.
# But, bookmark is not implemented.
cond << "#{Changeset.table_name}.scmid IN (?)"
# Revisions in root directory and sub directory are not equal.
# So, in order to get correct limit, we need to get all revisions.
# But, it is very heavy.
# Mercurial does not treat direcotry.
# So, "hg log DIR" is very heavy.
branch_limit = path.blank? ? limit : ( limit * 5 )
args << scm.nodes_in_branch(rev, :limit => branch_limit)
elsif last = rev ? find_changeset_by_name(scm.tagmap[rev] || rev) : nil
cond << "#{Changeset.table_name}.id <= ?"
args << last.id
end
unless path.blank?
cond << "EXISTS (SELECT * FROM #{Change.table_name}
WHERE #{Change.table_name}.changeset_id = #{Changeset.table_name}.id
AND (#{Change.table_name}.path = ?
OR #{Change.table_name}.path LIKE ? ESCAPE ?))"
args << path.with_leading_slash
args << "#{path.with_leading_slash.gsub(/[%_\\]/) { |s| "\\#{s}" }}/%" << '\\'
end
[cond.join(' AND '), *args] unless cond.empty?
end
private :latest_changesets_cond
def fetch_changesets
return if scm.info.nil?
scm_rev = scm.info.lastrev.revision.to_i
db_rev = latest_changeset ? latest_changeset.revision.to_i : -1
return unless db_rev < scm_rev # already up-to-date
logger.debug "Fetching changesets for repository #{url}" if logger
(db_rev + 1).step(scm_rev, FETCH_AT_ONCE) do |i|
transaction do
scm.each_revision('', i, [i + FETCH_AT_ONCE - 1, scm_rev].min) do |re|
cs = Changeset.create(:repository => self,
:revision => re.revision,
:scmid => re.scmid,
:committer => re.author,
:committed_on => re.time,
:comments => re.message)
re.paths.each { |e| cs.create_change(e) }
def entries(path=nil, identifier=nil)
entries=scm.entries(path, identifier)
if entries
entries.each do |entry|
next unless entry.is_file?
# Set the filesize unless browsing a specific revision
if identifier.nil?
full_path = File.join(root_url, entry.path)
entry.size = File.stat(full_path).size if File.file?(full_path)
end
# Search the DB for the entry's last change
change = changes.find(:first, :conditions => ["path = ?", scm.with_leading_slash(entry.path)], :order => "#{Changeset.table_name}.committed_on DESC")
if change
entry.lastrev.identifier = change.changeset.revision
entry.lastrev.name = change.changeset.revision
entry.lastrev.author = change.changeset.committer
entry.lastrev.revision = change.revision
end
end
end
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
# latest revision found in database
db_revision = latest_changeset ? latest_changeset.revision.to_i : -1
# latest revision in the repository
latest_revision = scm_info.lastrev
return if latest_revision.nil?
scm_revision = latest_revision.identifier.to_i
if db_revision < scm_revision
logger.debug "Fetching changesets for repository #{url}" if logger && logger.debug?
identifier_from = db_revision + 1
while (identifier_from <= scm_revision)
# loads changesets by batches of 100
identifier_to = [identifier_from + 99, scm_revision].min
revisions = scm.revisions('', identifier_from, identifier_to, :with_paths => true)
transaction do
revisions.each do |revision|
changeset = Changeset.create(:repository => self,
:revision => revision.identifier,
:scmid => revision.scmid,
:committer => revision.author,
:committed_on => revision.time,
:comments => revision.message)
revision.paths.each do |change|
changeset.create_change(change)
end
end
end unless revisions.nil?
identifier_from = identifier_to + 1
end
end
end
self
end
end
+7 -15
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -22,7 +22,7 @@ class Repository::Subversion < Repository
validates_presence_of :url
validates_format_of :url, :with => /^(http|https|svn(\+[^\s:\/\\]+)?|file):\/\/.+/i
def self.scm_adapter_class
def scm_adapter
Redmine::Scm::Adapters::SubversionAdapter
end
@@ -30,14 +30,6 @@ class Repository::Subversion < Repository
'Subversion'
end
def supports_directory_revisions?
true
end
def repo_log_encoding
'UTF-8'
end
def latest_changesets(path, rev, limit=10)
revisions = scm.revisions(path, rev, nil, :limit => limit)
revisions ? changesets.find_all_by_revision(revisions.collect(&:identifier), :order => "committed_on DESC", :include => :user) : []
@@ -64,11 +56,11 @@ class Repository::Subversion < Repository
revisions = scm.revisions('', identifier_to, identifier_from, :with_paths => true)
revisions.reverse_each do |revision|
transaction do
changeset = Changeset.create(:repository => self,
:revision => revision.identifier,
:committer => revision.author,
changeset = Changeset.create(:repository => self,
:revision => revision.identifier,
:committer => revision.author,
:committed_on => revision.time,
:comments => revision.message)
:comments => revision.message)
revision.paths.each do |change|
changeset.create_change(change)
+3 -19
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -20,12 +20,6 @@ class Role < ActiveRecord::Base
BUILTIN_NON_MEMBER = 1
BUILTIN_ANONYMOUS = 2
ISSUES_VISIBILITY_OPTIONS = [
['all', :label_issues_visibility_all],
['default', :label_issues_visibility_public],
['own', :label_issues_visibility_own]
]
named_scope :givable, { :conditions => "builtin = 0", :order => 'position' }
named_scope :builtin, lambda { |*args|
compare = 'not' if args.first == true
@@ -49,9 +43,7 @@ class Role < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name
validates_length_of :name, :maximum => 30
validates_inclusion_of :issues_visibility,
:in => ISSUES_VISIBILITY_OPTIONS.collect(&:first),
:if => lambda {|role| role.respond_to?(:issues_visibility)}
validates_format_of :name, :with => /^[\w\s\'\-]*$/i
def permissions
read_attribute(:permissions) || []
@@ -93,14 +85,6 @@ class Role < ActiveRecord::Base
name
end
def name
case builtin
when 1; l(:label_role_non_member, :default => read_attribute(:name))
when 2; l(:label_role_anonymous, :default => read_attribute(:name))
else; read_attribute(:name)
end
end
# Return true if the role is a builtin role
def builtin?
self.builtin != 0
+2 -3
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -65,7 +65,6 @@ class Setting < ActiveRecord::Base
UTF-16LE
EUC-JP
Shift_JIS
CP932
GB18030
GBK
ISCII91
+2 -9
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -39,11 +39,6 @@ class TimeEntry < ActiveRecord::Base
validates_numericality_of :hours, :allow_nil => true, :message => :invalid
validates_length_of :comments, :maximum => 255, :allow_nil => true
named_scope :visible, lambda {|*args| {
:include => :project,
:conditions => Project.allowed_to_condition(args.shift || User.current, :view_time_entries, *args)
}}
def after_initialize
if new_record? && self.activity.nil?
if default_activity = TimeEntryActivity.default
@@ -84,9 +79,7 @@ class TimeEntry < ActiveRecord::Base
(usr == user && usr.allowed_to?(:edit_own_time_entries, project)) || usr.allowed_to?(:edit_time_entries, project)
end
# TODO: remove this method in 1.3.0
def self.visible_by(usr)
ActiveSupport::Deprecation.warn "TimeEntry.visible_by is deprecated and will be removed in Redmine 1.3.0. Use the visible scope instead."
with_scope(:find => { :conditions => Project.allowed_to_condition(usr, :view_time_entries) }) do
yield
end
+1 -2
View File
@@ -31,8 +31,7 @@ class Tracker < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name
validates_length_of :name, :maximum => 30
named_scope :named, lambda {|arg| { :conditions => ["LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip]}}
validates_format_of :name, :with => /^[\w\s\'\-]*$/i
def to_s; name end
+15 -110
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -48,8 +48,8 @@ class User < Principal
has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
has_many :changesets, :dependent => :nullify
has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
has_one :rss_token, :class_name => 'Token', :conditions => "action='feeds'"
has_one :api_token, :class_name => 'Token', :conditions => "action='api'"
has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
has_one :api_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='api'"
belongs_to :auth_source
# Active non-anonymous users scope
@@ -68,23 +68,13 @@ class User < Principal
# Login must contain lettres, numbers, underscores only
validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
validates_length_of :login, :maximum => 30
validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
validates_length_of :firstname, :lastname, :maximum => 30
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
before_destroy :remove_references_before_destroy
named_scope :in_group, lambda {|group|
group_id = group.is_a?(Group) ? group.id : group.to_i
{ :conditions => ["#{User.table_name}.id IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id] }
}
named_scope :not_in_group, lambda {|group|
group_id = group.is_a?(Group) ? group.id : group.to_i
{ :conditions => ["#{User.table_name}.id NOT IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id] }
}
def before_create
self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
true
@@ -92,14 +82,11 @@ class User < Principal
def before_save
# update hashed_password if password was set
if self.password && self.auth_source_id.blank?
salt_password(password)
end
self.hashed_password = User.hash_password(self.password) if self.password && self.auth_source_id.blank?
end
def reload(*args)
@name = nil
@projects_by_role = nil
super
end
@@ -133,7 +120,7 @@ class User < Principal
return nil unless user.auth_source.authenticate(login, password)
else
# authentication with local password
return nil unless user.check_password?(password)
return nil unless User.hash_password(password) == user.hashed_password
end
else
# user is not yet registered, try to authenticate with available sources
@@ -212,22 +199,14 @@ class User < Principal
update_attribute(:status, STATUS_LOCKED)
end
# Returns true if +clear_password+ is the correct user's password, otherwise false
def check_password?(clear_password)
if auth_source_id.present?
auth_source.authenticate(self.login, clear_password)
else
User.hash_password("#{salt}#{User.hash_password clear_password}") == hashed_password
User.hash_password(clear_password) == self.hashed_password
end
end
# Generates a random salt and computes hashed_password for +clear_password+
# The hashed password is stored in the following form: SHA1(salt + SHA1(password))
def salt_password(clear_password)
self.salt = User.generate_salt
self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}")
end
# Does the backend storage allow this user to change their password?
def change_password_allowed?
return true if auth_source_id.blank?
@@ -371,33 +350,16 @@ class User < Principal
!roles_for_project(project).detect {|role| role.member?}.nil?
end
# Returns a hash of user's projects grouped by roles
def projects_by_role
return @projects_by_role if @projects_by_role
@projects_by_role = Hash.new {|h,k| h[k]=[]}
memberships.each do |membership|
membership.roles.each do |role|
@projects_by_role[role] << membership.project if membership.project
end
end
@projects_by_role.each do |role, projects|
projects.uniq!
end
@projects_by_role
end
# Return true if the user is allowed to do the specified action on a specific context
# Action can be:
# * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
# * a permission Symbol (eg. :edit_project)
# Context can be:
# * a project : returns true if user is allowed to do the specified action on this project
# * an array of projects : returns true if user is allowed on every project
# * a group of projects : returns true if user is allowed on every project
# * nil with options[:global] set : check if user has at least one role allowed for this action,
# or falls back to Non Member / Anonymous permissions depending if the user is logged
def allowed_to?(action, context, options={}, &block)
def allowed_to?(action, context, options={})
if context && context.is_a?(Project)
# No action allowed on archived projects
return false unless context.active?
@@ -408,15 +370,12 @@ class User < Principal
roles = roles_for_project(context)
return false unless roles
roles.detect {|role|
(context.is_public? || role.member?) &&
role.allowed_to?(action) &&
(block_given? ? yield(role, self) : true)
}
roles.detect {|role| (context.is_public? || role.member?) && role.allowed_to?(action)}
elsif context && context.is_a?(Array)
# Authorize if user is authorized on every element of the array
context.map do |project|
allowed_to?(action, project, options, &block)
allowed_to?(action,project,options)
end.inject do |memo,allowed|
memo && allowed
end
@@ -426,11 +385,7 @@ class User < Principal
# authorize if user has at least one role that has this permission
roles = memberships.collect {|m| m.roles}.flatten.uniq
roles << (self.logged? ? Role.non_member : Role.anonymous)
roles.detect {|role|
role.allowed_to?(action) &&
(block_given? ? yield(role, self) : true)
}
roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
else
false
end
@@ -438,8 +393,8 @@ class User < Principal
# Is the user allowed to do the specified action on any project?
# See allowed_to? for the actions and valid options.
def allowed_to_globally?(action, options, &block)
allowed_to?(action, nil, options.reverse_merge(:global => true), &block)
def allowed_to_globally?(action, options)
allowed_to?(action, nil, options.reverse_merge(:global => true))
end
safe_attributes 'login',
@@ -518,20 +473,6 @@ class User < Principal
anonymous_user
end
# Salts all existing unsalted passwords
# It changes password storage scheme from SHA1(password) to SHA1(salt + SHA1(password))
# This method is used in the SaltPasswords migration and is to be kept as is
def self.salt_unsalted_passwords!
transaction do
User.find_each(:conditions => "salt IS NULL OR salt = ''") do |user|
next if user.hashed_password.blank?
salt = User.generate_salt
hashed_password = User.hash_password("#{salt}#{user.hashed_password}")
User.update_all("salt = '#{salt}', hashed_password = '#{hashed_password}'", ["id = ?", user.id] )
end
end
end
protected
def validate
@@ -543,41 +484,10 @@ class User < Principal
private
# Removes references that are not handled by associations
# Things that are not deleted are reassociated with the anonymous user
def remove_references_before_destroy
return if self.id.nil?
substitute = User.anonymous
Attachment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
Comment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
Issue.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
Issue.update_all 'assigned_to_id = NULL', ['assigned_to_id = ?', id]
Journal.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
JournalDetail.update_all ['old_value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s]
JournalDetail.update_all ['value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s]
Message.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
News.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
# Remove private queries and keep public ones
Query.delete_all ['user_id = ? AND is_public = ?', id, false]
Query.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
TimeEntry.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
Token.delete_all ['user_id = ?', id]
Watcher.delete_all ['user_id = ?', id]
WikiContent.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
WikiContent::Version.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
end
# Return password digest
def self.hash_password(clear_password)
Digest::SHA1.hexdigest(clear_password || "")
end
# Returns a 128bits random salt as a hex string (32 chars long)
def self.generate_salt
ActiveSupport::SecureRandom.hex(16)
end
end
class AnonymousUser < User
@@ -598,9 +508,4 @@ class AnonymousUser < User
def mail; nil end
def time_zone; nil end
def rss_key; nil end
# Anonymous user can not be destroyed
def destroy
false
end
end
-3
View File
@@ -51,7 +51,4 @@ class UserPreference < ActiveRecord::Base
def comments_sorting; self[:comments_sorting] end
def comments_sorting=(order); self[:comments_sorting]=order end
def warn_on_leaving_unsaved; self[:warn_on_leaving_unsaved] || '1'; end
def warn_on_leaving_unsaved=(value); self[:warn_on_leaving_unsaved]=value; end
end
-1
View File
@@ -33,7 +33,6 @@ class Version < ActiveRecord::Base
validates_inclusion_of :status, :in => VERSION_STATUSES
validates_inclusion_of :sharing, :in => VERSION_SHARINGS
named_scope :named, lambda {|arg| { :conditions => ["LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip]}}
named_scope :open, :conditions => {:status => 'open'}
named_scope :visible, lambda {|*args| { :include => :project,
:conditions => Project.allowed_to_condition(args.first || User.current, :view_issues) } }
+1 -10
View File
@@ -44,26 +44,17 @@ class Wiki < ActiveRecord::Base
# find the page with the given title
def find_page(title, options = {})
@page_found_with_redirect = false
title = start_page if title.blank?
title = Wiki.titleize(title)
page = pages.first(:conditions => ["LOWER(title) = LOWER(?)", title])
if !page && !(options[:with_redirect] == false)
# search for a redirect
redirect = redirects.first(:conditions => ["LOWER(title) = LOWER(?)", title])
if redirect
page = find_page(redirect.redirects_to, :with_redirect => false)
@page_found_with_redirect = true
end
page = find_page(redirect.redirects_to, :with_redirect => false) if redirect
end
page
end
# Returns true if the last page was found with a redirect
def page_found_with_redirect?
@page_found_with_redirect
end
# Finds a page by title
# The given string can be of one of the forms: "title" or "project:title"
# Examples:
+2 -2
View File
@@ -1,5 +1,5 @@
# RedMine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
+10 -29
View File
@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -30,9 +30,8 @@ class WikiPage < ActiveRecord::Base
:datetime => :created_on,
:url => Proc.new {|o| {:controller => 'wiki', :action => 'show', :project_id => o.wiki.project, :id => o.title}}
acts_as_searchable :columns => ['title', "#{WikiContent.table_name}.text"],
acts_as_searchable :columns => ['title', 'text'],
:include => [{:wiki => :project}, :content],
:permission => :view_wiki_pages,
:project_key => "#{Wiki.table_name}.project_id"
attr_accessor :redirect_existing_links
@@ -42,12 +41,6 @@ class WikiPage < ActiveRecord::Base
validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false
validates_associated :content
# eager load information about last updates, without loading text
named_scope :with_updated_on, {
:select => "#{WikiPage.table_name}.*, #{WikiContent.table_name}.updated_on",
:joins => "LEFT JOIN #{WikiContent.table_name} ON #{WikiContent.table_name}.page_id = #{WikiPage.table_name}.id"
}
# Wiki pages that are protected by default
DEFAULT_PROTECTED_PAGES = %w(sidebar)
@@ -128,18 +121,6 @@ class WikiPage < ActiveRecord::Base
content.text if content
end
def updated_on
unless @updated_on
if time = read_attribute(:updated_on)
# content updated_on was eager loaded with the page
@updated_on = Time.parse(time) rescue nil
else
@updated_on = content && content.updated_on
end
end
@updated_on
end
# Returns true if usr is allowed to edit the page, otherwise false
def editable_by?(usr)
!protected? || usr.allowed_to?(:protect_wiki_pages, wiki.project)
@@ -168,13 +149,17 @@ class WikiPage < ActiveRecord::Base
end
end
class WikiDiff < Redmine::Helpers::Diff
attr_reader :content_to, :content_from
class WikiDiff
attr_reader :diff, :words, :content_to, :content_from
def initialize(content_to, content_from)
@content_to = content_to
@content_from = content_from
super(content_to.text, content_from.text)
@words = content_to.text.split(/(\s+)/)
@words = @words.select {|word| word != ' '}
words_from = content_from.text.split(/(\s+)/)
words_from = words_from.select {|word| word != ' '}
@diff = words_from.diff @words
end
end
@@ -212,10 +197,6 @@ class WikiAnnotate
break unless @lines.detect { |line| line[0].nil? }
current = current.previous
end
@lines.each { |line|
line[0] ||= current.version
# if the last known version is > 1 (eg. history was cleared), we don't know the author
line[1] ||= current.author if current.version == 1
}
@lines.each { |line| line[0] ||= current.version }
end
end
+2 -2
View File
@@ -89,8 +89,8 @@ class Workflow < ActiveRecord::Base
else
transaction do
delete_all :tracker_id => target_tracker.id, :role_id => target_role.id
connection.insert "INSERT INTO #{Workflow.table_name} (tracker_id, role_id, old_status_id, new_status_id, author, assignee)" +
" SELECT #{target_tracker.id}, #{target_role.id}, old_status_id, new_status_id, author, assignee" +
connection.insert "INSERT INTO #{Workflow.table_name} (tracker_id, role_id, old_status_id, new_status_id)" +
" SELECT #{target_tracker.id}, #{target_role.id}, old_status_id, new_status_id" +
" FROM #{Workflow.table_name}" +
" WHERE tracker_id = #{source_tracker.id} AND role_id = #{source_role.id}"
end
+8 -6
View File
@@ -21,14 +21,16 @@
<%= content_tag('p', l(:label_no_data), :class => 'nodata') if @events_by_day.empty? %>
<div style="float:left;">
<%= link_to_content_update('&#171; ' + l(:label_previous),
params.merge(:from => @date_to - @days - 1),
:title => l(:label_date_from_to, :start => format_date(@date_to - 2*@days), :end => format_date(@date_to - @days - 1))) %>
<%= link_to_remote(('&#171; ' + l(:label_previous)),
{:update => "content", :url => params.merge(:from => @date_to - @days - 1), :method => :get, :complete => 'window.scrollTo(0,0)'},
{:href => url_for(params.merge(:from => @date_to - @days - 1)),
:title => l(:label_date_from_to, :start => format_date(@date_to - 2*@days), :end => format_date(@date_to - @days - 1))}) %>
</div>
<div style="float:right;">
<%= link_to_content_update(l(:label_next) + ' &#187;',
params.merge(:from => @date_to + @days - 1),
:title => l(:label_date_from_to, :start => format_date(@date_to), :end => format_date(@date_to + @days - 1))) unless @date_to >= Date.today %>
<%= link_to_remote((l(:label_next) + ' &#187;'),
{:update => "content", :url => params.merge(:from => @date_to + @days - 1), :method => :get, :complete => 'window.scrollTo(0,0)'},
{:href => url_for(params.merge(:from => @date_to + @days - 1)),
:title => l(:label_date_from_to, :start => format_date(@date_to), :end => format_date(@date_to + @days - 1))}) unless @date_to >= Date.today %>
</div>
&nbsp;
<% other_formats_links do |f| %>
+4 -5
View File
@@ -4,11 +4,10 @@
<table class="list">
<% @checklist.each do |label, result| %>
<tr class="<%= cycle 'odd', 'even' %>">
<td><%= l(label) %></td>
<td width="30px"><%= image_tag((result ? 'true.png' : 'exclamation.png'),
:style => "vertical-align:bottom;") %></td>
</tr>
<tr class="<%= cycle 'odd', 'even' %>">
<td><%= l(label) %></td>
<td width="30px"><%= image_tag((result ? 'true.png' : 'exclamation.png'), :style => "vertical-align:bottom;") %></td>
</tr>
<% end %>
</table>
-1
View File
@@ -11,7 +11,6 @@
<label><%= l(:label_project) %>:</label>
<%= text_field_tag 'name', params[:name], :size => 30 %>
<%= submit_tag l(:button_apply), :class => "small", :name => nil %>
<%= link_to l(:button_clear), {:controller => 'admin', :action => 'projects'}, :class => 'icon icon-reload' %>
</fieldset>
<% end %>
&nbsp;
+17 -8
View File
@@ -1,16 +1,16 @@
<h2><%= @query.new_record? ? l(:label_calendar) : h(@query.name) %></h2>
<h2><%= l(:label_calendar) %></h2>
<% form_tag({:controller => 'calendars', :action => 'show', :project_id => @project}, :method => :get, :id => 'query_form') do %>
<%= hidden_field_tag 'set_filter', '1' %>
<fieldset id="filters" class="collapsible <%= @query.new_record? ? "" : "collapsed" %>">
<% form_tag(calendar_path, :method => :put, :id => 'query_form') do %>
<%= hidden_field_tag('project_id', @project.to_param) if @project%>
<fieldset id="filters" class="collapsible">
<legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
<div style="<%= @query.new_record? ? "" : "display: none;" %>">
<div>
<%= render :partial => 'queries/filters', :locals => {:query => @query} %>
</div>
</fieldset>
<p style="float:right;">
<%= link_to_previous_month(@year, @month) %> | <%= link_to_next_month(@year, @month) %>
<%= link_to_previous_month(@year, @month, :project => @project) %> | <%= link_to_next_month(@year, @month, :project => @project) %>
</p>
<p class="buttons">
@@ -19,8 +19,17 @@
<%= label_tag('year', l(:label_year)) %>
<%= select_year(@year, :prefix => "year", :discard_type => true) %>
<%= link_to_function l(:button_apply), '$("query_form").submit()', :class => 'icon icon-checked' %>
<%= link_to l(:button_clear), { :project_id => @project, :set_filter => 1 }, :class => 'icon icon-reload' %>
<%= link_to_remote l(:button_apply),
{ :url => { :set_filter => (@query.new_record? ? 1 : nil) },
:update => "content",
:with => "Form.serialize('query_form')"
}, :class => 'icon icon-checked' %>
<%= link_to_remote l(:button_clear),
{ :url => { :project_id => @project, :set_filter => (@query.new_record? ? 1 : nil) },
:method => :put,
:update => "content",
}, :class => 'icon icon-reload' if @query.new_record? %>
</p>
<% end %>
+32 -22
View File
@@ -1,56 +1,66 @@
<% diff = Redmine::UnifiedDiff.new(diff, :type => diff_type, :max_lines => Setting.diff_max_lines_displayed.to_i) -%>
<% diff.each do |table_file| -%>
<div class="autoscroll">
<% if diff.diff_type == 'sbs' -%>
<% if diff_type == 'sbs' -%>
<table class="filecontent">
<thead>
<tr><th colspan="4" class="filename"><%=to_utf8 table_file.file_name %></th></tr>
<tr><th colspan="4" class="filename"><%= table_file.file_name %></th></tr>
</thead>
<tbody>
<% table_file.each_line do |spacing, line| -%>
<% if spacing -%>
<% prev_line_left, prev_line_right = nil, nil -%>
<% table_file.keys.sort.each do |key| -%>
<% if prev_line_left && prev_line_right && (table_file[key].nb_line_left != prev_line_left+1) && (table_file[key].nb_line_right != prev_line_right+1) -%>
<tr class="spacing">
<th class="line-num">...</th><td></td><th class="line-num">...</th><td></td>
</tr>
<th class="line-num">...</th><td></td><th class="line-num">...</th><td></td>
<% end -%>
<tr>
<th class="line-num"><%= line.nb_line_left %></th>
<td class="line-code <%= line.type_diff_left %>">
<pre><%=to_utf8 line.html_line_left %></pre>
<th class="line-num"><%= table_file[key].nb_line_left %></th>
<td class="line-code <%= table_file[key].type_diff_left %>">
<pre><%=to_utf8 table_file[key].line_left %></pre>
</td>
<th class="line-num"><%= line.nb_line_right %></th>
<td class="line-code <%= line.type_diff_right %>">
<pre><%=to_utf8 line.html_line_right %></pre>
<th class="line-num"><%= table_file[key].nb_line_right %></th>
<td class="line-code <%= table_file[key].type_diff_right %>">
<pre><%=to_utf8 table_file[key].line_right %></pre>
</td>
</tr>
<% prev_line_left, prev_line_right = table_file[key].nb_line_left.to_i, table_file[key].nb_line_right.to_i -%>
<% end -%>
</tbody>
</table>
<% else -%>
<table class="filecontent">
<table class="filecontent syntaxhl">
<thead>
<tr><th colspan="3" class="filename"><%=to_utf8 table_file.file_name %></th></tr>
<tr><th colspan="3" class="filename"><%= table_file.file_name %></th></tr>
</thead>
<tbody>
<% table_file.each_line do |spacing, line| %>
<% if spacing -%>
<% prev_line_left, prev_line_right = nil, nil -%>
<% table_file.keys.sort.each do |key, line| %>
<% if prev_line_left && prev_line_right && (table_file[key].nb_line_left != prev_line_left+1) && (table_file[key].nb_line_right != prev_line_right+1) -%>
<tr class="spacing">
<th class="line-num">...</th><th class="line-num">...</th><td></td>
<th class="line-num">...</th><th class="line-num">...</th><td></td>
</tr>
<% end -%>
<tr>
<th class="line-num"><%= line.nb_line_left %></th>
<th class="line-num"><%= line.nb_line_right %></th>
<td class="line-code <%= line.type_diff %>">
<pre><%=to_utf8 line.html_line %></pre>
<th class="line-num"><%= table_file[key].nb_line_left %></th>
<th class="line-num"><%= table_file[key].nb_line_right %></th>
<% if table_file[key].line_left.empty? -%>
<td class="line-code <%= table_file[key].type_diff_right %>">
<pre><%=to_utf8 table_file[key].line_right %></pre>
</td>
<% else -%>
<td class="line-code <%= table_file[key].type_diff_left %>">
<pre><%=to_utf8 table_file[key].line_left %></pre>
</td>
<% end -%>
</tr>
<% prev_line_left = table_file[key].nb_line_left.to_i if table_file[key].nb_line_left.to_i > 0 -%>
<% prev_line_right = table_file[key].nb_line_right.to_i if table_file[key].nb_line_right.to_i > 0 -%>
<% end -%>
</tbody>
</table>
<% end -%>
</div>
<% end -%>
+1 -1
View File
@@ -115,7 +115,7 @@
<li><%= context_menu_link l(:button_move), new_issue_move_path(:ids => @issues.collect(&:id)),
:class => 'icon-move', :disabled => !@can[:move] %></li>
<li><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :ids => @issues.collect(&:id), :back_url => @back},
:method => :post, :confirm => issues_destroy_confirmation_message(@issues), :class => 'icon-del', :disabled => !@can[:delete] %></li>
:method => :post, :confirm => l(:text_issues_destroy_confirmation), :class => 'icon-del', :disabled => !@can[:delete] %></li>
<%= call_hook(:view_issues_context_menu_end, {:issues => @issues, :can => @can, :back => @back }) %>
</ul>
@@ -1,33 +0,0 @@
<ul>
<% if !@time_entry.nil? -%>
<li><%= context_menu_link l(:button_edit), {:controller => 'timelog', :action => 'edit', :id => @time_entry},
:class => 'icon-edit', :disabled => !@can[:edit] %></li>
<% else %>
<li><%= context_menu_link l(:button_edit), {:controller => 'timelog', :action => 'bulk_edit', :ids => @time_entries.collect(&:id)},
:class => 'icon-edit', :disabled => !@can[:edit] %></li>
<% end %>
<%= call_hook(:view_time_entries_context_menu_start, {:time_entries => @time_entries, :can => @can, :back => @back }) %>
<% if @activities.present? -%>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_activity) %></a>
<ul>
<% @activities.each do |u| -%>
<li><%= context_menu_link u.name, {:controller => 'timelog', :action => 'bulk_edit', :ids => @time_entries.collect(&:id), :time_entry => {'activity_id' => u}, :back_url => @back}, :method => :post,
:selected => (@time_entry && u == @time_entry.activity), :disabled => !@can[:update] %></li>
<% end -%>
<li><%= context_menu_link l(:label_nobody), {:controller => 'timelog', :action => 'bulk_edit', :ids => @time_entries.collect(&:id), :time_entry => {'activity_id' => 'none'}, :back_url => @back}, :method => :post,
:selected => (@time_entry && @time_entry.activity.nil?), :disabled => !@can[:update] %></li>
</ul>
</li>
<% end %>
<%= call_hook(:view_time_entries_context_menu_end, {:time_entries => @time_entries, :can => @can, :back => @back }) %>
<li>
<%= context_menu_link l(:button_delete),
{:controller => 'timelog', :action => 'destroy', :ids => @time_entries.collect(&:id), :back_url => @back},
:method => :delete, :confirm => l(:text_time_entries_destroy_confirmation), :class => 'icon-del', :disabled => !@can[:delete] %>
</li>
</ul>
+10 -18
View File
@@ -18,41 +18,33 @@ function toggle_custom_field_format() {
Element.hide(p_length.parentNode);
Element.hide(p_regexp.parentNode);
if (p_searchable) Element.show(p_searchable.parentNode);
Element.show(p_values.parentNode);
Element.show(p_values);
break;
case "bool":
p_default.setAttribute('type','checkbox');
Element.hide(p_length.parentNode);
Element.hide(p_regexp.parentNode);
if (p_searchable) Element.hide(p_searchable.parentNode);
Element.hide(p_values.parentNode);
Element.hide(p_values);
break;
case "date":
Element.hide(p_length.parentNode);
Element.hide(p_regexp.parentNode);
if (p_searchable) Element.hide(p_searchable.parentNode);
Element.hide(p_values.parentNode);
Element.hide(p_values);
break;
case "float":
case "int":
Element.show(p_length.parentNode);
Element.show(p_regexp.parentNode);
if (p_searchable) Element.hide(p_searchable.parentNode);
Element.hide(p_values.parentNode);
Element.hide(p_values);
break;
case "user":
case "version":
Element.hide(p_length.parentNode);
Element.hide(p_regexp.parentNode);
if (p_searchable) Element.hide(p_searchable.parentNode);
Element.hide(p_values.parentNode);
Element.hide(p_default.parentNode);
break;
default:
Element.show(p_length.parentNode);
Element.show(p_regexp.parentNode);
if (p_searchable) Element.show(p_searchable.parentNode);
Element.hide(p_values.parentNode);
Element.hide(p_values);
break;
}
}
@@ -62,16 +54,16 @@ function toggle_custom_field_format() {
<div class="box">
<p><%= f.text_field :name, :required => true %></p>
<p><%= f.select :field_format, custom_field_formats_for_select(@custom_field), {}, :onchange => "toggle_custom_field_format();",
<p><%= f.select :field_format, custom_field_formats_for_select, {}, :onchange => "toggle_custom_field_format();",
:disabled => !@custom_field.new_record? %></p>
<p><label for="custom_field_min_length"><%=l(:label_min_max_length)%></label>
<%= f.text_field :min_length, :size => 5, :no_label => true %> -
<%= f.text_field :max_length, :size => 5, :no_label => true %><br>(<%=l(:text_min_max_length_info)%>)</p>
<p><%= f.text_field :regexp, :size => 50 %><br>(<%=l(:text_regexp_info)%>)</p>
<p>
<%= f.text_area :possible_values, :value => @custom_field.possible_values.to_a.join("\n"), :rows => 15 %>
<br /><em><%= l(:text_custom_field_possible_values_info) %></em>
</p>
<p id="custom_field_possible_values"><%= f.text_area :possible_values, :value => @custom_field.possible_values.to_a.join("\n"),
:cols => 20,
:rows => 15 %>
<br /><em><%= l(:text_custom_field_possible_values_info) %></em></p>
<p><%= @custom_field.field_format == 'bool' ? f.check_box(:default_value) : f.text_field(:default_value) %></p>
<%= call_hook(:view_custom_fields_form_upper_box, :custom_field => @custom_field, :form => f) %>
</div>
+20 -11
View File
@@ -1,11 +1,11 @@
<% @gantt.view = self %>
<h2><%= @query.new_record? ? l(:label_gantt) : h(@query.name) %></h2>
<h2><%= l(:label_gantt) %></h2>
<% form_tag({:controller => 'gantts', :action => 'show', :project_id => @project, :month => params[:month], :year => params[:year], :months => params[:months]}, :method => :get, :id => 'query_form') do %>
<%= hidden_field_tag 'set_filter', '1' %>
<fieldset id="filters" class="collapsible <%= @query.new_record? ? "" : "collapsed" %>">
<% form_tag(gantt_path(:month => params[:month], :year => params[:year], :months => params[:months]), :method => :put, :id => 'query_form') do %>
<%= hidden_field_tag('project_id', @project.to_param) if @project%>
<fieldset id="filters" class="collapsible">
<legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
<div style="<%= @query.new_record? ? "" : "display: none;" %>">
<div>
<%= render :partial => 'queries/filters', :locals => {:query => @query} %>
</div>
</fieldset>
@@ -22,8 +22,17 @@
<%= select_year(@gantt.year_from, :prefix => "year", :discard_type => true) %>
<%= hidden_field_tag 'zoom', @gantt.zoom %>
<%= link_to_function l(:button_apply), '$("query_form").submit()', :class => 'icon icon-checked' %>
<%= link_to l(:button_clear), { :project_id => @project, :set_filter => 1 }, :class => 'icon icon-reload' %>
<%= link_to_remote l(:button_apply),
{ :url => { :set_filter => (@query.new_record? ? 1 : nil) },
:update => "content",
:with => "Form.serialize('query_form')"
}, :class => 'icon icon-checked' %>
<%= link_to_remote l(:button_clear),
{ :url => { :project_id => @project, :set_filter => (@query.new_record? ? 1 : nil) },
:method => :put,
:update => "content",
}, :class => 'icon icon-reload' if @query.new_record? %>
</p>
<% end %>
@@ -169,14 +178,14 @@ if Date.today >= @gantt.date_from and Date.today <= @gantt.date_to %>
<table width="100%">
<tr>
<td align="left"><%= link_to_content_update('&#171; ' + l(:label_previous), params.merge(@gantt.params_previous)) %></td>
<td align="right"><%= link_to_content_update(l(:label_next) + ' &#187;', params.merge(@gantt.params_next)) %></td>
<td align="left"><%= link_to_remote ('&#171; ' + l(:label_previous)), {:url => @gantt.params_previous, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'}, {:href => url_for(@gantt.params_previous)} %></td>
<td align="right"><%= link_to_remote (l(:label_next) + ' &#187;'), {:url => @gantt.params_next, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'}, {:href => url_for(@gantt.params_next)} %></td>
</tr>
</table>
<% other_formats_links do |f| %>
<%= f.link_to 'PDF', :url => params.merge(@gantt.params) %>
<%= f.link_to('PNG', :url => params.merge(@gantt.params)) if @gantt.respond_to?('to_image') %>
<%= f.link_to 'PDF', :url => @gantt.params %>
<%= f.link_to('PNG', :url => @gantt.params) if @gantt.respond_to?('to_image') %>
<% end %>
<% end # query.valid? %>
Loaded 100 of 544 files, more files were not shown because too many files have changed in this diff. Show more