Compare commits

..

532 Commits

Author SHA1 Message Date
Jean-Philippe Lang
2a4774627d tagged version 2.1.0
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/tags/2.1.0@10395 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-16 12:47:11 +00:00
Jean-Philippe Lang
5eab4af70d Merged r10391 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10392 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-16 12:43:43 +00:00
Toshi MARUYAMA
96b2e00015 Merged r10379 from trunk (#11836)
Japanese translation updated by Go MAEDA.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10380 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-15 12:02:36 +00:00
Jean-Philippe Lang
9939fbeef6 Merged r10375 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10376 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-12 19:16:31 +00:00
Jean-Philippe Lang
b369f82d15 Merged r10368 to r10373 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10374 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 19:40:59 +00:00
Jean-Philippe Lang
0ad5dfaba0 Set branch to stable.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10367 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 16:33:49 +00:00
Jean-Philippe Lang
d7d18689b7 Added 2.1-stable branch.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.1-stable@10366 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 16:30:57 +00:00
Jean-Philippe Lang
4cd22bb53a Set version to 2.1.0.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10365 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 16:30:07 +00:00
Toshi MARUYAMA
7d8db59dae do not show estimated time if it is nil on issue page
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10364 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 14:13:30 +00:00
Toshi MARUYAMA
d1606fd3e3 add asserting that issue estimated hours is nil when creating minimal issue
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10363 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 14:13:11 +00:00
Toshi MARUYAMA
097d9661c9 gantt: use content_tag instead of html tag at the tooltip
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10362 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 03:45:06 +00:00
Toshi MARUYAMA
f8cedf00a8 gantt: use content_tag instead of html tag at the label on the right
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10361 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 03:44:52 +00:00
Toshi MARUYAMA
2461d12388 gantt: remove redundant empty lines from unit gantt helper test
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10360 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 02:58:59 +00:00
Toshi MARUYAMA
bcd8ce2de2 gantt: use content_tag instead of html tag at subject for project
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10359 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 02:16:32 +00:00
Toshi MARUYAMA
0cd9149e7a gantt: use content_tag instead of html tag at the markers
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10358 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 02:16:18 +00:00
Toshi MARUYAMA
69d0660760 gantt: code layout cleanup to_pdf method of lib/redmine/helpers/gantt.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10357 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 02:16:03 +00:00
Toshi MARUYAMA
3f7a7d9b14 gantt: fix unit gantt helper test fails
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10356 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 01:53:34 +00:00
Toshi MARUYAMA
66f881aad3 gantt: use content_tag instead of html tag at the task bar
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10355 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 00:57:10 +00:00
Toshi MARUYAMA
d3d719c0e7 gantt: code layout cleanup image_task method of lib/redmine/helpers/gantt.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10354 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 00:56:56 +00:00
Toshi MARUYAMA
03aa7037a5 gantt: code layout cleanup to_image method of lib/redmine/helpers/gantt.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10353 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 00:56:41 +00:00
Toshi MARUYAMA
ffb981255c gantt: use content_tag instead of html tag at helper subject_for_version method
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10352 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 00:09:01 +00:00
Toshi MARUYAMA
3bbb2566c8 gantt: code layout cleanup pdf_task method of lib/redmine/helpers/gantt.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10351 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 00:08:46 +00:00
Toshi MARUYAMA
8cfec7ddf5 gantt: code layout cleanup pdf_subject method of lib/redmine/helpers/gantt.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10350 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 00:08:32 +00:00
Toshi MARUYAMA
af177c3a59 gantt: code layout cleanup to_pdf method of lib/redmine/helpers/gantt.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10349 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-11 00:08:16 +00:00
Toshi MARUYAMA
25aeb4c3d3 gantt: use content_tag instead of html tag at subject gravatar
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10348 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-10 22:57:24 +00:00
Toshi MARUYAMA
b75afd49de gantt: code layout cleanup html_subject method of lib/redmine/helpers/gantt.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10347 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-10 22:56:56 +00:00
Toshi MARUYAMA
0cd08ed08c gantt: code layout cleanup line_for_issue method of lib/redmine/helpers/gantt.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10346 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-10 22:56:15 +00:00
Toshi MARUYAMA
ad71bffe34 gantt: code layout cleanup line_for_version method of lib/redmine/helpers/gantt.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10345 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-10 22:55:44 +00:00
Toshi MARUYAMA
a2edef9035 gantt: code layout cleanup render method of lib/redmine/helpers/gantt.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10344 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-10 22:55:13 +00:00
Toshi MARUYAMA
abcfbd9c75 gantt: code layout cleanup params* method of lib/redmine/helpers/gantt.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10343 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-10 11:55:13 +00:00
Toshi MARUYAMA
d0ca8eb703 gantt: code layout cleanup initialize method of lib/redmine/helpers/gantt.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10342 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-10 11:54:49 +00:00
Toshi MARUYAMA
e637c15660 gantt: remove redundant empty lines from lib/redmine/helpers/gantt.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10341 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-10 11:54:24 +00:00
Jean-Philippe Lang
f9d0b4776f Fixed TLS configuration in example (#11034).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10340 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-09 15:24:34 +00:00
Jean-Philippe Lang
3b5fd45fae Log current user on each request.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10339 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-09 14:54:22 +00:00
Jean-Philippe Lang
6b0e8fd82a Deep headings in TOC are too small.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10338 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-09 14:13:25 +00:00
Jean-Philippe Lang
972a991a92 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10337 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-09 11:34:35 +00:00
Jean-Philippe Lang
62d5b4c710 Issues assigned to a locked/closed version are now copied (#11207).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10336 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-09 11:04:55 +00:00
Jean-Philippe Lang
197a14a82e Fixed that the reminder email excludes issues assigned to groups (#11723).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10335 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-09 11:02:13 +00:00
Jean-Philippe Lang
ff86c37ed3 Fixed: Issues associated with a locked version are not copied when copying a project (#11207).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10334 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-09 10:11:49 +00:00
Jean-Philippe Lang
5e9320137b Fixed that target version is lost on project copy for issues that are assigned to a shared version from another project.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10333 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-09 10:01:03 +00:00
Jean-Philippe Lang
18ed4cf373 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10332 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-09 09:51:44 +00:00
Toshi MARUYAMA
8303e7c168 Bulgarian translation updated by Ivan Cenov (#11799)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10331 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-08 09:21:26 +00:00
Jean-Philippe Lang
eb58c62f26 Filling locales (#6965).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10330 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-08 06:18:55 +00:00
Jean-Philippe Lang
b72d40a429 Do not copy subtasks twice when copying an issue and its descendants (#6965).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10329 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-08 06:14:35 +00:00
Jean-Philippe Lang
6a37151b5c Adapt subtasks copy when copying a project (#6965).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10328 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-08 05:56:48 +00:00
Jean-Philippe Lang
5003927f13 Option to copy subtasks when copying issue(s) (#6965).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10327 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-08 05:34:07 +00:00
Toshi MARUYAMA
ffcf1925e3 gantt: code layout cleanup html days headers
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10326 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-08 03:21:55 +00:00
Toshi MARUYAMA
ad4ee1c8d8 gantt: code layout cleanup html weeks headers
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10325 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-08 03:21:17 +00:00
Toshi MARUYAMA
d4fc347a8a gantt: use content_tag instead of html tag at days headers
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10324 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-08 02:47:27 +00:00
Toshi MARUYAMA
c25d175623 gantt: use content_tag instead of html tag at gantt header outline
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10323 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-08 02:00:32 +00:00
Toshi MARUYAMA
47e375b271 gantt: code layout cleanup ruby code at head of show.html.erb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10322 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-08 02:00:17 +00:00
Toshi MARUYAMA
eeedd287ad gantt: code layout cleanup html months headers
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10321 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-08 00:59:42 +00:00
Toshi MARUYAMA
2edb8d597a gantt: use content_tag instead of html tag at gantt subject
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10320 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-08 00:59:08 +00:00
Toshi MARUYAMA
23eae0bf3c gantt: code cleanup html today red line
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10319 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-08 00:58:41 +00:00
Toshi MARUYAMA
822a627c44 gantt: fix code indents of html today red line
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10318 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-08 00:57:57 +00:00
Toshi MARUYAMA
3431b860ad gantt: use content_tag instead of html tag at gantt today red line
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10317 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-07 23:03:40 +00:00
Toshi MARUYAMA
3483938185 gantt: use content_tag instead of html tag at gantt weeks headers
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10316 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-07 23:03:16 +00:00
Toshi MARUYAMA
2af4bb6a86 gantt: use content_tag instead of html tag at gantt months headers
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10315 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-07 14:35:33 +00:00
Toshi MARUYAMA
b9586dd570 gantt: code layout cleanup html gantt days headers
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10314 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-07 14:35:20 +00:00
Toshi MARUYAMA
40df75243b code layout cleanup app/views/gantts/show.html.erb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10313 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-07 11:59:48 +00:00
Toshi MARUYAMA
0486ce9976 code layout cleanup gantt weeks headers
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10312 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-07 11:59:34 +00:00
Toshi MARUYAMA
621b15c923 code layout cleanup gantt months headers
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10311 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-07 11:59:19 +00:00
Toshi MARUYAMA
d1eb07dd8b fix code indent of gantt days headers
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10310 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-07 11:59:05 +00:00
Toshi MARUYAMA
9c5f339efb code layout cleanup app/views/gantts/show.html.erb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10309 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-07 01:36:16 +00:00
Toshi MARUYAMA
bbed770fa4 remove redundant empty lines from app/views/repositories/_revision_graph.html.erb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10308 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-07 01:35:18 +00:00
Jean-Philippe Lang
1f7a944d14 XML output broken with builder 3.0.1.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10306 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-06 22:34:21 +00:00
Jean-Philippe Lang
ef8c393290 Let plugin override mailer views (#11776).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10304 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-06 17:34:14 +00:00
Jean-Philippe Lang
bcb4c2cde4 Parse any heading level (#11789).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10294 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-06 17:03:13 +00:00
Toshi MARUYAMA
3dfef8a00d remove redundant empty lines from revision_graph.js
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10293 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-06 06:46:28 +00:00
Toshi MARUYAMA
ee2481a3b9 replace tab to space at revision_graph.js
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10292 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-06 06:46:13 +00:00
Toshi MARUYAMA
99913e80f0 show JQuery datepicker today button
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10291 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-05 17:22:08 +00:00
Toshi MARUYAMA
48ac467981 Traditional Chinese translation updated by ChunChang Lo (#11766)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10290 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-04 09:48:44 +00:00
Toshi MARUYAMA
6e21342c60 fix redmine_plugin generator output in USAGE
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10287 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-03 17:14:42 +00:00
Toshi MARUYAMA
3f63b5ea93 fix plugin generator script name of USAGE on Rails3
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10286 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-03 17:14:28 +00:00
Jean-Philippe Lang
ca7498c2d6 Create role by copy (#9258).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10285 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-03 17:04:28 +00:00
Jean-Philippe Lang
1b64e4be5e Set radio button when selecting a date with the date picker.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10284 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-02 17:24:29 +00:00
Jean-Philippe Lang
3a32edc3bd Adds a view for editing all trackers fields.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10283 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-02 16:55:16 +00:00
Jean-Philippe Lang
462c986452 Moved link to the contextual menu.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10282 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-02 15:13:40 +00:00
Jean-Philippe Lang
c3170e9264 Redmine.pm: Allow fallback to other Apache auth providers (#11475).
Contributed by Yasin Al Farhad.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10281 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-02 14:51:56 +00:00
Jean-Philippe Lang
2a9c001625 Doc update.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10280 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-02 13:00:19 +00:00
Jean-Philippe Lang
94db7c0029 Redmine.pm: HEAD is not considered as a read-only method (#11749).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10279 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-02 12:59:27 +00:00
Toshi MARUYAMA
e1492f771c remove redundant empty lines from test/test_helper.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10278 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-02 11:09:56 +00:00
Toshi MARUYAMA
48b9b805b2 fix typo "MACROS_SUB_RE" (#11736)
Contributed by Anton Argirov.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10277 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-02 11:09:43 +00:00
Jean-Philippe Lang
8b72710d7e Fixed: New multi-line macros regexp is too eager (#11736).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10276 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-02 10:24:56 +00:00
Jean-Philippe Lang
265baa1b2c Upgrade to Rails 3.2.8 (#11758).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10275 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-02 10:08:51 +00:00
Toshi MARUYAMA
139ddd639f backout r10247
Gemfile: pin i18n gem version 0.6.0.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10265 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-01 05:33:58 +00:00
Toshi MARUYAMA
e4b7b7cc38 code cleanup test_with_a_start_date_attribute_* of unit IssuesHelperTest
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10264 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-01 05:33:43 +00:00
Toshi MARUYAMA
8eb39d682d replace shoulder "with a due_date attribute" context of unit IssuesHelperTest and use with_settings
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10263 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-01 05:33:28 +00:00
Toshi MARUYAMA
4bca584024 use date_format setting at unit IssuesHelperTest test_with_a_start_date_attribute_*
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10257 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-01 04:10:33 +00:00
Toshi MARUYAMA
935d8e594b add missing fixtures to test/unit/lib/redmine/hook_test.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10256 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-01 03:47:20 +00:00
Toshi MARUYAMA
374a0cc7cb add missing fixture to test/unit/lib/redmine/safe_attributes_test.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10255 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-01 02:55:13 +00:00
Toshi MARUYAMA
53dd8dc11a add unit query test of French label
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10254 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-01 02:37:20 +00:00
Toshi MARUYAMA
444987ce91 replace shoulder "with a start_date attribute" context of test/unit/helpers/issues_helper_test.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10253 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-01 02:23:59 +00:00
Toshi MARUYAMA
43add0d306 scm: git: use with_settings at test_diff_truncated of functional test (#11752)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10252 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-01 02:23:44 +00:00
Toshi MARUYAMA
3581782ec2 scm: git: use diff_max_lines_displayed setting at test_diff_with_rev_and_path of functional test (#11752)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10251 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-01 02:23:29 +00:00
Toshi MARUYAMA
8d4c0e5ecd set language en to test_label_for at test/unit/query_test.rb
Test on CI Server fails.

http://www.redmine.org/builds/logs/build_trunk_sqlite3_ruby-1.9.3-p194_581.html

<pre>
Failure:
  <"Assignee">("US-ASCII") expected but was
  <"Assigné à">("UTF-8").
test_label_for(QueryTest)
test/unit/query_test.rb:860:in `test_label_for'
</pre>

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10249 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-09-01 01:29:48 +00:00
Toshi MARUYAMA
9a57cc18e3 Gemfile: pin i18n gem version 0.6.0
Tests on CI server fails.
http://www.redmine.org/builds/logs/build_trunk_sqlite3_ruby-1.9.3-p194_580.html

<pre>
Failure:
  <"Due date changed from 01 01 2010 to 31 01 2010"> expected to be =~
  </01\/31\/2010/>.
test: IssuesHelper#show_detail with a due_date attribute should format the current date. (IssuesHelperTest)
test/unit/helpers/issues_helper_test.rb:127:in `block (3 levels) in <class:IssuesHelperTest>'
</pre>

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10247 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-31 23:44:35 +00:00
Toshi MARUYAMA
564de36c08 remove trailing white-space from app/controllers/wiki_controller.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10246 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-31 23:01:51 +00:00
Toshi MARUYAMA
6ea1bc725a scm: git: add functional test of diff with revision and path (#11752)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10245 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-31 22:22:26 +00:00
Jean-Philippe Lang
a7be337a4c Updates test for r10243.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10244 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-27 11:21:08 +00:00
Jean-Philippe Lang
3a77d543fd Code cleanup: removed the obsolete with_limit scope.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10243 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-27 10:54:01 +00:00
Jean-Philippe Lang
d33fa1f8c8 Do not build a projects_by_role Hash that gets updated when accessing a key that is not present (#11662).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10242 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-27 10:11:20 +00:00
Jean-Philippe Lang
50506ef621 Make sure we don't build an empty IN () statement (#11662).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10241 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-27 10:09:08 +00:00
Jean-Philippe Lang
c68ee7f545 Fixed that destroying a user from the edit page returns a 404 response (#11691).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10240 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-26 10:50:59 +00:00
Jean-Philippe Lang
ebc979e9b1 Do not use escaped back_url param (#11691).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10239 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-26 10:40:09 +00:00
Jean-Philippe Lang
3cc6d5e815 Reverted r10234 and r10235 that broke redirect after login (#11691).
Tests in account_controller_test.rb should not have been changed.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10238 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-26 09:29:48 +00:00
Jean-Philippe Lang
a2e59cc956 Don't use tag helper in layout (#11704).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10237 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-26 09:25:25 +00:00
Jean-Philippe Lang
5969df8142 MailHandler: Match assignee on the full display name (#11552).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10236 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-26 09:03:17 +00:00
Etienne Massip
2ed4364c8a Reverted changes made to tests for r1893 (#11691).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10235 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-24 21:41:52 +00:00
Etienne Massip
70226f1833 Use a back_url parameter instead of referrer to refresh the page after user deletion (#11691).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10234 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-24 21:23:10 +00:00
Toshi MARUYAMA
a1d8cab6c6 remove empty setup method from unit enumeration test
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10232 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-22 16:31:26 +00:00
Toshi MARUYAMA
3478a21c2f add unit test that enumeration returns default in regardless of active or not
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10231 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-22 16:31:13 +00:00
Toshi MARUYAMA
44c15c7560 add check default document category is not defined on database in tests (#11665)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10230 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-22 16:18:01 +00:00
Toshi MARUYAMA
d1a52fd301 use Rails3 Active Record syntax in DocumentCategory model
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10228 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-22 00:13:31 +00:00
Toshi MARUYAMA
377ef084ad add position to DocumentCategory in enumerations fixture (#11665)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10222 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-21 16:14:33 +00:00
Toshi MARUYAMA
665a331cc4 force set default document category if it is not set on database (#11665)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10221 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-21 13:45:53 +00:00
Toshi MARUYAMA
185ab2a020 add unit test to get default document category defined on database (#11665)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10220 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-21 13:45:40 +00:00
Toshi MARUYAMA
02fb4c3b2c add functional test to create non default document category (#11665)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10219 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-21 06:15:29 +00:00
Toshi MARUYAMA
8376311ee1 add missing fixtures to test/functional/documents_controller_test.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10216 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-21 04:49:22 +00:00
Toshi MARUYAMA
ac5e3c2036 add missing fixtures to test/unit/document_test.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10213 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-21 02:15:04 +00:00
Jean-Philippe Lang
a5c4fcc8f0 Updated macro documentation.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10212 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-18 12:55:00 +00:00
Jean-Philippe Lang
29d54f5d50 Validate macro name and options.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10211 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-18 12:35:14 +00:00
Jean-Philippe Lang
fc3a09e49a Let macros optionally accept a block of text (#3061).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10210 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-18 08:25:22 +00:00
Jean-Philippe Lang
73aece0baf Macros processing overhaul (#3061, #11633).
* macro arguments are no longer parsed by text formatters
* macro output is escaped unless it's html safe

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10209 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-17 14:46:55 +00:00
Jean-Philippe Lang
af5a814f4c Prevents "Overwriting existing method Issue.open" warning (#11545).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10208 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-17 14:34:10 +00:00
Toshi MARUYAMA
91a09bd474 fix unit test fails on JRuby 1.6.7.2 (#11577)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10207 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-14 00:30:23 +00:00
Jean-Philippe Lang
3673fbd881 Fixed: Can't use non-latin anchor in wiki (#11577).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10206 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-13 19:36:00 +00:00
Jean-Philippe Lang
d79bcc4369 Fixed that text email templates are escaped (#11355).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10205 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-13 19:05:42 +00:00
Jean-Philippe Lang
327660eb7f Respond with 404 on ActionView::MissingTemplate (#11503).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10204 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-13 18:34:00 +00:00
Jean-Philippe Lang
49e80c2cea Adds .settings to svn:ignore and removes svk property.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10203 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-13 18:16:45 +00:00
Jean-Philippe Lang
3119d1996c Use abbr_day_name to get the first letter of the day name (#9839).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10202 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-13 18:13:10 +00:00
Jean-Philippe Lang
11d4d8177c Escape filter values using .text instead of .html.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10201 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-13 14:59:59 +00:00
Jean-Philippe Lang
861ca78179 Fixed that roadmap anchor links can be ambigous (#11540).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10200 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-13 14:41:53 +00:00
Jean-Philippe Lang
8fb1a7e3cc Fixed that open scope on Project#issues raises an error (#11545).
Patch by Petr Pospisil.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10199 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-13 14:23:33 +00:00
Jean-Philippe Lang
91c875437d Adds a title to the issue link in the flash message.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10198 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-13 14:04:00 +00:00
Jean-Philippe Lang
60574bfb9c Context menu style.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10197 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-13 13:48:20 +00:00
Jean-Philippe Lang
3e4cbec95e Custom JQueryUI theme.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10196 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-13 13:39:21 +00:00
Jean-Philippe Lang
dbf41dba68 Set application/javascript as javascript mime tpye (#11621).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10195 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-13 12:39:11 +00:00
Toshi MARUYAMA
163659d0a5 cherry-pick avatar test from reverted r10184
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10194 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-11 00:31:18 +00:00
Jean-Philippe Lang
ec02853141 Raised the max-height.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10193 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-10 18:49:30 +00:00
Jean-Philippe Lang
2118f3a1fd Adds a max-height to projects lists.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10192 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-10 18:48:53 +00:00
Jean-Philippe Lang
145e5997b2 Removed duplicated helper (#11539).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10191 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-10 18:41:24 +00:00
Jean-Philippe Lang
32fcdff69f Removed p around ul.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10190 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-10 18:39:49 +00:00
Jean-Philippe Lang
c0c491dd61 Display a projects tree instead of a flat list in notification preferences (#11539).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10189 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-10 17:30:35 +00:00
Jean-Philippe Lang
0a6c1d9c13 Extract code to render nested listed of projects in an helper (#11539).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10188 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-10 17:09:48 +00:00
Jean-Philippe Lang
e52219f09d Fixed that projects are not ordered alphabetically after renaming project (#11508).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10187 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-10 16:50:15 +00:00
Jean-Philippe Lang
02b2a61e15 Reverted r10184 (#9365).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10186 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-10 16:35:58 +00:00
Jean-Philippe Lang
8b12702ebe Fixed that search results are escaped twice.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10185 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-10 16:22:26 +00:00
Jean-Philippe Lang
cbfafcd5e2 Use SSL for gravatars according to the protocol in settings (#9365).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10184 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-09 18:04:54 +00:00
Jean-Philippe Lang
e06bf0c464 Don't show the project dropdown when logging time on an issue.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10183 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-09 17:12:24 +00:00
Jean-Philippe Lang
f1650b8ff4 Adds autocomplete to issue field on time logging form.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10182 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-09 16:58:36 +00:00
Jean-Philippe Lang
f82a7a35b0 Adds autocomplete to "Related issue" field on revision (#11102).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10181 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-09 16:46:13 +00:00
Jean-Philippe Lang
908b960e71 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10180 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-09 16:43:12 +00:00
Jean-Philippe Lang
0ce0b52342 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10179 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-08 21:28:07 +00:00
Jean-Philippe Lang
b907398788 Slight change to the macro regexp.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10178 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-08 18:54:01 +00:00
Jean-Philippe Lang
2e4f3d5a60 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10177 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-08 18:47:52 +00:00
Jean-Philippe Lang
e0faf5cf84 Headings style.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10176 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-08 16:50:06 +00:00
Jean-Philippe Lang
9553325ee3 Use the accessor.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10175 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-08 16:35:25 +00:00
Jean-Philippe Lang
e2d6f0af4e Adds an option to macro definition to disable arguments parsing (#11578).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10174 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-08 16:28:33 +00:00
Toshi MARUYAMA
405bcc10c0 fix plural form of the abbreviation for hours in Brazilian Portuguese (#11600)
Contributed by Mauricio Piacentini.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10169 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-08 00:00:14 +00:00
Toshi MARUYAMA
2e536a2c56 Gemfile: mocha version up 0.12.3
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10167 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-07 23:10:50 +00:00
Jean-Philippe Lang
b6be9bff35 Log successful authentications.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10166 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-07 19:50:18 +00:00
Jean-Philippe Lang
b418e27283 Filling locales.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10165 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-07 19:24:03 +00:00
Jean-Philippe Lang
3676783052 Ability to filter issues using project, author, assignee and target version custom fields (#8161).
Custom fields must be marked as "Used as filter" to show up in the filters list.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10164 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-07 19:17:59 +00:00
Jean-Philippe Lang
599736aca7 Update the new custom field form with remotely.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10163 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-07 16:58:51 +00:00
Jean-Philippe Lang
1749fbf3e6 Build issue filters using javascript.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10162 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-07 16:41:46 +00:00
Jean-Philippe Lang
e9de6c1415 Removed csshover for IE6.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10161 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-07 16:16:19 +00:00
Jean-Philippe Lang
962134ab4f Prevent "Overwriting existing method User.active" warning, scope is already defined on the base class (#11545).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10160 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-07 16:14:24 +00:00
Jean-Philippe Lang
2e35a6800f Prevents "Overwriting existing method Issue.open" warning (#11545).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10159 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-07 16:13:04 +00:00
Jean-Philippe Lang
0a5828ee4a Added SVG mime-type (#11595).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10158 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-06 21:18:24 +00:00
Jean-Philippe Lang
1949f61d0c Fixed: Custom fields of type version not proper handled in receiving e-mails (#11571).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10157 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-06 21:16:19 +00:00
Jean-Philippe Lang
3f1c35b71e Tests not running after upgrade to mocha 0.12.2.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10155 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-02 17:37:03 +00:00
Jean-Philippe Lang
1a38434d5f Error running tests with ruby 1.9.3p194 (2012-04-20) [i386-mingw32].
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10154 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-02 17:20:36 +00:00
Jean-Philippe Lang
f8183429a9 Adjust the comment.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10153 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-02 16:48:23 +00:00
Jean-Philippe Lang
2275f46138 Makes Version#<=> consistent with SQL sort.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10152 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-02 16:47:53 +00:00
Jean-Philippe Lang
34cdac1669 Sort issues by scheduled versions first then unscheduled versions.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10151 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-02 16:47:06 +00:00
Toshi MARUYAMA
c7b712067e add translator name to Bulgarian yaml (#11561)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10150 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-08-01 12:59:00 +00:00
Toshi MARUYAMA
6ca1c117ab Bulgarian translation updated by Ivan Cenov (#11556)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10149 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-31 23:04:09 +00:00
Jean-Philippe Lang
b603aa74f9 Adds sharing attribute to the versions API (#11541).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10148 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-31 18:14:28 +00:00
Jean-Philippe Lang
c57a2974d1 Fixed that Mailer.with_synched_deliveries does not use delivery settings (#11550).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10147 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-31 18:09:33 +00:00
Jean-Philippe Lang
77d94818a4 Removed AuthSourceLdap.human_attribute_name.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10146 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-31 17:30:01 +00:00
Jean-Philippe Lang
a53894dd22 Set @previewed for rendering preview partial.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10145 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-31 17:22:17 +00:00
Jean-Philippe Lang
d99dbe9f0c Eager loading and no extra count query.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10144 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-31 17:21:14 +00:00
Jean-Philippe Lang
3f88fd44ef Filling locales.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10143 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-31 17:18:35 +00:00
Jean-Philippe Lang
bc153cb61d Support for subforums (#3831).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10142 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-31 17:17:52 +00:00
Jean-Philippe Lang
9554f0133f New edit icon.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10141 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-31 16:40:05 +00:00
Toshi MARUYAMA
f50da1593e Gemfile: prevent "rake db:migrate RAILS_ENV=test" causes exception on Ruby 1.9.3 (#10320, #10818)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10137 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-31 02:32:40 +00:00
Toshi MARUYAMA
96306f9dff replace tabs to spaces at app/views/repositories/_dir_list_content.html.erb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10136 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-30 01:32:31 +00:00
Jean-Baptiste Barth
8f0947e578 Fixed rake redmine:plugins:test:* not running some tests in subdirectories (#11533)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10135 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 18:27:13 +00:00
Jean-Baptiste Barth
d048d86c50 Invoke db:schema:dump after plugin migrations so that db/schema.rb stays up-to-date
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10134 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 18:01:13 +00:00
Jean-Philippe Lang
95457c434e Stylesheet cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10133 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 15:38:32 +00:00
Jean-Philippe Lang
42570c4426 Slight changes to syntax highlightment styles.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10132 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 15:30:47 +00:00
Jean-Philippe Lang
e4518af32a Removed line numbers from syntax highlightment.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10131 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 14:52:57 +00:00
Jean-Philippe Lang
08f200c487 Fixed legend tag position.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10130 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 12:13:47 +00:00
Jean-Philippe Lang
64f66daedd Duplicate ul tags.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10129 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 12:12:32 +00:00
Jean-Philippe Lang
0c72347d61 Fixed label with invalid for attribute.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10128 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 12:10:34 +00:00
Jean-Philippe Lang
15341bd844 Make labels clickable in Adminstration/Settings (#11496).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10127 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 12:08:10 +00:00
Jean-Philippe Lang
7a4c6d2a19 Add labels on SCMs.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10126 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 12:04:49 +00:00
Jean-Philippe Lang
1e6938a119 Removed duplicate ids.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10125 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 12:04:11 +00:00
Jean-Philippe Lang
9ef719f15e Add links to forums.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10124 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 11:52:50 +00:00
Jean-Philippe Lang
2175e4a901 Add links to repositories on repository list.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10123 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 11:51:09 +00:00
Jean-Philippe Lang
3692369584 html5 compliance.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10122 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 11:34:00 +00:00
Jean-Philippe Lang
75b0c01431 Typo.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10121 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 11:33:47 +00:00
Jean-Philippe Lang
d252848b2e Removed inline styles.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10120 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 11:32:24 +00:00
Jean-Philippe Lang
3a7207672e html5 compliance.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10119 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 11:30:43 +00:00
Jean-Philippe Lang
f8de723c46 Removed inline styles.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10118 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 11:28:22 +00:00
Jean-Philippe Lang
a0b2ab338e Removed inline styles.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10117 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 11:23:59 +00:00
Jean-Philippe Lang
308010c7c9 Fixed assertion.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10116 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 11:17:36 +00:00
Jean-Philippe Lang
6918a8974c Removed self closing a tags.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10115 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 11:16:19 +00:00
Jean-Philippe Lang
ba9f1c0388 html5 compliance.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10114 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 10:53:02 +00:00
Jean-Philippe Lang
45d9763090 Removed invalid html5 meta (Rails sets an equivalent response header natively) (#10128).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10113 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 10:46:04 +00:00
Jean-Philippe Lang
62d5b359c0 html5 compliance.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10112 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 10:21:27 +00:00
Jean-Philippe Lang
33d6f9e7a1 Label should be escaped.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10111 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 10:15:20 +00:00
Jean-Philippe Lang
c6106543e1 html5 compliance.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10110 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 10:12:26 +00:00
Jean-Philippe Lang
8ee2ae1846 html5 compliance.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10109 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-29 10:06:41 +00:00
Jean-Philippe Lang
a7caf3faf0 Use assert_select instead of assert_tag.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10108 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-28 18:41:08 +00:00
Jean-Philippe Lang
cc79feabe2 Adds assertions for subversion annotate.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10107 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-28 18:36:03 +00:00
Jean-Philippe Lang
71c5d6c8ee Use assert_select instead of assert_tag.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10106 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-28 18:16:55 +00:00
Jean-Philippe Lang
65524cc1cc Don't repeat revision on annotate view.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10105 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-28 18:11:03 +00:00
Jean-Philippe Lang
138f6736b2 Restores bottom border on h4.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10104 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-28 17:05:42 +00:00
Jean-Philippe Lang
0880096162 Same heading on message edit/show views.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10103 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-28 17:00:45 +00:00
Jean-Philippe Lang
571d316b13 Removed bottom border on headings.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10102 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-28 16:58:02 +00:00
Jean-Philippe Lang
93d9d459c5 Fixed broken assertion.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10101 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-28 16:54:21 +00:00
Jean-Philippe Lang
c544e9b9c6 Lighter blue for the header.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10100 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-28 16:46:26 +00:00
Jean-Philippe Lang
5565a078f3 Do not scroll to the preview.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10099 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-28 16:42:10 +00:00
Jean-Philippe Lang
7306c733bf HTML5 doctype.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10098 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-28 16:38:39 +00:00
Jean-Philippe Lang
969a5be7da New icons for the repository browser.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10097 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-28 16:25:02 +00:00
Jean-Philippe Lang
6851b0ca81 Lighter blue for links.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10096 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-28 16:07:51 +00:00
Jean-Philippe Lang
a7a0d47f2e Merged and minified jstoolbar.js and textile.js.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10095 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-28 15:58:54 +00:00
Jean-Philippe Lang
93a65c3c82 Moved syntax highlight styles to application.css.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10094 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-28 15:54:23 +00:00
Jean-Philippe Lang
1a088fb31e Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10093 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-28 15:45:36 +00:00
Jean-Philippe Lang
6e7507d580 Adds a replacement for deprecated link_to_function helper.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10092 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-28 15:26:45 +00:00
Jean-Philippe Lang
42fdc1d54d Fixed API key display on my account.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10091 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-28 15:14:56 +00:00
Jean-Philippe Lang
afc8239bf7 Fixed apply link on gantt.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10090 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-28 15:06:53 +00:00
Jean-Philippe Lang
8088749f10 Fixed tests broken by r10088.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10089 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-28 14:59:36 +00:00
Jean-Philippe Lang
3c3bdb8bb0 Adds JSONP support to the API (#11469).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10088 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-27 19:56:49 +00:00
Jean-Philippe Lang
9b4d29dc0d Do not propose non-assignable version (#11506).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10087 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-27 19:41:36 +00:00
Jean-Philippe Lang
9eb041fbc7 Versions that are not shared should not be assignable when selecting another project (#11506).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10086 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-27 19:36:53 +00:00
Jean-Philippe Lang
ed165f6716 Do not user user session for API requests.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10085 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-27 18:24:42 +00:00
Toshi MARUYAMA
8ed4620bb9 fix confirmation page has broken HTML when a project folding sub project is deleted (#11511)
Contributed by Haruka Yoshihara.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10083 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-26 23:45:17 +00:00
Jean-Philippe Lang
8e1f7607ec Identifier is now frozen.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10082 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-26 18:41:44 +00:00
Jean-Philippe Lang
87f284dcb6 Repository Identifier should be frozen (#11109).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10081 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-26 17:41:47 +00:00
Jean-Philippe Lang
04f9a321b1 Adds a task to duplicate a string in locales.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10080 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-26 17:32:22 +00:00
Jean-Philippe Lang
f03e21fb45 Changed issues css class from priority-{position} to status-{id} (#2071).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10079 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-26 16:55:40 +00:00
Jean-Philippe Lang
88efd302a7 Changed issues css class from status-{position} to status-{id} (#11304).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10078 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-26 16:50:39 +00:00
Jean-Philippe Lang
28cdc8adfc Don't turn #nnn with leading zeros into link (#11494).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10077 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-24 21:51:10 +00:00
Jean-Philippe Lang
3209c4c5e4 Sort versions ascending when grouping issues by version (#11153).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10076 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-24 18:41:04 +00:00
Jean-Philippe Lang
8fa787719a Fixed broken test.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10075 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-24 18:39:14 +00:00
Jean-Philippe Lang
bdd29295b4 In case the column is not found.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10074 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-24 17:42:45 +00:00
Jean-Philippe Lang
faab8678d4 Ability to group and sort the issue list by user/version custom field (#9419).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10073 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-24 17:39:30 +00:00
Jean-Philippe Lang
44fcc8919d Adds Version.fields_for_order_statement.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10072 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-24 16:32:39 +00:00
Jean-Philippe Lang
0b31c8ac85 Adds CustomField#group_statement.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10071 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-24 16:28:34 +00:00
Jean-Philippe Lang
46b1a49453 Use JQuery Dialog (#11445).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10070 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-22 15:42:29 +00:00
Jean-Philippe Lang
cd54e15a8d Use JQuery Datepicker (#11445).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10069 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-22 14:43:19 +00:00
Jean-Philippe Lang
6a2ca5e034 JQuery in, Prototype/Scriptaculous out (#11445).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10068 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-22 13:29:26 +00:00
Jean-Philippe Lang
387836f8aa Fixed that settings raises an error if not trackers exist (#11467).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10067 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-21 15:43:44 +00:00
Toshi MARUYAMA
1f84a8d83e code layout cleanup lib/redmine/helpers/gantt.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10066 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-21 13:03:29 +00:00
Toshi MARUYAMA
dc393cc480 replace tabs to spaces and fix indents at lib/redmine/helpers/gantt.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10065 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-21 12:53:06 +00:00
Toshi MARUYAMA
d41d493128 code layout cleanup app/views/gantts/show.html.erb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10064 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-21 11:00:59 +00:00
Toshi MARUYAMA
dadd294a25 code layout cleanup test/unit/lib/redmine/helpers/gantt_test.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10063 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-21 08:08:06 +00:00
Toshi MARUYAMA
bd85428b01 add copyright statement to test/functional/gantts_controller_test.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10062 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-21 07:12:18 +00:00
Toshi MARUYAMA
eb62603144 replace shoulda to Rails standard tests at functional gantts controller test
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10061 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-21 07:03:36 +00:00
Jean-Philippe Lang
387f09d4f4 Removed -moz-border-radius styles.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10060 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-21 06:49:18 +00:00
Jean-Philippe Lang
47e496f049 Removes RJS from UsersController.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10059 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-21 06:35:49 +00:00
Jean-Philippe Lang
d3bfbb800c Removes RJS from MembersController.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10058 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-21 06:35:11 +00:00
Toshi MARUYAMA
9c3045eeda Russian translation for 1.4-stable and 2.0-stable updated by Александр Закревский (#11448)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10055 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-20 09:31:39 +00:00
Jean-Philippe Lang
3eaa998c28 Removes RJS from JournalsController.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10054 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-19 19:10:18 +00:00
Jean-Philippe Lang
e8469e2c5b Removes RJS from MessagesController.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10053 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-19 18:23:45 +00:00
Jean-Philippe Lang
3386008491 Removes RJS from VersionsController.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10052 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-19 18:13:21 +00:00
Jean-Philippe Lang
dee586e9eb Removes RJS from WikisController.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10051 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-19 18:13:08 +00:00
Jean-Philippe Lang
5b6732cfaf Removes RJS from IssuesController.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10050 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-19 17:54:26 +00:00
Jean-Philippe Lang
b53f9c688b Fixed migration broken by r9977.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10049 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-19 17:34:37 +00:00
Jean-Philippe Lang
87da04c808 Removes RJS from RepositoriesController.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10048 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-19 17:27:14 +00:00
Jean-Philippe Lang
38060a2cf0 Removes RJS from WatchersController.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10047 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-19 17:17:10 +00:00
Jean-Philippe Lang
c178aded61 Test cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10046 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-19 17:07:43 +00:00
Jean-Philippe Lang
8553107016 Removes RJS from IssueRelationsController.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10045 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-19 17:06:30 +00:00
Jean-Philippe Lang
21ee2e2cf2 Removes RJS from GroupsController.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10044 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-19 16:57:59 +00:00
Jean-Philippe Lang
035805fbd0 Start removing the RJS stuff.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10043 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-19 16:43:33 +00:00
Jean-Philippe Lang
51d5a52029 Do not let is_private be required.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10042 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-19 16:21:28 +00:00
Jean-Philippe Lang
6668d7ebd1 Removed deprecated #labelled_tabular_form_for helper.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10041 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-18 19:14:35 +00:00
Jean-Philippe Lang
1c8d03c1e9 ApplicationHelper#labelled_remote_form_for deprecated.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10040 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-18 19:13:30 +00:00
Jean-Philippe Lang
c790f1ca41 ApplicationHelper#link_to_remote_if_authorized deprecated.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10039 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-18 19:10:08 +00:00
Jean-Philippe Lang
d4f7b4af6d Removes calls to link_to_remote.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10038 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-18 19:09:52 +00:00
Jean-Philippe Lang
690b0fb08c Removes calls to link_to_remote.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10037 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-18 18:54:19 +00:00
Jean-Philippe Lang
cc553764ba Removes a call to form_remote_tag.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10036 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-18 18:48:39 +00:00
Jean-Philippe Lang
705f96ccde Removes calls to remote_form_for.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10035 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-18 18:43:13 +00:00
Jean-Philippe Lang
cd39b12f2c Removes a call to form_remote_tag.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10034 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-18 18:35:25 +00:00
Jean-Philippe Lang
7e5bad993d Removes most of the ajax stuff on my page layout.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10033 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-18 18:26:10 +00:00
Jean-Philippe Lang
c9a46950de Removes calls to link_to_remote.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10032 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-18 18:01:14 +00:00
Jean-Philippe Lang
ff68fff80e Adds a helper for preview links.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10031 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-18 17:47:19 +00:00
Jean-Philippe Lang
bb2af97dcf Removes calls to link_to_remote.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10030 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-18 17:34:11 +00:00
Jean-Philippe Lang
ccc5f64a99 Removes calls to link_to_remote.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10029 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-18 16:50:10 +00:00
Jean-Philippe Lang
6f3c339e5e Removes a call to link_to_remote.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10028 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-18 16:47:30 +00:00
Jean-Philippe Lang
b0757bbdf6 Removes a call to link_to_remote.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10027 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-18 16:36:08 +00:00
Jean-Philippe Lang
a49d50614f Removes a call to link_to_remote.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10026 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-18 16:31:36 +00:00
Jean-Philippe Lang
7b3aaca83c Removes a call to link_to_remote.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10025 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-18 16:28:07 +00:00
Jean-Philippe Lang
922f1d5243 Removes a call to link_to_remote.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10024 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-18 16:24:23 +00:00
Toshi MARUYAMA
ee3478f389 Traditional Chinese translation updated by ChunChang Lo (#11436)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10023 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-18 03:50:31 +00:00
Toshi MARUYAMA
07546b4943 remove trailing white-space from app/models/changeset.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10022 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-17 23:55:32 +00:00
Toshi MARUYAMA
2b642c0662 Bulgarian translation updated by Ivan Cenov (#11433)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10021 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-17 22:50:45 +00:00
Toshi MARUYAMA
3e3714c2d6 Japanese translation updated by Go MAEDA (#11427)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10020 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-17 14:47:31 +00:00
Toshi MARUYAMA
b5a4446f18 fix disordered use of long sound in Japanese "user" translation (#11411)
Contributed by Kenichi Maehashi.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10019 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-17 05:25:46 +00:00
Toshi MARUYAMA
4fdbeea686 Simplified Chinese translation updated by Steven Wong (#11417)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10018 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-17 04:59:06 +00:00
Toshi MARUYAMA
bbb14cb57c fix wrong Japanese "label_attachment" translation (#11419)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10017 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-17 04:58:36 +00:00
Toshi MARUYAMA
07ce80be2d fix unnatural Japanese message when users failed to login (#11412)
Contributed by Kenichi Maehashi.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10016 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-17 04:58:04 +00:00
Jean-Philippe Lang
c3c7f1f900 Removed debug message.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10015 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-16 17:56:08 +00:00
Jean-Philippe Lang
fe95692f10 Test if convert binary is available.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10014 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-16 17:37:24 +00:00
Jean-Philippe Lang
537be80be2 Adds a macro for inserting thumbnails in formatted text (#3510).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10013 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-16 17:15:40 +00:00
Jean-Philippe Lang
5c2de4dfc9 Adds Attachment#title.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10012 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-16 16:42:23 +00:00
Jean-Philippe Lang
570e1b1d62 Removed assertion that is susceptible to fail if test runs slowly.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10011 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-16 16:35:21 +00:00
Jean-Philippe Lang
7f1fb7ce81 Don't show "Required" option for standard/custom fields that are always required.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10010 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-16 16:33:27 +00:00
Toshi MARUYAMA
5ac4b6670f Japanese translation added for default role names by Kenichi Maehashi (#11402)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10009 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-16 14:47:04 +00:00
Toshi MARUYAMA
898873a22d Fix Japanese mistranslation for "button_submit" (#11401)
Contributed by Kenichi Maehashi.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10007 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-16 13:16:28 +00:00
Toshi MARUYAMA
74ee5c078f German translation updated by Hannes Meier (#11404)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10006 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-16 13:15:59 +00:00
Toshi MARUYAMA
185175c78e German translation for configurable session lifetime and timeout updated by Hannes Meier (#11406, #11404)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10005 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-16 13:15:27 +00:00
Jean-Philippe Lang
ba647689f4 Additional tests for custom field formats.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10004 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 19:30:47 +00:00
Jean-Philippe Lang
471f631dbd Use named routes.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10003 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 19:14:23 +00:00
Jean-Philippe Lang
54d2b07bff Test for when group does not exist.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10002 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 19:05:34 +00:00
Jean-Philippe Lang
e23511076c No blank option for custom fields marked as required on workflow settings.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10001 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 16:57:04 +00:00
Jean-Philippe Lang
072475b8a5 Remember "Only display used statuses" checkbox value after update.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9997 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 15:50:57 +00:00
Jean-Philippe Lang
071cf55fc8 Hard-coded strings.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9996 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 15:46:53 +00:00
Jean-Philippe Lang
a7bdc5e0af Adds option for displaying all statuses on workflow permissions.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9995 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 15:46:14 +00:00
Jean-Philippe Lang
712e5be29f Find role and tracker only when param is present.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9994 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 15:39:36 +00:00
Jean-Philippe Lang
5097f51784 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9993 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 15:37:49 +00:00
Jean-Philippe Lang
8a3126e175 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9992 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 15:36:45 +00:00
Jean-Philippe Lang
2f55caa1ba Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9991 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 15:35:02 +00:00
Jean-Philippe Lang
4c8c5e9187 Display all fields if tracker statuses are not yet set.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9990 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 15:34:44 +00:00
Jean-Philippe Lang
7946f4a696 Adds a sorted scope to IssueStatus model.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9989 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 15:33:17 +00:00
Jean-Philippe Lang
cb09055d60 Colorize transitions.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9988 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 15:25:26 +00:00
Jean-Philippe Lang
9e06942ef0 Moved code for replacing permissions to WorkflowPermission model.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9987 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 15:05:34 +00:00
Jean-Philippe Lang
02e7025685 Adds tmp/thumbnails directory.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9986 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 14:56:38 +00:00
Jean-Philippe Lang
88e1ae892c ruby1.8 compatibility (#703, #3521).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9985 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 14:50:38 +00:00
Jean-Philippe Lang
7f0bb136ad Validate attachment description length (#11365).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9984 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 14:42:00 +00:00
Jean-Philippe Lang
391d9b14fb Rewrites assertions with assert_select.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9983 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 14:36:13 +00:00
Jean-Philippe Lang
e0fb60c6b5 Adds maxlength=255 attribute on attachment description input field (#11365).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9982 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 14:34:28 +00:00
Jean-Philippe Lang
6c1db9c3a8 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9981 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 14:28:18 +00:00
Jean-Philippe Lang
7c6582009a Fills locales (#703, #3521).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9980 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 14:19:42 +00:00
Jean-Philippe Lang
b636695631 Removed hard-coded strings (#703, #3521).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9979 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 14:19:01 +00:00
Jean-Philippe Lang
004a13b75c Routing tests for r9977.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9978 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 14:13:48 +00:00
Jean-Philippe Lang
d7b669e50b Workflow enhancement: editable and required fields configurable by role, tracker and status (#703, #3521).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9977 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-15 14:12:17 +00:00
Jean-Philippe Lang
54d55a360a Adds assertions on response status and body.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9976 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-14 08:31:45 +00:00
Jean-Philippe Lang
18f693f9f7 Fixed that 200 API responses have a body containing one space (#11388).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9975 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-14 08:13:55 +00:00
Etienne Massip
6bf60e8c20 Fixes transparency issue.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9974 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-11 00:17:45 +00:00
Etienne Massip
823ac010c5 File handling cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9973 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-11 00:14:56 +00:00
Etienne Massip
0dbe234226 Use tabs instead of spaces for consistency.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9972 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-10 20:27:06 +00:00
Etienne Massip
9c9b0a4150 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9971 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-10 20:23:49 +00:00
Etienne Massip
7502ac484e Make sure that file handle is freed when an error is raised to prevent access exception on subsequent deletion attempt.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9970 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-10 20:16:07 +00:00
Etienne Massip
e2851a8b2a Use 1.8.7 compatible @Pathname#to_s@ alias for @Pathname#to_path@.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9969 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-10 20:14:06 +00:00
Etienne Massip
c929ab5c16 Set temporary path cache variable as a String back again so that TCPDF handles error messages fine.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9968 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-10 19:53:03 +00:00
Jean-Philippe Lang
165c2be523 Fixed that link to the assignee is escaped twice on the issue view (#11352).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9967 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-09 16:46:22 +00:00
Toshi MARUYAMA
b9d7a02b51 Traditional Chinese translation updated by ChunChang Lo (#11350)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9966 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-09 09:59:38 +00:00
Toshi MARUYAMA
2984dc903c Bulgarian translation updated by Ivan Cenov (#11347)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9965 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-09 09:59:06 +00:00
Jean-Philippe Lang
5d90228866 Test failure.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9964 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-08 17:10:58 +00:00
Jean-Philippe Lang
2dbabde7f4 Adds Etags on attachments.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9963 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-08 15:26:40 +00:00
Jean-Philippe Lang
e74d4ecf5f Compute issue ids array only once.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9962 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-08 13:31:22 +00:00
Jean-Philippe Lang
23a1ef543f Show shared versions when editing issues from different projects with the context menu (#11345).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9961 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-08 13:19:46 +00:00
Jean-Philippe Lang
7acd04fb87 Tests should not change settings.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9949 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-08 12:23:47 +00:00
Jean-Philippe Lang
133ca59edb Removed unused helper.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9948 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-08 07:58:28 +00:00
Jean-Philippe Lang
1c825df549 Adds a scope for sorting groups.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9947 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-08 07:40:31 +00:00
Jean-Philippe Lang
c11f5a23fe Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9946 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-08 07:36:58 +00:00
Jean-Philippe Lang
ab96a29460 Fixed test failure.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9945 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-08 07:20:48 +00:00
Jean-Philippe Lang
c02594af96 Test failure.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9944 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 22:08:10 +00:00
Jean-Philippe Lang
193b571e67 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9943 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 18:27:34 +00:00
Jean-Philippe Lang
a1d0acd632 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9942 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 18:05:32 +00:00
Jean-Philippe Lang
6adf61fd02 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9941 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 17:55:54 +00:00
Jean-Philippe Lang
986ffb2434 Use named routes.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9940 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 17:45:12 +00:00
Jean-Philippe Lang
efc6abea07 Fixed that deleting the last reply of a topic does not update last_reply_id.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9939 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 15:09:57 +00:00
Jean-Philippe Lang
851fbaf750 Use named routes.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9938 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 14:39:29 +00:00
Jean-Philippe Lang
7b40767428 Deprecated :confirm => 'Text' option.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9937 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 14:36:49 +00:00
Jean-Philippe Lang
56666a41b8 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9936 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 13:59:26 +00:00
Jean-Philippe Lang
7a529b24ee Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9935 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 13:53:38 +00:00
Jean-Philippe Lang
b379397d95 Fills locales (#1006).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9934 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 13:48:48 +00:00
Jean-Philippe Lang
a0c495b953 Displays thumbnails of attached images of the issue view (#1006).
This behaviour can be turned on/off in Settings -> Display (off by default). Thumbnail size can be configured there too.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9933 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 13:48:07 +00:00
Jean-Philippe Lang
b0bd506201 Fills locales (#8978).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9932 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 09:37:40 +00:00
Jean-Philippe Lang
3b207ee77c Adds a configurable timeout for LDAP authentication (#8978).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9931 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 09:36:04 +00:00
Jean-Philippe Lang
553066e804 Fixed test case name.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9930 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 09:26:26 +00:00
Jean-Philippe Lang
76a4b81cf3 Fixed: Openid registration form should not require user to enter password (#11331).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9929 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 06:22:35 +00:00
Jean-Philippe Lang
6086aa1be4 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9928 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 05:42:52 +00:00
Jean-Philippe Lang
8a080d8e53 Enable openid in test case setup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9927 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 05:41:58 +00:00
Jean-Philippe Lang
6351631ec6 Moved openid functional tests for their own test case.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9926 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 05:40:01 +00:00
Jean-Philippe Lang
5981aee06d Fixed: escaped link in conflict resolution form (#11341).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9925 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 05:23:52 +00:00
Jean-Philippe Lang
232edc6237 Ignore emails with "Auto-Submitted: auto-*" header (#11338).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9924 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-07 05:08:58 +00:00
Toshi MARUYAMA
d4dd35a78c Traditional Chinese translation updated by ChunChang Lo (#11336)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9923 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-06 13:08:14 +00:00
Toshi MARUYAMA
f4a2d1f342 Bulgarian translation updated by Ivan Cenov (#11335)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9922 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-06 13:07:36 +00:00
Toshi MARUYAMA
232d57450a sort files in locales:update rake task
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9921 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-06 13:06:48 +00:00
Jean-Philippe Lang
196f96fff1 Adds Private filter to the issue list (#8577).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9920 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-05 13:51:56 +00:00
Jean-Philippe Lang
1d7c0eb7c0 Makes Private column available on the issue list (#8577).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9919 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-05 13:29:07 +00:00
Jean-Philippe Lang
bd927eea88 Perf: don't load preferences for the anonymous user.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9917 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-05 13:05:11 +00:00
Jean-Philippe Lang
a1f17b982c Perf: use a custom decoder for Role#permissions instead of YAML.load.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9916 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-05 13:04:38 +00:00
Toshi MARUYAMA
cc2ee90f97 sort files in locales:check_interpolation and locales:check_parsing_by_psych rake tasks
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9915 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-05 12:52:28 +00:00
Toshi MARUYAMA
1eebd030ca generate translation files (#1091)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9914 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-05 12:52:06 +00:00
Jean-Philippe Lang
4cecc1beed Ability to disable standard fields on a per tracker basis (#1091).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9912 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-05 12:20:07 +00:00
Toshi MARUYAMA
51a1bf90dd fix Japanese mistranslation for 'label_language_based' (#11328)
Contributed by Go MAEDA.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9911 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-05 11:22:33 +00:00
Jean-Philippe Lang
ad68830a1b Fixed test failure.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9910 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-04 19:24:25 +00:00
Jean-Philippe Lang
1d1aef9d61 Fixed: unified diff link broken on specific file/revision diff (#11325).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9909 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-04 19:17:43 +00:00
Jean-Philippe Lang
d00ba6d2bb Fixed: Can't filter for negative numeric custom field (#11307).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9908 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-04 18:50:09 +00:00
Jean-Philippe Lang
86617fc437 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9907 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-04 18:28:05 +00:00
Jean-Philippe Lang
65b4515e1a Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9906 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-04 17:55:46 +00:00
Jean-Philippe Lang
a8e5ba0007 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9905 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-04 17:37:03 +00:00
Jean-Philippe Lang
df41255105 Perf: don't load parent message for generating a link.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9904 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-07-04 17:34:37 +00:00
Toshi MARUYAMA
bf69f9af9f Bulgarian translation updated by Ivan Cenov (#11272)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9903 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-27 13:01:03 +00:00
Toshi MARUYAMA
0c435ce630 Traditional Chinese translation updated by ChunChang Lo (#11268)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9902 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-27 13:00:37 +00:00
Jean-Philippe Lang
3e5dfed5d6 Typo in french locale.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9901 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-26 22:13:18 +00:00
Jean-Philippe Lang
a265e323ad Slight change to french locale.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9900 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-26 22:05:16 +00:00
Etienne Massip
56cf381357 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9899 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-26 17:36:29 +00:00
Jean-Philippe Lang
eb8f455c13 More specific flash message when creating a user account.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9898 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-26 17:14:44 +00:00
Jean-Philippe Lang
6c9401b624 Additional "Log time" link on project overview (#11181).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9897 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-26 17:05:02 +00:00
Jean-Philippe Lang
f9f5e9e7c6 Fixed: Link is escaped in wiki added/updated notification email (#11262).
Patch by Łukasz Jachymczyk.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9896 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-26 17:03:08 +00:00
Jean-Philippe Lang
6529035ee1 Fixed that wiki diff may produce html (#11209).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9895 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-26 16:57:09 +00:00
Jean-Philippe Lang
7f6ac407ef Fixed that root projects are escaped twice in the project drop down (#11217).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9894 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-26 16:54:30 +00:00
Toshi MARUYAMA
0ecab46229 Japanese translation updated by Go MAEDA (#11260)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9892 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-26 10:55:04 +00:00
Toshi MARUYAMA
c38c3c9436 Simplified Chinese translation updated by Steven Wong (#11259)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9891 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-26 10:37:24 +00:00
Toshi MARUYAMA
2007543539 Simplified Chinese translation updated by Steven Wong (#11261)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9890 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-26 10:36:58 +00:00
Toshi MARUYAMA
72e5b638e9 Traditional Chinese translation added and changed by ChunChang Lo (#11256)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9889 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-26 03:26:58 +00:00
Toshi MARUYAMA
66390fd0e6 Bulgarian translation updated by Ivan Cenov (#11254)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9888 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-25 23:29:57 +00:00
Jean-Philippe Lang
21ac4a3d0e Makes Redmine.pm handle project status (#3640).
Repositories of archived projects are no longer accessible. Repositories of closed projects are read-only.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9887 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-25 20:08:49 +00:00
Jean-Philippe Lang
2ceb6b8230 Moved logic relative to project status from User to Project model (#3640).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9886 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-25 18:44:25 +00:00
Jean-Philippe Lang
7fb2ddefde Visual hint for closed projects on project list (#3640).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9885 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-25 18:39:23 +00:00
Jean-Philippe Lang
8334651c4d Fills locales (#3640).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9884 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-25 17:50:14 +00:00
Jean-Philippe Lang
ac56c0c99c Ability to close projects (read-only) (#3640).
A new permission (Close/reopen project) is available to give non-admin users the ability to close their projects.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9883 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-25 17:49:35 +00:00
Toshi MARUYAMA
5961a1e70d add missing fixture to test/unit/query_test.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9882 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-23 23:29:42 +00:00
Etienne Massip
1221b79e98 Removed test line committed accidently (#11073).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9881 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-23 16:51:54 +00:00
Etienne Massip
0cc817fba3 Use base class name as customized type to fix @UserCustomField#order_statement@.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9880 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-23 15:47:19 +00:00
Jean-Philippe Lang
7ed2c481d8 Reversed order of priorities on the issue summary page (#11205).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9879 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-20 19:24:58 +00:00
Jean-Philippe Lang
26e75568cf Makes repository url read-only after saving.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9878 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-20 18:45:51 +00:00
Toshi MARUYAMA
37cbbcea71 Korean translation updated by Kihyun Yun (#11201)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9877 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-20 08:27:26 +00:00
Jean-Philippe Lang
585d08765e Safe attributes for repositories.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9876 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-19 19:47:54 +00:00
Jean-Philippe Lang
3b854bee59 Make repository identifier accept underscores (#11192).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9875 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-19 19:12:17 +00:00
Toshi MARUYAMA
6c6aae07a2 fix test_global_index of functional activities controller test fails around UTC 00:00
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9871 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-19 00:43:10 +00:00
Toshi MARUYAMA
9c060fe33a scm: git: fix unable to run unit lib test if git binary is not available on Windows
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9870 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-18 23:57:27 +00:00
Jean-Philippe Lang
d3aa9c6a7b Removed invalid nested p tags.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9869 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-18 21:40:16 +00:00
Jean-Philippe Lang
a787325ed2 Updates for 2.0.3 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9864 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-18 18:40:05 +00:00
Jean-Philippe Lang
2311f80c5d Fix: Unable to run unit and functional tests if git binary is not available.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9861 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-18 18:33:33 +00:00
Jean-Philippe Lang
f62507dae5 Fixed that deleting a project with subtasks may fail (#11185).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9858 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-18 17:16:13 +00:00
Toshi MARUYAMA
9e878a9384 Traditional Chinese translation updated by ChunChang Lo (#11186)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9856 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-18 03:45:52 +00:00
Toshi MARUYAMA
e3eab40ec4 fix PDF export tables problems (#10688)
Contributed by Jun NAITOH.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9853 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-17 13:05:05 +00:00
Toshi MARUYAMA
b93e040bb8 fix unit test fails on Ruby 1.8 and JRuby (#2190)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9850 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-17 09:34:12 +00:00
Etienne Massip
dbdc9b9da7 Display dates using user's locale if available (#2190).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9838 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-16 21:45:47 +00:00
Jean-Philippe Lang
2c07b478bb Fixed that time entries should be sorted by date and created_on (#11178).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9837 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-16 20:09:12 +00:00
Jean-Philippe Lang
542b355210 Fixed that sticky messages are not displayed first (#11170).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9836 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-16 19:34:19 +00:00
Etienne Massip
897e83a556 Added missing local variable declaration.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9835 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-14 21:12:03 +00:00
Etienne Massip
768e36caf0 Added a min-width to revision graph holder style to fix its rendering in Chrome (#11061).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9832 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-14 20:57:28 +00:00
Jean-Philippe Lang
76eeb64d1a Fixed that time report raises a SQL error if there are multiple CustomValue for a time entry (#11160).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9831 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-14 10:47:04 +00:00
Jean-Philippe Lang
c804151780 Upgrade to Rails 3.2.6 (#11162).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9830 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-14 10:33:30 +00:00
Jean-Philippe Lang
e199c4b823 Adds support for Git's smart HTTP protocol to Redmine.pm (#4905).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9829 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-14 08:13:53 +00:00
Jean-Philippe Lang
9eef74f09a Additional tests for Redmine.pm.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9828 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-14 08:04:44 +00:00
Jean-Philippe Lang
48a557a79e Use a random filename.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9827 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-12 19:59:28 +00:00
Jean-Philippe Lang
4fda1f6cd1 Start implementing tests for Redmine.pm perl module.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9826 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-12 19:33:09 +00:00
Jean-Philippe Lang
12ea96066b Doc cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9825 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-12 19:14:06 +00:00
Jean-Philippe Lang
13dced9d76 Run only unit, functional then integration tests for coverage.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9824 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-12 19:04:24 +00:00
Jean-Philippe Lang
667693b32c Fixed that Subversion#load_entries_changesets raises an error if entries is nil.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9823 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-12 17:43:23 +00:00
Jean-Philippe Lang
965ef33192 Fixed that lines with spaces only before headings break wiki section extraction (#11133).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9822 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-11 17:51:14 +00:00
Toshi MARUYAMA
462d0ddfe8 Simplified Chinese translation updated (#11130)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9817 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-11 06:30:23 +00:00
Toshi MARUYAMA
9989ae5a06 Bulgarian translation updated by Ivan Cenov (#11129)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9816 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-11 06:29:59 +00:00
Toshi MARUYAMA
89feb8a3fb Japanese translation update by Go MAEDA (#11128)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9815 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-11 06:29:33 +00:00
Toshi MARUYAMA
fdd36ce713 fix test_user_index of activities controller test fails at Japanese morning and afternoon
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9814 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-11 04:42:54 +00:00
Toshi MARUYAMA
21dac7287b remove trailing white-spaces from config/settings.yml
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9813 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-11 01:25:45 +00:00
Toshi MARUYAMA
74a2c5a521 scm: mercurial: use Rails3 ActiveRecord syntax with same as git r9807 at find_changeset_by_name
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9812 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-11 00:18:24 +00:00
Toshi MARUYAMA
96f5d315ea add some avatar tests
* the default size was 50px
* class="gravatar"'
* some tests to test the default and option parsing

Contributed Felix Schäfer.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9811 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-11 00:18:09 +00:00
Jean-Philippe Lang
9b63117856 Extract code from view.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9809 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-10 19:19:22 +00:00
Jean-Philippe Lang
888d284136 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9808 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-10 18:56:42 +00:00
Jean-Philippe Lang
f673027bc1 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9807 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-10 18:38:31 +00:00
Jean-Philippe Lang
8a491dbae9 Faster changesets loading for subversion.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9806 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-10 18:32:48 +00:00
Jean-Philippe Lang
7c105ec9e9 Adds a method to load changesets for repository entries.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9805 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-10 18:32:09 +00:00
Jean-Philippe Lang
9b60214b3a Fixed that Repository#entries returns an Array.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9804 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-10 17:01:33 +00:00
Jean-Philippe Lang
4d12bea397 Make sure we look for a feeds token.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9801 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-10 13:29:47 +00:00
Jean-Philippe Lang
490bfc2934 Stringify tokens.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9800 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-10 13:27:51 +00:00
Jean-Philippe Lang
c47293edb1 Stringify User.try_to_login arguments.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9799 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-10 13:24:28 +00:00
Jean-Philippe Lang
be4ad60058 Make sure that #api_key_from_request returns a String.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9798 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-10 13:23:01 +00:00
Jean-Philippe Lang
74645eb017 Configurable session lifetime and timeout (#6597).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9797 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-10 13:16:56 +00:00
Jean-Philippe Lang
26ff9e1c26 Fixed: German umlauts in Subject get striped with ruby1.8 (#11065).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9796 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-10 08:15:22 +00:00
Jean-Philippe Lang
c73823cd0f Set human size precision to Rails' default which is now 3 (#11118).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9794 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-09 14:09:23 +00:00
Jean-Philippe Lang
8d4e528f22 Force rendering of activity view if activity items count changed.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9787 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-09 10:53:17 +00:00
Jean-Philippe Lang
898a4164a0 Fixed that link to user is escaped in activity title (#11124).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9786 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-09 10:32:34 +00:00
Jean-Philippe Lang
d21bacb01d Fixed that content_for does not work in Hook.render_on (#11105).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9785 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-09 09:19:15 +00:00
Jean-Philippe Lang
8b381e3bd4 Restored KB instead of kB (#11118).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9784 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-09 07:57:38 +00:00
Jean-Philippe Lang
9f788310b1 Fixed that custom_fields property is ignored for time entries in REST API (#11112).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9783 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-09 07:55:26 +00:00
Jean-Philippe Lang
7fba8bc682 Change in Rails' for handling localized number precision (#11118).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9782 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-09 07:46:07 +00:00
Jean-Philippe Lang
2314e41474 Priorities have the same position and can't be reordered (#11098).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9781 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-09 07:27:43 +00:00
Jean-Philippe Lang
77bac4b14d Ability to set default column order in issue list (#11068).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9780 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-08 18:50:22 +00:00
Toshi MARUYAMA
b7a435f7a0 fix German "field_multiple" translation glitch by Andreas Deininger (#11113)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9777 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-08 07:45:39 +00:00
Jean-Philippe Lang
234e5d59ac Show repository images inline when clicking 'View' (#10362).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9776 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-07 17:00:29 +00:00
Jean-Philippe Lang
44ea32afba Updates for 2.0.2 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9770 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-05 17:19:00 +00:00
Jean-Philippe Lang
c8226509fd Fixed that wiki start page can't be changed (#11085).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9768 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-03 22:20:33 +00:00
Jean-Philippe Lang
327d5d2132 Makes users optional in GET /groups/:id (#8981).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9765 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-03 13:32:14 +00:00
Jean-Philippe Lang
1c5b214056 Upgrade to Rails 3.2.5 (#11084).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9764 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-03 13:16:16 +00:00
Jean-Philippe Lang
5bfe80949c Rails 3.2.5 compatibility.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9763 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-03 12:29:21 +00:00
Jean-Philippe Lang
0523ac387b Test failure due to a regression in Rails 3.2.5.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9762 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-03 12:15:55 +00:00
Jean-Philippe Lang
ef01cada8b Fixed argument to repository_path that triggers an error with Rails 3.2.5.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9761 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-03 12:02:46 +00:00
Jean-Philippe Lang
182a215c7b Renamed #changes association to #ticket_changes.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9760 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-03 11:47:26 +00:00
Jean-Philippe Lang
2cbf9c9cc4 Renamed #changes association to #filechanges (clash with AR::Base.changes that triggers errors with Rails 3.2.5).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9759 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-03 11:46:58 +00:00
Jean-Philippe Lang
dd9c2cafa7 REST Api for Groups (#8981).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9758 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-03 10:40:32 +00:00
Jean-Philippe Lang
3142183b30 Fixed that Redmine.pm does not support "bind as user" ldap authentication (#11046).
Patch contributed by Adi Kriegisch.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9755 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-03 08:49:46 +00:00
Toshi MARUYAMA
ba25d27e3a scm: git: skip Latin-1 path tests on Git for Windows above 1.7.10
Git for Windows (msysGit) changed internal API from ANSI to Unicode in 1.7.10.
http://code.google.com/p/msysgit/issues/detail?id=80

So, Latin-1 path tests fail on Japanese Windows

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9750 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-02 18:36:23 +00:00
Toshi MARUYAMA
69b46ab1be scm: mercurial: remove comment about Mercurial 0.9.5 compatibility from redminehelper.py
Redmine supports Mercurial 1.2 or higher from r7650.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9749 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-02 10:39:27 +00:00
Jean-Philippe Lang
dd1cb4a5bf Adds #find_group filter.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9748 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-01 19:41:06 +00:00
Jean-Philippe Lang
d58e5a0a1e Removed auto-generated comments.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9747 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-01 19:07:22 +00:00
Jean-Philippe Lang
9f531a4380 Use safe_attributes in GroupsController.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9746 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-01 19:06:16 +00:00
Jean-Philippe Lang
733fef458c Replaced group[lastname] parameter with group[name].
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9745 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-01 18:24:33 +00:00
Etienne Massip
121ce2a390 Use time_zone_select specific helper.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9744 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-01 13:38:03 +00:00
Etienne Massip
fc7b790081 Get rid of ActiveSupport::Concern InstanceMethods deprecation warnings.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9743 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-30 18:33:26 +00:00
Jean-Philippe Lang
d915a5e36d Adds missing require 'rubygems' (#11051).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9742 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-30 17:56:39 +00:00
Jean-Philippe Lang
4ee133e77e Fixed that project, issue and activity should be preserved when logging time with "Create and continue" (#11038).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9741 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-29 18:54:26 +00:00
Jean-Philippe Lang
53a0cee57a Fixed that logging time inside redirects at global time logging (#11038).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9740 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-29 17:07:07 +00:00
Jean-Philippe Lang
251d62c2bf Adds redmine:plugins:test rake tasks for running plugins tests.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9738 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-28 10:02:24 +00:00
Jean-Philippe Lang
afbff44fb4 Fixes test templates for plugins.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9737 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-28 09:00:21 +00:00
Jean-Philippe Lang
3ba6718805 Fixed test_helper template for plugins.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9736 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-28 08:40:20 +00:00
Toshi MARUYAMA
e1268faa4a code layout cleanup app/views/users/_mail_notifications.html.erb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9735 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-28 08:24:46 +00:00
Toshi MARUYAMA
cfaa0fc23e fix project list is not shown on Email notifications (#11032)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9734 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-28 08:24:31 +00:00
Jean-Philippe Lang
cfddd2ff48 Updates for 2.0.1 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9731 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-28 07:23:48 +00:00
Toshi MARUYAMA
87783362cf code layout cleanup test/functional/users_controller_test.rb
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9730 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-28 07:07:00 +00:00
Jean-Philippe Lang
9aef7a3242 Fixed test failures on wiki_page updated_on eager loading (#10996).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9727 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-26 19:35:25 +00:00
Jean-Philippe Lang
189be55235 Fixed time zone issues introduced by r9719 (#10996).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9726 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-26 12:07:56 +00:00
Jean-Philippe Lang
84084b0168 Restores local time zone for timestamps for 1.x compatibility (#10996).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9719 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-26 09:12:04 +00:00
Jean-Philippe Lang
3a4a708d51 Fixed that plugin model generator does not generate the migration (#11024).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9718 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-26 08:50:32 +00:00
Jean-Philippe Lang
e4332ba35f Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9717 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-25 20:54:11 +00:00
Jean-Philippe Lang
d664fdcde9 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9716 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-25 20:51:43 +00:00
Jean-Philippe Lang
aa18cd54c7 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9715 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-25 20:43:18 +00:00
Jean-Philippe Lang
59cbc68dde Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9714 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-25 20:18:55 +00:00
Jean-Philippe Lang
221585c7b5 Code cleanup.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9713 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-25 19:48:22 +00:00
Jean-Philippe Lang
e7b9a9c6ff Replace RAILS_DEFAULT_LOGGER with ::Rails.logger (#11013).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9712 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-25 16:54:23 +00:00
Jean-Philippe Lang
472d3a00a3 Fixed that some input fields are escaped on validation failures (#11027).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9711 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-25 16:50:25 +00:00
Jean-Philippe Lang
235238b583 Fixed that project identifier can always be updated (#11028).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9710 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-25 16:43:18 +00:00
Jean-Philippe Lang
73500a349b Fixed that the issue link is escaped on my page spent time (#10991).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9709 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-25 16:34:40 +00:00
Toshi MARUYAMA
b587f693ab Korean translation added and changed by jongyoon Choi (#10988)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9708 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-25 13:50:12 +00:00
Jean-Philippe Lang
c77370a9eb Use a modal form to create a category from the issue form.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9707 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-24 17:31:53 +00:00
Jean-Philippe Lang
6206c88dfa Fixed that updated_on is not updated when updating an issue (#10964).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9703 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-21 18:23:03 +00:00
Jean-Philippe Lang
1ab9e42b9b Fixed that columns selection in not displayed on the custom query form (#10972).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9702 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-21 16:55:48 +00:00
Etienne Massip
ae8830ed61 Added yard to development gems.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9701 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-18 13:52:49 +00:00
Jean-Philippe Lang
1996af104d Fixed that watchers delete links are escaped on the issue view (#10932).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9700 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-15 19:33:42 +00:00
Jean-Philippe Lang
e68043e13c Fixed that creating a new version from project settings does not redirect to versions tab (#10923).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9699 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-15 17:25:27 +00:00
Jean-Philippe Lang
05481fe96b Updates for 2.0.0 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9696 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-14 22:17:57 +00:00
572 changed files with 12886 additions and 22662 deletions

View File

@@ -1,7 +1,7 @@
source 'http://rubygems.org'
gem 'rails', '3.2.6'
gem 'prototype-rails', '3.2.1'
gem 'rails', '3.2.8'
gem "jquery-rails", "~> 2.0.2"
gem "i18n", "~> 0.6.0"
gem "coderay", "~> 1.0.6"
gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby]

View File

@@ -40,19 +40,26 @@ class AccountController < ApplicationController
redirect_to home_url
end
# Enable user to choose a new password
# Lets user choose a new password
def lost_password
redirect_to(home_url) && return unless Setting.lost_password?
if params[:token]
@token = Token.find_by_action_and_value("recovery", params[:token])
redirect_to(home_url) && return unless @token and !@token.expired?
@token = Token.find_by_action_and_value("recovery", params[:token].to_s)
if @token.nil? || @token.expired?
redirect_to home_url
return
end
@user = @token.user
unless @user && @user.active?
redirect_to home_url
return
end
if request.post?
@user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
if @user.save
@token.destroy
flash[:notice] = l(:notice_account_password_updated)
redirect_to :action => 'login'
redirect_to signin_path
return
end
end
@@ -60,17 +67,23 @@ class AccountController < ApplicationController
return
else
if request.post?
user = User.find_by_mail(params[:mail])
# user not found in db
(flash.now[:error] = l(:notice_account_unknown_email); return) unless user
# user uses an external authentification
(flash.now[:error] = l(:notice_can_t_change_password); return) if user.auth_source_id
user = User.find_by_mail(params[:mail].to_s)
# user not found or not active
unless user && user.active?
flash.now[:error] = l(:notice_account_unknown_email)
return
end
# user cannot change its password
unless user.change_password_allowed?
flash.now[:error] = l(:notice_can_t_change_password)
return
end
# create a new token for password recovery
token = Token.new(:user => user, :action => "recovery")
if token.save
Mailer.lost_password(token).deliver
flash[:notice] = l(:notice_account_lost_email_sent)
redirect_to :action => 'login'
redirect_to signin_path
return
end
end
@@ -84,8 +97,9 @@ class AccountController < ApplicationController
session[:auth_source_registration] = nil
@user = User.new(:language => Setting.default_language)
else
user_params = params[:user] || {}
@user = User.new
@user.safe_attributes = params[:user]
@user.safe_attributes = user_params
@user.admin = false
@user.register
if session[:auth_source_registration]
@@ -100,7 +114,9 @@ class AccountController < ApplicationController
end
else
@user.login = params[:user][:login]
@user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation]
unless user_params[:identity_url].present? && user_params[:password].blank? && user_params[:password_confirmation].blank?
@user.password, @user.password_confirmation = user_params[:password], user_params[:password_confirmation]
end
case Setting.self_registration
when '1'
@@ -126,7 +142,7 @@ class AccountController < ApplicationController
token.destroy
flash[:notice] = l(:notice_account_activated)
end
redirect_to :action => 'login'
redirect_to signin_path
end
private
@@ -194,6 +210,7 @@ class AccountController < ApplicationController
end
def successful_authentication(user)
logger.info "Successful authentication for '#{user.login}' from #{request.remote_ip} at #{Time.now.utc}"
# Valid user
self.logged_user = user
# generate a key and set cookie if autologin
@@ -221,7 +238,7 @@ class AccountController < ApplicationController
def onthefly_creation_failed(user, auth_source_options = { })
@user = user
session[:auth_source_registration] = auth_source_options unless auth_source_options.empty?
render :action => 'register'
render register_path
end
def invalid_credentials
@@ -237,7 +254,7 @@ class AccountController < ApplicationController
if user.save and token.save
Mailer.register(token).deliver
flash[:notice] = l(:notice_account_register_done)
redirect_to :action => 'login'
redirect_to signin_path
else
yield if block_given?
end
@@ -274,6 +291,6 @@ class AccountController < ApplicationController
def account_pending
flash[:notice] = l(:notice_account_pending)
redirect_to :action => 'login'
redirect_to signin_path
end
end

View File

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

View File

@@ -35,47 +35,95 @@ class ApplicationController < ActionController::Base
cookies.delete(:autologin)
end
before_filter :user_setup, :check_if_login_required, :set_localization
before_filter :session_expiration, :user_setup, :check_if_login_required, :set_localization
rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
rescue_from ::Unauthorized, :with => :deny_access
rescue_from ::ActionView::MissingTemplate, :with => :missing_template
include Redmine::Search::Controller
include Redmine::MenuManager::MenuController
helper Redmine::MenuManager::MenuHelper
def session_expiration
if session[:user_id]
if session_expired? && !try_to_autologin
reset_session
flash[:error] = l(:error_session_expired)
redirect_to signin_url
else
session[:atime] = Time.now.utc.to_i
end
end
end
def session_expired?
if Setting.session_lifetime?
unless session[:ctime] && (Time.now.utc.to_i - session[:ctime].to_i <= Setting.session_lifetime.to_i * 60)
return true
end
end
if Setting.session_timeout?
unless session[:atime] && (Time.now.utc.to_i - session[:atime].to_i <= Setting.session_timeout.to_i * 60)
return true
end
end
false
end
def start_user_session(user)
session[:user_id] = user.id
session[:ctime] = Time.now.utc.to_i
session[:atime] = Time.now.utc.to_i
end
def user_setup
# Check the settings cache for each request
Setting.check_cache
# Find the current user
User.current = find_current_user
logger.info(" Current user: " + (User.current.logged? ? "#{User.current.login} (id=#{User.current.id})" : "anonymous")) if logger
end
# Returns the current user or nil if no user is logged in
# and starts a session if needed
def find_current_user
if session[:user_id]
# existing session
(User.active.find(session[:user_id]) rescue nil)
elsif cookies[:autologin] && Setting.autologin?
# auto-login feature starts a new session
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?
# RSS key authentication does not start a session
User.find_by_rss_key(params[:key])
elsif Setting.rest_api_enabled? && accept_api_auth?
user = nil
unless api_request?
if session[:user_id]
# existing session
user = (User.active.find(session[:user_id]) rescue nil)
elsif autologin_user = try_to_autologin
user = autologin_user
elsif params[:format] == 'atom' && params[:key] && request.get? && accept_rss_auth?
# RSS key authentication does not start a session
user = User.find_by_rss_key(params[:key])
end
end
if user.nil? && Setting.rest_api_enabled? && accept_api_auth?
if (key = api_key_from_request)
# Use API key
User.find_by_api_key(key)
user = User.find_by_api_key(key)
else
# HTTP Basic, either username/password or API key/random
authenticate_with_http_basic do |username, password|
User.try_to_login(username, password) || User.find_by_api_key(username)
user = User.try_to_login(username, password) || User.find_by_api_key(username)
end
end
end
user
end
def try_to_autologin
if cookies[:autologin] && Setting.autologin?
# auto-login feature starts a new session
user = User.try_to_autologin(cookies[:autologin])
if user
reset_session
start_user_session(user)
end
user
end
end
# Sets the logged in user
@@ -83,7 +131,7 @@ class ApplicationController < ActionController::Base
reset_session
if user && user.is_a?(User)
User.current = user
session[:user_id] = user.id
start_user_session(user)
else
User.current = User.anonymous
end
@@ -235,7 +283,7 @@ class ApplicationController < ActionController::Base
# make sure that the user is a member of the project (or admin) if project is private
# used as a before_filter for actions that do not require any particular permission on the project
def check_project_privacy
if @project && @project.active?
if @project && !@project.archived?
if @project.visible?
true
else
@@ -249,12 +297,16 @@ class ApplicationController < ActionController::Base
end
def back_url
params[:back_url] || request.env['HTTP_REFERER']
url = params[:back_url]
if url.nil? && referer = request.env['HTTP_REFERER']
url = CGI.unescape(referer.to_s)
end
url
end
def redirect_back_or_default(default)
back_url = CGI.unescape(params[:back_url].to_s)
if !back_url.blank?
back_url = params[:back_url].to_s
if back_url.present?
begin
uri = URI.parse(back_url)
# do not redirect user to another host or to the login or register page
@@ -263,6 +315,7 @@ class ApplicationController < ActionController::Base
return
end
rescue URI::InvalidURIError
logger.warn("Could not redirect to invalid URL #{back_url}")
# redirect to default
end
end
@@ -306,13 +359,17 @@ class ApplicationController < ActionController::Base
format.html {
render :template => 'common/error', :layout => use_layout, :status => @status
}
format.atom { head @status }
format.xml { head @status }
format.js { head @status }
format.json { head @status }
format.any { head @status }
end
end
# Handler for ActionView::MissingTemplate exception
def missing_template
logger.warn "Missing template, responding with 404"
@project = nil
render_404
end
# Filter for actions that provide an API response
# but have no HTML representation for non admin users
def require_admin_or_api_request
@@ -479,6 +536,12 @@ class ApplicationController < ActionController::Base
render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
end
# Renders a 200 response for successfull updates or deletions via the API
def render_api_ok
# head :ok would return a response body with one space
render :text => '', :status => :ok, :layout => nil
end
# Renders API response on validation failure
def render_validation_errors(objects)
if objects.is_a?(Array)

View File

@@ -17,7 +17,7 @@
class AttachmentsController < ApplicationController
before_filter :find_project, :except => :upload
before_filter :file_readable, :read_authorize, :only => [:show, :download]
before_filter :file_readable, :read_authorize, :only => [:show, :download, :thumbnail]
before_filter :delete_authorize, :only => :destroy
before_filter :authorize_global, :only => :upload
@@ -52,11 +52,26 @@ class AttachmentsController < ApplicationController
@attachment.increment_download
end
# images are sent inline
send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
:type => detect_content_type(@attachment),
:disposition => (@attachment.image? ? 'inline' : 'attachment')
if stale?(:etag => @attachment.digest)
# images are sent inline
send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
:type => detect_content_type(@attachment),
:disposition => (@attachment.image? ? 'inline' : 'attachment')
end
end
def thumbnail
if @attachment.thumbnailable? && thumbnail = @attachment.thumbnail(:size => params[:size])
if stale?(:etag => thumbnail)
send_file thumbnail,
:filename => filename_for_content_disposition(@attachment.filename),
:type => detect_content_type(@attachment),
:disposition => 'inline'
end
else
# No thumbnail for the attachment or thumbnail could not be created
render :nothing => true, :status => 404
end
end
def upload

View File

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

View File

@@ -25,7 +25,7 @@ class BoardsController < ApplicationController
helper :watchers
def index
@boards = @project.boards
@boards = @project.boards.includes(:last_message => :author).all
# show the board if there is only one
if @boards.size == 1
@board = @boards.first

View File

@@ -24,6 +24,7 @@ class ContextMenusController < ApplicationController
if (@issues.size == 1)
@issue = @issues.first
end
@issue_ids = @issues.map(&:id).sort
@allowed_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
@projects = @issues.collect(&:project).compact.uniq
@@ -48,6 +49,7 @@ class ContextMenusController < ApplicationController
@assignables = @projects.map(&:assignable_users).reduce(:&)
@trackers = @projects.map(&:trackers).reduce(:&)
end
@versions = @projects.map {|p| p.shared_versions.open}.reduce(:&)
@priorities = IssuePriority.active.reverse
@back = back_url
@@ -65,6 +67,7 @@ class ContextMenusController < ApplicationController
end
end
@safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
render :layout => false
end

View File

@@ -19,51 +19,34 @@ class GroupsController < ApplicationController
layout 'admin'
before_filter :require_admin
before_filter :find_group, :except => [:index, :new, :create]
accept_api_auth :index, :show, :create, :update, :destroy, :add_users, :remove_user
helper :custom_fields
# GET /groups
# GET /groups.xml
def index
@groups = Group.find(:all, :order => 'lastname')
@groups = Group.sorted.all
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @groups }
format.html
format.api
end
end
# GET /groups/1
# GET /groups/1.xml
def show
@group = Group.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => @group }
format.html
format.api
end
end
# GET /groups/new
# GET /groups/new.xml
def new
@group = Group.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @group }
end
end
# GET /groups/1/edit
def edit
@group = Group.find(params[:id], :include => :projects)
end
# POST /groups
# POST /groups.xml
def create
@group = Group.new(params[:group])
@group = Group.new
@group.safe_attributes = params[:group]
respond_to do |format|
if @group.save
@@ -71,102 +54,87 @@ class GroupsController < ApplicationController
flash[:notice] = l(:notice_successful_create)
redirect_to(params[:continue] ? new_group_path : groups_path)
}
format.xml { render :xml => @group, :status => :created, :location => @group }
format.api { render :action => 'show', :status => :created, :location => group_url(@group) }
else
format.html { render :action => "new" }
format.xml { render :xml => @group.errors, :status => :unprocessable_entity }
format.api { render_validation_errors(@group) }
end
end
end
# PUT /groups/1
# PUT /groups/1.xml
def edit
end
def update
@group = Group.find(params[:id])
@group.safe_attributes = params[:group]
respond_to do |format|
if @group.update_attributes(params[:group])
if @group.save
flash[:notice] = l(:notice_successful_update)
format.html { redirect_to(groups_path) }
format.xml { head :ok }
format.api { render_api_ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => @group.errors, :status => :unprocessable_entity }
format.api { render_validation_errors(@group) }
end
end
end
# DELETE /groups/1
# DELETE /groups/1.xml
def destroy
@group = Group.find(params[:id])
@group.destroy
respond_to do |format|
format.html { redirect_to(groups_url) }
format.xml { head :ok }
format.api { render_api_ok }
end
end
def add_users
@group = Group.find(params[:id])
users = User.find_all_by_id(params[:user_ids])
@group.users << users if request.post?
@users = User.find_all_by_id(params[:user_id] || params[:user_ids])
@group.users << @users if request.post?
respond_to do |format|
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'users' }
format.js {
render(:update) {|page|
page.replace_html "tab-content-users", :partial => 'groups/users'
users.each {|user| page.visual_effect(:highlight, "user-#{user.id}") }
}
}
format.js
format.api { render_api_ok }
end
end
def remove_user
@group = Group.find(params[:id])
@group.users.delete(User.find(params[:user_id])) if request.delete?
respond_to do |format|
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'users' }
format.js { render(:update) {|page| page.replace_html "tab-content-users", :partial => 'groups/users'} }
format.js
format.api { render_api_ok }
end
end
def autocomplete_for_user
@group = Group.find(params[:id])
@users = User.active.not_in_group(@group).like(params[:q]).all(:limit => 100)
render :layout => false
end
def edit_membership
@group = Group.find(params[:id])
@membership = Member.edit_membership(params[:membership_id], params[:membership], @group)
@membership.save if request.post?
respond_to do |format|
if @membership.valid?
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
format.js {
render(:update) {|page|
page.replace_html "tab-content-memberships", :partial => 'groups/memberships'
page.visual_effect(:highlight, "member-#{@membership.id}")
}
}
else
format.js {
render(:update) {|page|
page.alert(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', ')))
}
}
end
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
format.js
end
end
def destroy_membership
@group = Group.find(params[:id])
Member.find(params[:membership_id]).destroy if request.post?
respond_to do |format|
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'groups/memberships'} }
format.js
end
end
private
def find_group
@group = Group.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

@@ -41,6 +41,11 @@ class IssueCategoriesController < ApplicationController
def new
@category = @project.issue_categories.build
@category.safe_attributes = params[:issue_category]
respond_to do |format|
format.html
format.js
end
end
def create
@@ -52,20 +57,13 @@ class IssueCategoriesController < ApplicationController
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project
end
format.js do
# IE doesn't support the replace_html rjs method for select box options
render(:update) {|page| page.replace "issue_category_id",
content_tag('select', content_tag('option') + options_from_collection_for_select(@project.issue_categories, 'id', 'name', @category.id), :id => 'issue_category_id', :name => 'issue[category_id]')
}
end
format.js
format.api { render :action => 'show', :status => :created, :location => issue_category_path(@category) }
end
else
respond_to do |format|
format.html { render :action => 'new'}
format.js do
render(:update) {|page| page.alert(@category.errors.full_messages.join('\n')) }
end
format.js { render :action => 'new'}
format.api { render_validation_errors(@category) }
end
end
@@ -82,7 +80,7 @@ class IssueCategoriesController < ApplicationController
flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project
}
format.api { head :ok }
format.api { render_api_ok }
end
else
respond_to do |format|
@@ -102,7 +100,7 @@ class IssueCategoriesController < ApplicationController
@category.destroy(reassign_to)
respond_to do |format|
format.html { redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'categories' }
format.api { head :ok }
format.api { render_api_ok }
end
return
end

View File

@@ -49,16 +49,9 @@ class IssueRelationsController < ApplicationController
respond_to do |format|
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @issue }
format.js do
format.js {
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
render :update do |page|
page.replace_html "relations", :partial => 'issues/relations'
if @relation.errors.empty?
page << "$('relation_delay').value = ''"
page << "$('relation_issue_to_id').value = ''"
end
end
end
}
format.api {
if saved
render :action => 'show', :status => :created, :location => relation_url(@relation)
@@ -75,8 +68,8 @@ class IssueRelationsController < ApplicationController
respond_to do |format|
format.html { redirect_to issue_path } # TODO : does this really work since @issue is always nil? What is it useful to?
format.js { render(:update) {|page| page.remove "relation-#{@relation.id}"} }
format.api { head :ok }
format.js
format.api { render_api_ok }
end
end

View File

@@ -50,7 +50,6 @@ class IssuesController < ApplicationController
include SortHelper
include IssuesHelper
helper :timelog
helper :gantt
include Redmine::Export::PDF
def index
@@ -128,17 +127,7 @@ class IssuesController < ApplicationController
def new
respond_to do |format|
format.html { render :action => 'new', :layout => !request.xhr? }
format.js {
render(:update) { |page|
if params[:project_change]
page.replace_html 'all_attributes', :partial => 'form'
else
page.replace_html 'attributes', :partial => 'attributes'
end
m = User.current.allowed_to?(:log_time, @issue.project) ? 'show' : 'hide'
page << "if ($('log_time')) {Element.#{m}('log_time');}"
}
}
format.js { render :partial => 'update_form' }
end
end
@@ -150,7 +139,7 @@ class IssuesController < ApplicationController
respond_to do |format|
format.html {
render_attachment_warning_if_needed(@issue)
flash[:notice] = l(:notice_issue_successful_create, :id => "<a href='#{issue_path(@issue)}'>##{@issue.id}</a>")
flash[:notice] = l(:notice_issue_successful_create, :id => view_context.link_to("##{@issue.id}", issue_path(@issue), :title => @issue.subject))
redirect_to(params[:continue] ? { :action => 'new', :project_id => @issue.project, :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
{ :action => 'show', :id => @issue })
}
@@ -183,12 +172,7 @@ class IssuesController < ApplicationController
rescue ActiveRecord::StaleObjectError
@conflict = true
if params[:last_journal_id]
if params[:last_journal_id].present?
last_journal_id = params[:last_journal_id].to_i
@conflict_journals = @issue.journals.all(:conditions => ["#{Journal.table_name}.id > ?", last_journal_id])
else
@conflict_journals = @issue.journals.all
end
@conflict_journals = @issue.journals_after(params[:last_journal_id]).all
end
end
@@ -198,7 +182,7 @@ class IssuesController < ApplicationController
respond_to do |format|
format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
format.api { head :ok }
format.api { render_api_ok }
end
else
respond_to do |format|
@@ -237,6 +221,7 @@ class IssuesController < ApplicationController
@categories = target_projects.map {|p| p.issue_categories}.reduce(:&)
if @copy
@attachments_present = @issues.detect {|i| i.attachments.any?}.present?
@subtasks_present = @issues.detect {|i| !i.leaf?}.present?
end
@safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
@@ -250,10 +235,20 @@ class IssuesController < ApplicationController
unsaved_issue_ids = []
moved_issues = []
if @copy && params[:copy_subtasks].present?
# Descendant issues will be copied with the parent task
# Don't copy them twice
@issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}}
end
@issues.each do |issue|
issue.reload
if @copy
issue = issue.copy({}, :attachments => params[:copy_attachments].present?)
issue = issue.copy({},
:attachments => params[:copy_attachments].present?,
:subtasks => params[:copy_subtasks].present?
)
end
journal = issue.init_journal(User.current, params[:notes])
issue.safe_attributes = attributes
@@ -308,7 +303,7 @@ class IssuesController < ApplicationController
end
respond_to do |format|
format.html { redirect_back_or_default(:action => 'index', :project_id => @project) }
format.api { head :ok }
format.api { render_api_ok }
end
end
@@ -390,7 +385,8 @@ private
begin
@copy_from = Issue.visible.find(params[:copy_from])
@copy_attachments = params[:copy_attachments].present? || request.get?
@issue.copy_from(@copy_from, :attachments => @copy_attachments)
@copy_subtasks = params[:copy_subtasks].present? || request.get?
@issue.copy_from(@copy_from, :attachments => @copy_attachments, :subtasks => @copy_subtasks)
rescue ActiveRecord::RecordNotFound
render_404
return

View File

@@ -67,16 +67,8 @@ class JournalsController < ApplicationController
end
# Replaces pre blocks with [...]
text = text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]')
content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> "
content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
render(:update) { |page|
page.<< "$('notes').value = \"#{escape_javascript content}\";"
page.show 'update'
page << "Form.Element.focus('notes');"
page << "Element.scrollTo('update');"
page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;"
}
@content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> "
@content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
end
def edit

View File

@@ -63,31 +63,16 @@ class MembersController < ApplicationController
end
respond_to do |format|
if members.present? && members.all? {|m| m.valid? }
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
format.js {
render(:update) {|page|
page.replace_html "tab-content-members", :partial => 'projects/settings/members'
page << 'hideOnLoad()'
members.each {|member| page.visual_effect(:highlight, "member-#{member.id}") }
}
}
format.api {
@member = members.first
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
format.js { @members = members }
format.api {
@member = members.first
if @member.valid?
render :action => 'show', :status => :created, :location => membership_url(@member)
}
else
format.js {
render(:update) {|page|
errors = members.collect {|m|
m.errors.full_messages
}.flatten.uniq
page.alert(l(:notice_failed_to_save_members, :errors => errors.join(', ')))
}
}
format.api { render_validation_errors(members.first) }
end
else
render_validation_errors(@member)
end
}
end
end
@@ -98,16 +83,10 @@ class MembersController < ApplicationController
saved = @member.save
respond_to do |format|
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
format.js {
render(:update) {|page|
page.replace_html "tab-content-members", :partial => 'projects/settings/members'
page << 'hideOnLoad()'
page.visual_effect(:highlight, "member-#{@member.id}")
}
}
format.js
format.api {
if saved
head :ok
render_api_ok
else
render_validation_errors(@member)
end
@@ -121,14 +100,10 @@ class MembersController < ApplicationController
end
respond_to do |format|
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
format.js { render(:update) {|page|
page.replace_html "tab-content-members", :partial => 'projects/settings/members'
page << 'hideOnLoad()'
}
}
format.js
format.api {
if @member.destroyed?
head :ok
render_api_ok
else
head :unprocessable_entity
end
@@ -140,5 +115,4 @@ class MembersController < ApplicationController
@principals = Principal.active.not_member_of(@project).like(params[:q]).all(:limit => 100)
render :layout => false
end
end

View File

@@ -22,6 +22,7 @@ class MessagesController < ApplicationController
before_filter :find_message, :except => [:new, :preview]
before_filter :authorize, :except => [:preview, :edit, :destroy]
helper :boards
helper :watchers
helper :attachments
include AttachmentsHelper
@@ -59,7 +60,7 @@ class MessagesController < ApplicationController
if @message.save
call_hook(:controller_messages_new_after_save, { :params => params, :message => @message})
render_attachment_warning_if_needed(@message)
redirect_to :action => 'show', :id => @message
redirect_to board_message_path(@board, @message)
end
end
end
@@ -76,7 +77,7 @@ class MessagesController < ApplicationController
attachments = Attachment.attach_files(@reply, params[:attachments])
render_attachment_warning_if_needed(@reply)
end
redirect_to :action => 'show', :id => @topic, :r => @reply
redirect_to board_message_path(@board, @topic, :r => @reply)
end
# Edit a message
@@ -88,7 +89,7 @@ class MessagesController < ApplicationController
render_attachment_warning_if_needed(@message)
flash[:notice] = l(:notice_successful_update)
@message.reload
redirect_to :action => 'show', :board_id => @message.board, :id => @message.root, :r => (@message.parent_id && @message.id)
redirect_to board_message_path(@message.board, @message.root, :r => (@message.parent_id && @message.id))
end
end
@@ -97,32 +98,26 @@ class MessagesController < ApplicationController
(render_403; return false) unless @message.destroyable_by?(User.current)
r = @message.to_param
@message.destroy
redirect_to @message.parent.nil? ?
{ :controller => 'boards', :action => 'show', :project_id => @project, :id => @board } :
{ :action => 'show', :id => @message.parent, :r => r }
if @message.parent
redirect_to board_message_path(@board, @message.parent, :r => r)
else
redirect_to project_board_path(@project, @board)
end
end
def quote
user = @message.author
text = @message.content
subject = @message.subject.gsub('"', '\"')
subject = "RE: #{subject}" unless subject.starts_with?('RE:')
content = "#{ll(Setting.default_language, :text_user_wrote, user)}\\n> "
content << text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]').gsub('"', '\"').gsub(/(\r?\n|\r\n?)/, "\\n> ") + "\\n\\n"
render(:update) { |page|
page << "$('message_subject').value = \"#{subject}\";"
page.<< "$('message_content').value = \"#{content}\";"
page.show 'reply'
page << "Form.Element.focus('message_content');"
page << "Element.scrollTo('reply');"
page << "$('message_content').scrollTop = $('message_content').scrollHeight - $('message_content').clientHeight;"
}
@subject = @message.subject
@subject = "RE: #{@subject}" unless @subject.starts_with?('RE:')
@content = "#{ll(Setting.default_language, :text_user_wrote, @message.author)}\n> "
@content << @message.content.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]').gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
end
def preview
message = @board.messages.find_by_id(params[:id])
@attachements = message.attachments if message
@text = (params[:message] || params[:reply])[:content]
@previewed = message
render :partial => 'common/preview'
end

View File

@@ -135,7 +135,11 @@ class MyController < ApplicationController
@user = User.current
@blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT.dup
@block_options = []
BLOCKS.each {|k, v| @block_options << [l("my.blocks.#{v}", :default => [v, v.to_s.humanize]), k.dasherize]}
BLOCKS.each do |k, v|
unless %w(top left right).detect {|f| (@blocks[f] ||= []).include?(k)}
@block_options << [l("my.blocks.#{v}", :default => [v, v.to_s.humanize]), k.dasherize]
end
end
end
# Add a block to user's page
@@ -152,7 +156,7 @@ class MyController < ApplicationController
layout['top'].unshift block
@user.pref[:my_page_layout] = layout
@user.pref.save
render :partial => "block", :locals => {:user => @user, :block_name => block}
redirect_to :action => 'page_layout'
end
# Remove a block to user's page
@@ -165,7 +169,7 @@ class MyController < ApplicationController
%w(top left right).each {|f| (layout[f] ||= []).delete block }
@user.pref[:my_page_layout] = layout
@user.pref.save
render :nothing => true
redirect_to :action => 'page_layout'
end
# Change blocks order on user's page
@@ -175,7 +179,8 @@ class MyController < ApplicationController
group = params[:group]
@user = User.current
if group.is_a?(String)
group_items = (params["list-#{group}"] || []).collect(&:underscore)
group_items = (params["blocks"] || []).collect(&:underscore)
group_items.each {|s| s.sub!(/^block_/, '')}
if group_items and group_items.is_a? Array
layout = @user.pref[:my_page_layout] || {}
# remove group blocks if they are presents in other groups

View File

@@ -48,7 +48,11 @@ class ProjectsController < ApplicationController
def index
respond_to do |format|
format.html {
@projects = Project.visible.find(:all, :order => 'lft')
scope = Project
unless params[:closed]
scope = scope.active
end
@projects = scope.visible.order('lft').all
}
format.api {
@offset, @limit = api_offset_and_limit
@@ -65,14 +69,14 @@ class ProjectsController < ApplicationController
def new
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.all
@trackers = Tracker.sorted.all
@project = Project.new
@project.safe_attributes = params[:project]
end
def create
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.all
@trackers = Tracker.sorted.all
@project = Project.new
@project.safe_attributes = params[:project]
@@ -105,7 +109,7 @@ class ProjectsController < ApplicationController
def copy
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.all
@trackers = Tracker.sorted.all
@root_projects = Project.find(:all,
:conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}",
:order => 'name')
@@ -152,12 +156,8 @@ class ProjectsController < ApplicationController
cond = @project.project_condition(Setting.display_subprojects_issues?)
@open_issues_by_tracker = Issue.visible.count(:group => :tracker,
:include => [:project, :status, :tracker],
:conditions => ["(#{cond}) AND #{IssueStatus.table_name}.is_closed=?", false])
@total_issues_by_tracker = Issue.visible.count(:group => :tracker,
:include => [:project, :status, :tracker],
:conditions => cond)
@open_issues_by_tracker = Issue.visible.open.where(cond).count(:group => :tracker)
@total_issues_by_tracker = Issue.visible.where(cond).count(:group => :tracker)
if User.current.allowed_to?(:view_time_entries, @project)
@total_hours = TimeEntry.visible.sum(:hours, :include => :project, :conditions => cond).to_f
@@ -175,7 +175,7 @@ class ProjectsController < ApplicationController
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@issue_category ||= IssueCategory.new
@member ||= @project.members.new
@trackers = Tracker.all
@trackers = Tracker.sorted.all
@wiki ||= @project.wiki
end
@@ -191,7 +191,7 @@ class ProjectsController < ApplicationController
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'settings', :id => @project
}
format.api { head :ok }
format.api { render_api_ok }
end
else
respond_to do |format|
@@ -224,6 +224,16 @@ class ProjectsController < ApplicationController
redirect_to(url_for(:controller => 'admin', :action => 'projects', :status => params[:status]))
end
def close
@project.close
redirect_to project_path(@project)
end
def reopen
@project.reopen
redirect_to project_path(@project)
end
# Delete @project
def destroy
@project_to_destroy = @project
@@ -231,7 +241,7 @@ class ProjectsController < ApplicationController
@project_to_destroy.destroy
respond_to do |format|
format.html { redirect_to :controller => 'admin', :action => 'projects' }
format.api { head :ok }
format.api { render_api_ok }
end
end
# hide project in layout

View File

@@ -22,7 +22,7 @@ class ReportsController < ApplicationController
def issue_report
@trackers = @project.trackers
@versions = @project.shared_versions.sort
@priorities = IssuePriority.all
@priorities = IssuePriority.all.reverse
@categories = @project.issue_categories
@assignees = (Setting.issue_group_assignment? ? @project.principals : @project.users).sort
@authors = @project.users.sort
@@ -53,7 +53,7 @@ class ReportsController < ApplicationController
@report_title = l(:field_version)
when "priority"
@field = "priority_id"
@rows = IssuePriority.all
@rows = IssuePriority.all.reverse
@data = Issue.by_priority(@project)
@report_title = l(:field_priority)
when "category"

View File

@@ -42,12 +42,12 @@ class RepositoriesController < ApplicationController
@repository = Repository.factory(scm)
@repository.is_default = @project.repository.nil?
@repository.project = @project
render :layout => !request.xhr?
end
def create
attrs = pickup_extra_info
@repository = Repository.factory(params[:repository_scm], attrs[:attrs])
@repository = Repository.factory(params[:repository_scm])
@repository.safe_attributes = params[:repository]
if attrs[:attrs_extra].keys.any?
@repository.merge_extra_info(attrs[:attrs_extra])
end
@@ -64,7 +64,7 @@ class RepositoriesController < ApplicationController
def update
attrs = pickup_extra_info
@repository.attributes = attrs[:attrs]
@repository.safe_attributes = attrs[:attrs]
if attrs[:attrs_extra].keys.any?
@repository.merge_extra_info(attrs[:attrs_extra])
end
@@ -176,6 +176,7 @@ class RepositoriesController < ApplicationController
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_opt[:disposition] = (Redmine::MimeType.is_type?('image', @path) && !is_raw ? 'inline' : 'attachment')
send_data @content, send_opt
else
# Prevent empty lines when displaying a file with Windows style eol
@@ -234,22 +235,6 @@ class RepositoriesController < ApplicationController
if @issue
@changeset.issues << @issue
respond_to do |format|
format.js {
render :update do |page|
page.replace_html "related-issues", :partial => "related_issues"
page.visual_effect :highlight, "related-issue-#{@issue.id}"
end
}
end
else
respond_to do |format|
format.js {
render :update do |page|
page.alert(l(:label_issue) + ' ' + l('activerecord.errors.messages.invalid'))
end
}
end
end
end
@@ -260,14 +245,6 @@ class RepositoriesController < ApplicationController
if @issue
@changeset.issues.delete(@issue)
end
respond_to do |format|
format.js {
render :update do |page|
page.remove "related-issue-#{@issue.id}"
end if @issue
}
end
end
def diff
@@ -454,4 +431,3 @@ class RepositoriesController < ApplicationController
graph.burn
end
end

View File

@@ -36,8 +36,11 @@ class RolesController < ApplicationController
end
def new
# Prefills the form with 'Non member' role permissions
# Prefills the form with 'Non member' role permissions by default
@role = Role.new(params[:role] || {:permissions => Role.non_member.permissions})
if params[:copy].present? && @copy_from = Role.find_by_id(params[:copy])
@role.copy_from(@copy_from)
end
@roles = Role.sorted.all
end
@@ -46,7 +49,7 @@ class RolesController < ApplicationController
if request.post? && @role.save
# workflow copy
if !params[:copy_workflow_from].blank? && (copy_from = Role.find_by_id(params[:copy_workflow_from]))
@role.workflows.copy(copy_from)
@role.workflow_rules.copy(copy_from)
end
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'index'

View File

@@ -171,7 +171,7 @@ class TimelogController < ApplicationController
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
}
format.api { head :ok }
format.api { render_api_ok }
end
else
respond_to do |format|
@@ -223,7 +223,7 @@ class TimelogController < ApplicationController
}
format.api {
if destroyed
head :ok
render_api_ok
else
render_validation_errors(@time_entries)
end

View File

@@ -29,7 +29,7 @@ class TrackersController < ApplicationController
render :action => "index", :layout => false if request.xhr?
}
format.api {
@trackers = Tracker.all
@trackers = Tracker.sorted.all
}
end
end
@@ -45,7 +45,7 @@ class TrackersController < ApplicationController
if request.post? and @tracker.save
# workflow copy
if !params[:copy_workflow_from].blank? && (copy_from = Tracker.find_by_id(params[:copy_workflow_from]))
@tracker.workflows.copy(copy_from)
@tracker.workflow_rules.copy(copy_from)
end
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'index'
@@ -80,4 +80,22 @@ class TrackersController < ApplicationController
end
redirect_to :action => 'index'
end
def fields
if request.post? && params[:trackers]
params[:trackers].each do |tracker_id, tracker_params|
tracker = Tracker.find_by_id(tracker_id)
if tracker
tracker.core_fields = tracker_params[:core_fields]
tracker.custom_field_ids = tracker_params[:custom_field_ids]
tracker.save
end
end
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'fields'
return
end
@trackers = Tracker.sorted.all
@custom_fields = IssueCustomField.all.sort
end
end

View File

@@ -103,7 +103,7 @@ class UsersController < ApplicationController
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_create)
flash[:notice] = l(:notice_user_successful_create, :id => view_context.link_to(@user.login, user_path(@user)))
redirect_to(params[:continue] ?
{:controller => 'users', :action => 'new'} :
{:controller => 'users', :action => 'edit', :id => @user}
@@ -156,7 +156,7 @@ class UsersController < ApplicationController
flash[:notice] = l(:notice_successful_update)
redirect_to_referer_or edit_user_path(@user)
}
format.api { head :ok }
format.api { render_api_ok }
end
else
@auth_sources = AuthSource.find(:all)
@@ -174,8 +174,8 @@ class UsersController < ApplicationController
def destroy
@user.destroy
respond_to do |format|
format.html { redirect_to_referer_or(users_url) }
format.api { head :ok }
format.html { redirect_back_or_default(users_url) }
format.api { render_api_ok }
end
end
@@ -183,21 +183,8 @@ class UsersController < ApplicationController
@membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
@membership.save
respond_to do |format|
if @membership.valid?
format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
format.js {
render(:update) {|page|
page.replace_html "tab-content-memberships", :partial => 'users/memberships'
page.visual_effect(:highlight, "member-#{@membership.id}")
}
}
else
format.js {
render(:update) {|page|
page.alert(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', ')))
}
}
end
format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
format.js
end
end
@@ -208,7 +195,7 @@ class UsersController < ApplicationController
end
respond_to do |format|
format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'users/memberships'} }
format.js
end
end

View File

@@ -78,13 +78,7 @@ class VersionsController < ApplicationController
respond_to do |format|
format.html
format.js do
render :update do |page|
page.replace_html 'ajax-modal', :partial => 'versions/new_modal'
page << "showModal('ajax-modal', '600px');"
page << "Form.Element.focus('version_name');"
end
end
format.js
end
end
@@ -103,14 +97,7 @@ class VersionsController < ApplicationController
flash[:notice] = l(:notice_successful_create)
redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
end
format.js do
render(:update) {|page|
page << 'hideModal();'
# IE doesn't support the replace_html rjs method for select box options
page.replace "issue_fixed_version_id",
content_tag('select', content_tag('option') + version_options_for_select(@project.shared_versions.open, @version), :id => 'issue_fixed_version_id', :name => 'issue[fixed_version_id]')
}
end
format.js
format.api do
render :action => 'show', :status => :created, :location => version_url(@version)
end
@@ -118,12 +105,7 @@ class VersionsController < ApplicationController
else
respond_to do |format|
format.html { render :action => 'new' }
format.js do
render :update do |page|
page.replace_html 'ajax-modal', :partial => 'versions/new_modal'
page << "Form.Element.focus('version_name');"
end
end
format.js { render :action => 'new' }
format.api { render_validation_errors(@version) }
end
end
@@ -144,7 +126,7 @@ class VersionsController < ApplicationController
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
}
format.api { head :ok }
format.api { render_api_ok }
end
else
respond_to do |format|
@@ -167,7 +149,7 @@ class VersionsController < ApplicationController
@version.destroy
respond_to do |format|
format.html { redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project }
format.api { head :ok }
format.api { render_api_ok }
end
else
respond_to do |format|
@@ -183,7 +165,7 @@ class VersionsController < ApplicationController
def status_by
respond_to do |format|
format.html { render :action => 'show' }
format.js { render(:update) {|page| page.replace_html 'status_by', render_issue_status_by(@version, params[:status_by])} }
format.js
end
end

View File

@@ -33,15 +33,6 @@ class WatchersController < ApplicationController
end
def new
respond_to do |format|
format.js do
render :update do |page|
page.replace_html 'ajax-modal', :partial => 'watchers/new', :locals => {:watched => @watched}
page << "showModal('ajax-modal', '400px');"
page << "$('ajax-modal').addClassName('new-watcher');"
end
end
end
end
def create
@@ -53,29 +44,14 @@ class WatchersController < ApplicationController
end
respond_to do |format|
format.html { redirect_to_referer_or {render :text => 'Watcher added.', :layout => true}}
format.js do
render :update do |page|
page.replace_html 'ajax-modal', :partial => 'watchers/new', :locals => {:watched => @watched}
page.replace_html 'watchers', :partial => 'watchers/watchers', :locals => {:watched => @watched}
end
end
format.js
end
end
def append
if params[:watcher].is_a?(Hash)
user_ids = params[:watcher][:user_ids] || [params[:watcher][:user_id]]
users = User.active.find_all_by_id(user_ids)
respond_to do |format|
format.js do
render :update do |page|
users.each do |user|
page << %|$$("#issue_watcher_user_ids_#{user.id}").each(function(el){el.remove();});|
end
page.insert_html :bottom, 'watchers_inputs', :text => watchers_checkboxes(nil, users, true)
end
end
end
@users = User.active.find_all_by_id(user_ids)
end
end
@@ -83,11 +59,7 @@ class WatchersController < ApplicationController
@watched.set_watcher(User.find(params[:user_id]), false) if request.post?
respond_to do |format|
format.html { redirect_to :back }
format.js do
render :update do |page|
page.replace_html 'watchers', :partial => 'watchers/watchers', :locals => {:watched => @watched}
end
end
format.js
end
end
@@ -117,12 +89,7 @@ private
@watched.set_watcher(user, watching)
respond_to do |format|
format.html { redirect_to_referer_or {render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true}}
format.js do
render(:update) do |page|
c = watcher_css(@watched)
page << %|$$(".#{c}").each(function(el){el.innerHTML="#{escape_javascript watcher_link(@watched, user)}"});|
end
end
format.js { render :partial => 'set_watcher', :locals => {:user => user, :watched => @watched} }
end
end
end

View File

@@ -109,7 +109,7 @@ class WikiController < ApplicationController
# To prevent StaleObjectError exception when reverting to a previous version
@content.version = @page.content.version
@text = @content.text
if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
@section = params[:section].to_i

View File

@@ -24,7 +24,6 @@ class WikisController < ApplicationController
@wiki = @project.wiki || Wiki.new(:project => @project)
@wiki.safe_attributes = params[:wiki]
@wiki.save if request.post?
render(:update) {|page| page.replace_html "tab-content-wiki", :partial => 'projects/settings/wiki'}
end
# Delete a project's wiki

View File

@@ -18,30 +18,27 @@
class WorkflowsController < ApplicationController
layout 'admin'
before_filter :require_admin
before_filter :find_roles
before_filter :find_trackers
before_filter :require_admin, :find_roles, :find_trackers
def index
@workflow_counts = Workflow.count_by_tracker_and_role
@workflow_counts = WorkflowTransition.count_by_tracker_and_role
end
def edit
@role = Role.find_by_id(params[:role_id])
@tracker = Tracker.find_by_id(params[:tracker_id])
@role = Role.find_by_id(params[:role_id]) if params[:role_id]
@tracker = Tracker.find_by_id(params[:tracker_id]) if params[:tracker_id]
if request.post?
Workflow.destroy_all( ["role_id=? and tracker_id=?", @role.id, @tracker.id])
WorkflowTransition.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)
WorkflowTransition.create(:role_id => @role.id, :tracker_id => @tracker.id, :old_status_id => status_id, :new_status_id => new_status_id, :author => author, :assignee => assignee)
}
}
if @role.save
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'edit', :role_id => @role, :tracker_id => @tracker
redirect_to :action => 'edit', :role_id => @role, :tracker_id => @tracker, :used_statuses_only => params[:used_statuses_only]
return
end
end
@@ -50,10 +47,10 @@ class WorkflowsController < ApplicationController
if @tracker && @used_statuses_only && @tracker.issue_statuses.any?
@statuses = @tracker.issue_statuses
end
@statuses ||= IssueStatus.find(:all, :order => 'position')
@statuses ||= IssueStatus.sorted.all
if @tracker && @role && @statuses.any?
workflows = Workflow.all(:conditions => {:role_id => @role.id, :tracker_id => @tracker.id})
workflows = WorkflowTransition.where(:role_id => @role.id, :tracker_id => @tracker.id).all
@workflows = {}
@workflows['always'] = workflows.select {|w| !w.author && !w.assignee}
@workflows['author'] = workflows.select {|w| w.author}
@@ -61,6 +58,35 @@ class WorkflowsController < ApplicationController
end
end
def permissions
@role = Role.find_by_id(params[:role_id]) if params[:role_id]
@tracker = Tracker.find_by_id(params[:tracker_id]) if params[:tracker_id]
if request.post? && @role && @tracker
WorkflowPermission.replace_permissions(@tracker, @role, params[:permissions] || {})
redirect_to :action => 'permissions', :role_id => @role, :tracker_id => @tracker, :used_statuses_only => params[:used_statuses_only]
return
end
@used_statuses_only = (params[:used_statuses_only] == '0' ? false : true)
if @tracker && @used_statuses_only && @tracker.issue_statuses.any?
@statuses = @tracker.issue_statuses
end
@statuses ||= IssueStatus.sorted.all
if @role && @tracker
@fields = (Tracker::CORE_FIELDS_ALL - @tracker.disabled_core_fields).map {|field| [field, l("field_"+field.sub(/_id$/, ''))]}
@custom_fields = @tracker.custom_fields
@permissions = WorkflowPermission.where(:tracker_id => @tracker.id, :role_id => @role.id).all.inject({}) do |h, w|
h[w.old_status_id] ||= {}
h[w.old_status_id][w.field_name] = w.rule
h
end
@statuses.each {|status| @permissions[status.id] ||= {}}
end
end
def copy
if params[:source_tracker_id].blank? || params[:source_tracker_id] == 'any'
@@ -83,7 +109,7 @@ class WorkflowsController < ApplicationController
elsif @target_trackers.nil? || @target_roles.nil?
flash.now[:error] = l(:error_workflow_copy_target)
else
Workflow.copy(@source_tracker, @source_role, @target_trackers, @target_roles)
WorkflowRule.copy(@source_tracker, @source_role, @target_trackers, @target_roles)
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'copy', :source_tracker_id => @source_tracker, :source_role_id => @source_role
end
@@ -93,10 +119,10 @@ class WorkflowsController < ApplicationController
private
def find_roles
@roles = Role.find(:all, :order => 'builtin, position')
@roles = Role.sorted.all
end
def find_trackers
@trackers = Tracker.find(:all, :order => 'position')
@trackers = Tracker.sorted.all
end
end

View File

@@ -20,6 +20,8 @@
module AdminHelper
def project_status_options_for_select(selected)
options_for_select([[l(:label_all), ''],
[l(:status_active), '1']], selected.to_s)
[l(:project_status_active), '1'],
[l(:project_status_closed), '5'],
[l(:project_status_archived), '9']], selected.to_s)
end
end

View File

@@ -43,12 +43,6 @@ module ApplicationHelper
link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
end
# Display a link to remote if user is authorized
def link_to_remote_if_authorized(name, options = {}, html_options = nil)
url = options[:url] || {}
link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
end
# Displays a link to user's account page if active
def link_to_user(user, options={})
if user.is_a?(User)
@@ -128,7 +122,7 @@ module ApplicationHelper
h(truncate(message.subject, :length => 60)),
{ :controller => 'messages', :action => 'show',
:board_id => message.board_id,
:id => message.root,
:id => (message.parent_id || message.id),
:r => (message.parent_id && message.id),
:anchor => (message.parent_id ? "message-#{message.id}" : nil)
}.merge(options),
@@ -145,17 +139,23 @@ module ApplicationHelper
# link_to_project(project, {}, :class => "project") # => html options with default url (project overview)
#
def link_to_project(project, options={}, html_options = nil)
if project.active?
if project.archived?
h(project)
else
url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
link_to(h(project), url, html_options)
else
h(project)
end
end
def thumbnail_tag(attachment)
link_to image_tag(url_for(:controller => 'attachments', :action => 'thumbnail', :id => attachment)),
{:controller => 'attachments', :action => 'show', :id => attachment, :filename => attachment.filename},
:title => attachment.filename
end
def toggle_link(name, id, options={})
onclick = "Element.toggle('#{id}'); "
onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
onclick = "$('##{id}').toggle(); "
onclick << (options[:focus] ? "$('##{options[:focus]}').focus(); " : "this.blur(); ")
onclick << "return false;"
link_to(name, "#", :onclick => onclick)
end
@@ -168,11 +168,6 @@ module ApplicationHelper
}))
end
def prompt_to_remote(name, text, param, url, html_options = {})
html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
link_to name, {}, html_options
end
def format_activity_title(text)
h(truncate_single_line(text, :length => 100))
end
@@ -200,6 +195,39 @@ module ApplicationHelper
end
end
# Renders a tree of projects as a nested set of unordered lists
# The given collection may be a subset of the whole project tree
# (eg. some intermediate nodes are private and can not be seen)
def render_project_nested_lists(projects)
s = ''
if projects.any?
ancestors = []
original_project = @project
projects.sort_by(&:lft).each do |project|
# set the project environment to please macros.
@project = project
if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>\n"
else
ancestors.pop
s << "</li>"
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
ancestors.pop
s << "</ul></li>\n"
end
end
classes = (ancestors.empty? ? 'root' : 'child')
s << "<li class='#{classes}'><div class='#{classes}'>"
s << h(block_given? ? yield(project) : project.name)
s << "</div>\n"
ancestors << project
end
s << ("</li></ul>\n" * ancestors.size)
@project = original_project
end
s.html_safe
end
def render_page_hierarchy(pages, node=nil, options={})
content = ''
if pages[node]
@@ -237,23 +265,24 @@ 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
projects = User.current.memberships.collect(&:project).compact.select(&:active?).uniq
if projects.any?
s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
"<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
'<option value="" disabled="disabled">---</option>'
s << project_tree_options_for_select(projects, :selected => @project) do |p|
{ :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
options =
("<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
'<option value="" disabled="disabled">---</option>').html_safe
options << project_tree_options_for_select(projects, :selected => @project) do |p|
{ :value => project_path(:id => p, :jump => current_menu_item) }
end
s << '</select>'
s.html_safe
select_tag('project_quick_jump_box', options, :onchange => 'if (this.value != \'\') { window.location = this.value; }')
end
end
def project_tree_options_for_select(projects, options = {})
s = ''
project_tree(projects) do |project, level|
name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '').html_safe
name_prefix = (level > 0 ? '&nbsp;' * 2 * level + '&#187; ' : '').html_safe
tag_options = {:value => project.id}
if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project))
tag_options[:selected] = 'selected'
@@ -273,30 +302,6 @@ module ApplicationHelper
Project.project_tree(projects, &block)
end
def project_nested_ul(projects, &block)
s = ''
if projects.any?
ancestors = []
projects.sort_by(&:lft).each do |project|
if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
s << "<ul>\n"
else
ancestors.pop
s << "</li>"
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
ancestors.pop
s << "</ul></li>\n"
end
end
s << "<li>"
s << yield(project).to_s
ancestors << project
end
s << ("</li></ul>\n" * ancestors.size)
end
s.html_safe
end
def principals_check_box_tags(name, principals)
s = ''
principals.sort.each do |principal|
@@ -309,7 +314,7 @@ module ApplicationHelper
def principals_options_for_select(collection, selected=nil)
s = ''
if collection.include?(User.current)
s << content_tag('option', "<< #{l(:label_me)} >>".html_safe, :value => User.current.id)
s << content_tag('option', "<< #{l(:label_me)} >>", :value => User.current.id)
end
groups = ''
collection.sort.each do |element|
@@ -522,6 +527,8 @@ module ApplicationHelper
project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
only_path = options.delete(:only_path) == false ? false : true
text = text.dup
macros = catch_macros(text)
text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr)
@parsed_headings = []
@@ -529,8 +536,8 @@ module ApplicationHelper
@current_section = 0 if options[:edit_section_links]
parse_sections(text, project, obj, attr, only_path, options)
text = parse_non_pre_blocks(text) do |text|
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_macros].each do |method_name|
text = parse_non_pre_blocks(text, obj, macros) do |text|
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links].each do |method_name|
send method_name, text, project, obj, attr, only_path, options
end
end
@@ -543,7 +550,7 @@ module ApplicationHelper
text.html_safe
end
def parse_non_pre_blocks(text)
def parse_non_pre_blocks(text, obj, macros)
s = StringScanner.new(text)
tags = []
parsed = ''
@@ -552,6 +559,9 @@ module ApplicationHelper
text, full_tag, closing, tag = s[1], s[2], s[3], s[4]
if tags.empty?
yield text
inject_macros(text, obj, macros) if macros.any?
else
inject_macros(text, obj, macros, false) if macros.any?
end
parsed << text
if tag
@@ -707,7 +717,7 @@ module ApplicationHelper
oid = identifier.to_i
case prefix
when nil
if issue = Issue.visible.find_by_id(oid, :include => :status)
if oid.to_s == identifier && issue = Issue.visible.find_by_id(oid, :include => :status)
anchor = comment_id ? "note-#{comment_id}" : nil
link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid, :anchor => anchor},
:class => issue.css_classes,
@@ -851,31 +861,57 @@ module ApplicationHelper
end
end
MACROS_RE = /
MACROS_RE = /(
(!)? # escaping
(
\{\{ # opening tag
([\w]+) # macro name
(\(([^\}]*)\))? # optional arguments
(\(([^\n\r]*?)\))? # optional arguments
([\n\r].*?[\n\r])? # optional block of text
\}\} # closing tag
)
/x unless const_defined?(:MACROS_RE)
)/mx unless const_defined?(:MACROS_RE)
# Macros substitution
def parse_macros(text, project, obj, attr, only_path, options)
MACRO_SUB_RE = /(
\{\{
macro\((\d+)\)
\}\}
)/x unless const_defined?(:MACRO_SUB_RE)
# Extracts macros from text
def catch_macros(text)
macros = {}
text.gsub!(MACROS_RE) do
esc, all, macro = $1, $2, $3.downcase
args = ($5 || '').split(',').each(&:strip)
if esc.nil?
begin
exec_macro(macro, obj, args)
rescue => e
"<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
end || all
all, macro = $1, $4.downcase
if macro_exists?(macro) || all =~ MACRO_SUB_RE
index = macros.size
macros[index] = all
"{{macro(#{index})}}"
else
all
end
end
macros
end
# Executes and replaces macros in text
def inject_macros(text, obj, macros, execute=true)
text.gsub!(MACRO_SUB_RE) do
all, index = $1, $2.to_i
orig = macros.delete(index)
if execute && orig && orig =~ MACROS_RE
esc, all, macro, args, block = $2, $3, $4.downcase, $6.to_s, $7.try(:strip)
if esc.nil?
h(exec_macro(macro, obj, args, block) || all)
else
h(all)
end
elsif orig
h(orig)
else
h(all)
end
end
end
TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
@@ -932,16 +968,6 @@ module ApplicationHelper
content_tag("label", label_text)
end
def labelled_tabular_form_for(*args, &proc)
ActiveSupport::Deprecation.warn "ApplicationHelper#labelled_tabular_form_for is deprecated and will be removed in Redmine 1.5. Use #labelled_form_for instead."
args << {} unless args.last.is_a?(Hash)
options = args.last
options[:html] ||= {}
options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
form_for(*args, &proc)
end
def labelled_form_for(*args, &proc)
args << {} unless args.last.is_a?(Hash)
options = args.last
@@ -960,6 +986,7 @@ module ApplicationHelper
end
def labelled_remote_form_for(*args, &proc)
ActiveSupport::Deprecation.warn "ApplicationHelper#labelled_remote_form_for is deprecated and will be removed in Redmine 2.2."
args << {} unless args.last.is_a?(Hash)
options = args.last
options.merge!({:builder => Redmine::Views::LabelledFormBuilder, :remote => true})
@@ -980,10 +1007,39 @@ module ApplicationHelper
html.html_safe
end
def delete_link(url, options={})
options = {
:method => :delete,
:data => {:confirm => l(:text_are_you_sure)},
:class => 'icon icon-del'
}.merge(options)
link_to l(:button_delete), url, options
end
def preview_link(url, form, target='preview', options={})
content_tag 'a', l(:label_preview), {
:href => "#",
:onclick => %|submitPreview("#{escape_javascript url_for(url)}", "#{escape_javascript form}", "#{escape_javascript target}"); return false;|,
:accesskey => accesskey(:preview)
}.merge(options)
end
def link_to_function(name, function, html_options={})
content_tag(:a, name, {:href => '#', :onclick => "#{function}; return false;"}.merge(html_options))
end
def back_url
url = params[:back_url]
if url.nil? && referer = request.env['HTTP_REFERER']
url = CGI.unescape(referer.to_s)
end
url
end
def back_url_hidden_field_tag
back_url = params[:back_url] || request.env['HTTP_REFERER']
back_url = CGI.unescape(back_url.to_s)
hidden_field_tag('back_url', CGI.escape(back_url), :id => nil) unless back_url.blank?
url = back_url
hidden_field_tag('back_url', url, :id => nil) unless url.blank?
end
def check_all_links(form_name)
@@ -1027,35 +1083,34 @@ module ApplicationHelper
end
@context_menu_included = true
end
javascript_tag "new ContextMenu('#{ url_for(url) }')"
javascript_tag "contextMenuInit('#{ url_for(url) }')"
end
def calendar_for(field_id)
include_calendar_headers_tags
image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
javascript_tag("$(function() { $('##{field_id}').datepicker(datepickerOptions); });")
end
def include_calendar_headers_tags
unless @calendar_headers_tags_included
@calendar_headers_tags_included = true
content_for :header_tags do
start_of_week = case Setting.start_of_week.to_i
when 1
'Calendar._FD = 1;' # Monday
when 7
'Calendar._FD = 0;' # Sunday
when 6
'Calendar._FD = 6;' # Saturday
else
'' # use language
end
start_of_week = Setting.start_of_week
start_of_week = l(:general_first_day_of_week, :default => '1') if start_of_week.blank?
# Redmine uses 1..7 (monday..sunday) in settings and locales
# JQuery uses 0..6 (sunday..saturday), 7 needs to be changed to 0
start_of_week = start_of_week.to_i % 7
javascript_include_tag('calendar/calendar') +
javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
javascript_tag(start_of_week) +
javascript_include_tag('calendar/calendar-setup') +
stylesheet_link_tag('calendar')
tags = javascript_tag(
"var datepickerOptions={dateFormat: 'yy-mm-dd', firstDay: #{start_of_week}, " +
"showOn: 'button', buttonImageOnly: true, buttonImage: '" +
path_to_image('/images/calendar.png') +
"', showButtonPanel: true};")
jquery_locale = l('jquery.locale', :default => current_language.to_s)
unless jquery_locale == 'en'
tags << javascript_include_tag("i18n/jquery.ui.datepicker-#{jquery_locale}.js")
end
tags
end
end
end
@@ -1153,14 +1208,19 @@ module ApplicationHelper
end
def sanitize_anchor_name(anchor)
anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
if ''.respond_to?(:encoding) || RUBY_PLATFORM == 'java'
anchor.gsub(%r{[^\p{Word}\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
else
# TODO: remove when ruby1.8 is no longer supported
anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
end
end
# Returns the javascript tags that are included in the html layout head
def javascript_heads
tags = javascript_include_tag('prototype', 'effects', 'dragdrop', 'controls', 'rails', 'application')
tags = javascript_include_tag('jquery-1.7.2-ui-1.8.21-ujs-2.0.3', 'application')
unless User.current.pref.warn_on_leaving_unsaved == '0'
tags << "\n".html_safe + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
tags << "\n".html_safe + javascript_tag("$(window).load(function(){ warnLeavingUnsaved('#{escape_javascript l(:text_warn_on_leaving_unsaved)}'); });")
end
tags
end

View File

@@ -21,12 +21,14 @@ module AttachmentsHelper
# Displays view/delete links to the attachments of the given object
# Options:
# :author -- author names are not displayed if set to false
# :thumbails -- display thumbnails if enabled in settings
def link_to_attachments(container, options = {})
options.assert_valid_keys(:author)
options.assert_valid_keys(:author, :thumbnails)
if container.attachments.any?
options = {:deletable => container.attachments_deletable?, :author => true}.merge(options)
render :partial => 'attachments/links', :locals => {:attachments => container.attachments, :options => options}
render :partial => 'attachments/links',
:locals => {:attachments => container.attachments, :options => options, :thumbnails => (options[:thumbnails] && Setting.thumbnails_enabled?)}
end
end

View File

@@ -18,4 +18,24 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module BoardsHelper
def board_breadcrumb(item)
board = item.is_a?(Message) ? item.board : item
links = [link_to(l(:label_board_plural), project_boards_path(item.project))]
boards = board.ancestors.reverse
if item.is_a?(Message)
boards << board
end
links += boards.map {|ancestor| link_to(h(ancestor.name), project_board_path(ancestor.project, ancestor))}
breadcrumb links
end
def boards_options_for_select(boards)
options = []
Board.board_tree(boards) do |board, level|
label = (level > 0 ? '&nbsp;' * 2 * level + '&#187; ' : '').html_safe
label << board.name
options << [label, board.id]
end
options
end
end

View File

@@ -36,7 +36,7 @@ module ContextMenusHelper
def bulk_update_custom_field_context_menu_link(field, text, value)
context_menu_link h(text),
{:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'custom_field_values' => {field.id => value}}, :back_url => @back},
{:controller => 'issues', :action => 'bulk_update', :ids => @issue_ids, :issue => {'custom_field_values' => {field.id => value}}, :back_url => @back},
:method => :post,
:selected => (@issue && @issue.custom_field_value(field) == value)
end

View File

@@ -73,15 +73,17 @@ module CustomFieldsHelper
end
# Return custom field label tag
def custom_field_label_tag(name, custom_value)
def custom_field_label_tag(name, custom_value, options={})
required = options[:required] || custom_value.custom_field.is_required?
content_tag "label", h(custom_value.custom_field.name) +
(custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>".html_safe : ""),
:for => "#{name}_custom_field_values_#{custom_value.custom_field.id}"
(required ? " <span class=\"required\">*</span>".html_safe : ""),
:for => "#{name}_custom_field_values_#{custom_value.custom_field.id}"
end
# Return custom field tag with its label tag
def custom_field_tag_with_label(name, custom_value)
custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
def custom_field_tag_with_label(name, custom_value, options={})
custom_field_label_tag(name, custom_value, options) + custom_field_tag(name, custom_value)
end
def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil)

View File

@@ -92,6 +92,48 @@ module IssuesHelper
s.html_safe
end
class IssueFieldsRows
include ActionView::Helpers::TagHelper
def initialize
@left = []
@right = []
end
def left(*args)
args.any? ? @left << cells(*args) : @left
end
def right(*args)
args.any? ? @right << cells(*args) : @right
end
def size
@left.size > @right.size ? @left.size : @right.size
end
def to_html
html = ''.html_safe
blank = content_tag('th', '') + content_tag('td', '')
size.times do |i|
left = @left[i] || blank
right = @right[i] || blank
html << content_tag('tr', left + right)
end
html
end
def cells(label, text, options={})
content_tag('th', "#{label}:", options) + content_tag('td', text, options)
end
end
def issue_fields_rows
r = IssueFieldsRows.new
yield r
r.to_html
end
def render_custom_fields_rows(issue)
return if issue.custom_field_values.empty?
ordered_values = []
@@ -248,7 +290,7 @@ module IssuesHelper
unless no_html
label = content_tag('strong', label)
old_value = content_tag("i", h(old_value)) if detail.old_value
old_value = content_tag("strike", old_value) if detail.old_value and detail.value.blank?
old_value = content_tag("del", old_value) if detail.old_value and detail.value.blank?
if detail.property == 'attachment' && !value.blank? && atta = Attachment.find_by_id(detail.prop_key)
# Link to the attachment if it has not been removed
value = link_to_attachment(atta, :download => true, :only_path => options[:only_path])

View File

@@ -23,11 +23,13 @@ module JournalsHelper
editable = User.current.logged? && (User.current.allowed_to?(:edit_issue_notes, issue.project) || (journal.user == User.current && User.current.allowed_to?(:edit_own_issue_notes, issue.project)))
links = []
if !journal.notes.blank?
links << link_to_remote(image_tag('comment.png'),
{ :url => {:controller => 'journals', :action => 'new', :id => issue, :journal_id => journal} },
:title => l(:button_quote)) if options[:reply_links]
links << link_to(image_tag('comment.png'),
{:controller => 'journals', :action => 'new', :id => issue, :journal_id => journal},
:remote => true,
:method => 'post',
:title => l(:button_quote)) if options[:reply_links]
links << link_to_in_place_notes_editor(image_tag('edit.png'), "journal-#{journal.id}-notes",
{ :controller => 'journals', :action => 'edit', :id => journal },
{ :controller => 'journals', :action => 'edit', :id => journal, :format => 'js' },
:title => l(:button_edit)) if editable
end
content << content_tag('div', links.join(' ').html_safe, :class => 'contextual') unless links.empty?
@@ -38,7 +40,7 @@ module JournalsHelper
end
def link_to_in_place_notes_editor(text, field_id, url, options={})
onclick = "new Ajax.Request('#{url_for(url)}', {asynchronous:true, evalScripts:true, method:'get'}); return false;"
onclick = "$.ajax({url: '#{url_for(url)}', type: 'get'}); return false;"
link_to text, '#', options.merge(:onclick => onclick)
end
end

View File

@@ -51,38 +51,15 @@ module ProjectsHelper
content_tag('select', options.html_safe, :name => 'project[parent_id]', :id => 'project_parent_id')
end
# Renders a tree of projects as a nested set of unordered lists
# The given collection may be a subset of the whole project tree
# (eg. some intermediate nodes are private and can not be seen)
# Renders the projects index
def render_project_hierarchy(projects)
s = ''
if projects.any?
ancestors = []
original_project = @project
projects.each do |project|
# set the project environment to please macros.
@project = project
if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>\n"
else
ancestors.pop
s << "</li>"
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
ancestors.pop
s << "</ul></li>\n"
end
end
classes = (ancestors.empty? ? 'root' : 'child')
s << "<li class='#{classes}'><div class='#{classes}'>" +
link_to_project(project, {}, :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}")
s << "<div class='wiki description'>#{textilizable(project.short_description, :project => project)}</div>" unless project.description.blank?
s << "</div>\n"
ancestors << project
render_project_nested_lists(projects) do |project|
s = link_to_project(project, {}, :class => "#{project.css_classes} #{User.current.member_of?(project) ? 'my-project' : nil}")
if project.description.present?
s << content_tag('div', textilizable(project.short_description, :project => project), :class => 'wiki description')
end
s << ("</li></ul>\n" * ancestors.size)
@project = original_project
s
end
s.html_safe
end
# Returns a set of options for a select field, grouped by project.
@@ -91,10 +68,6 @@ module ProjectsHelper
versions.each do |version|
grouped[version.project.name] << [version.name, version.id]
end
# Add in the selected
if selected && !versions.include?(selected)
grouped[selected.project.name] << [selected.name, selected.id]
end
if grouped.keys.size > 1
grouped_options_for_select(grouped, selected && selected.id)

View File

@@ -18,9 +18,12 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module QueriesHelper
def operators_for_select(filter_type)
Query.operators_by_filter_type[filter_type].collect {|o| [l(Query.operators[o]), o]}
def filters_options_for_select(query)
options = [[]]
options += query.available_filters.sort {|a,b| a[1][:order] <=> b[1][:order]}.map do |field, field_options|
[field_options[:name], field]
end
options_for_select(options)
end
def column_header(column)

View File

@@ -141,12 +141,7 @@ module RepositoriesHelper
select_tag('repository_scm',
options_for_select(scm_options, repository.class.name.demodulize),
:disabled => (repository && !repository.new_record?),
:onchange => remote_function(
:url => new_project_repository_path(@project),
:method => :get,
:update => 'content',
:with => "Form.serialize(this.form)")
)
:data => {:remote => true, :method => 'get'})
end
def with_leading_slash(path)
@@ -159,7 +154,7 @@ module RepositoriesHelper
def subversion_field_tags(form, repository)
content_tag('p', form.text_field(:url, :size => 60, :required => true,
:disabled => (repository && !repository.root_url.blank?)) +
:disabled => !repository.safe_attribute?('url')) +
'<br />'.html_safe +
'(file:///, http://, https://, svn://, svn+[tunnelscheme]://)') +
content_tag('p', form.text_field(:login, :size => 30)) +
@@ -174,7 +169,7 @@ module RepositoriesHelper
content_tag('p', form.text_field(
:url, :label => l(:field_path_to_repository),
:size => 60, :required => true,
:disabled => (repository && !repository.new_record?))) +
:disabled => !repository.safe_attribute?('url'))) +
content_tag('p', form.select(
:log_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_commit_logs_encoding), :required => true))
@@ -184,7 +179,7 @@ module RepositoriesHelper
content_tag('p', form.text_field(
:url, :label => l(:field_path_to_repository),
:size => 60, :required => true,
:disabled => (repository && !repository.root_url.blank?)
:disabled => !repository.safe_attribute?('url')
) +
'<br />'.html_safe + l(:text_mercurial_repository_note)) +
content_tag('p', form.select(
@@ -198,7 +193,7 @@ module RepositoriesHelper
content_tag('p', form.text_field(
:url, :label => l(:field_path_to_repository),
:size => 60, :required => true,
:disabled => (repository && !repository.root_url.blank?)
:disabled => !repository.safe_attribute?('url')
) +
'<br />'.html_safe +
l(:text_git_repository_note)) +
@@ -218,12 +213,12 @@ module RepositoriesHelper
:root_url,
:label => l(:field_cvsroot),
:size => 60, :required => true,
:disabled => !repository.new_record?)) +
:disabled => !repository.safe_attribute?('root_url'))) +
content_tag('p', form.text_field(
:url,
:label => l(:field_cvs_module),
:size => 30, :required => true,
:disabled => !repository.new_record?)) +
:disabled => !repository.safe_attribute?('url'))) +
content_tag('p', form.select(
:log_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_commit_logs_encoding), :required => true)) +
@@ -238,7 +233,7 @@ module RepositoriesHelper
content_tag('p', form.text_field(
:url, :label => l(:field_path_to_repository),
:size => 60, :required => true,
:disabled => (repository && !repository.new_record?))) +
:disabled => !repository.safe_attribute?('url'))) +
content_tag('p', form.select(
:log_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_commit_logs_encoding), :required => true))
@@ -248,7 +243,7 @@ module RepositoriesHelper
content_tag('p', form.text_field(
:url, :label => l(:field_root_directory),
:size => 60, :required => true,
:disabled => (repository && !repository.root_url.blank?))) +
:disabled => !repository.safe_attribute?('url'))) +
content_tag('p', form.select(
:path_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_scm_path_encoding)

View File

@@ -44,7 +44,7 @@ module SettingsHelper
setting_values = Setting.send(setting)
setting_values = [] unless setting_values.is_a?(Array)
setting_label(setting, options).html_safe +
content_tag("label", l(options[:label] || "setting_#{setting}")) +
hidden_field_tag("settings[#{setting}][]", '').html_safe +
choices.collect do |choice|
text, value = (choice.is_a?(Array) ? choice : [choice, choice])
@@ -53,7 +53,8 @@ module SettingsHelper
check_box_tag(
"settings[#{setting}][]",
value,
Setting.send(setting).include?(value)
Setting.send(setting).include?(value),
:id => nil
) + text.to_s,
:class => 'block'
)
@@ -72,13 +73,13 @@ module SettingsHelper
def setting_check_box(setting, options={})
setting_label(setting, options).html_safe +
hidden_field_tag("settings[#{setting}]", 0).html_safe +
hidden_field_tag("settings[#{setting}]", 0, :id => nil).html_safe +
check_box_tag("settings[#{setting}]", 1, Setting.send("#{setting}?"), options).html_safe
end
def setting_label(setting, options={})
label = options.delete(:label)
label != false ? content_tag("label", l(label || "setting_#{setting}")).html_safe : ''
label != false ? label_tag("settings_#{setting}", l(label || "setting_#{setting}")).html_safe : ''
end
# Renders a notification field for a Redmine::Notifiable option
@@ -86,7 +87,7 @@ module SettingsHelper
return content_tag(:label,
check_box_tag('settings[notified_events][]',
notifiable.name,
Setting.notified_events.include?(notifiable.name)).html_safe +
Setting.notified_events.include?(notifiable.name), :id => nil).html_safe +
l_or_humanize(notifiable.name, :prefix => 'label_').html_safe,
:class => notifiable.parent.present? ? "parent" : '').html_safe
end

View File

@@ -19,10 +19,18 @@
module VersionsHelper
STATUS_BY_CRITERIAS = %w(category tracker status priority author assigned_to)
def version_anchor(version)
if @project == version.project
anchor version.name
else
anchor "#{version.project.try(:identifier)}-#{version.name}"
end
end
STATUS_BY_CRITERIAS = %w(tracker status priority author assigned_to category)
def render_issue_status_by(version, criteria)
criteria = 'category' unless STATUS_BY_CRITERIAS.include?(criteria)
criteria = 'tracker' unless STATUS_BY_CRITERIAS.include?(criteria)
h = Hash.new {|k,v| k[v] = [0, 0]}
begin

View File

@@ -30,10 +30,8 @@ module WatchersHelper
:action => (watched ? 'unwatch' : 'watch'),
:object_type => object.class.to_s.underscore,
:object_id => object.id}
link_to_remote((watched ? l(:button_unwatch) : l(:button_watch)),
{:url => url},
:href => url_for(url),
:class => (watched ? 'icon icon-fav' : 'icon icon-fav-off'))
link_to((watched ? l(:button_unwatch) : l(:button_watch)), url,
:remote => true, :method => 'post', :class => (watched ? 'icon icon-fav' : 'icon icon-fav-off'))
end
@@ -57,11 +55,8 @@ module WatchersHelper
:object_id => object.id,
:user_id => user}
s << ' '
s << link_to_remote(image_tag('delete.png'),
{:url => url},
:href => url_for(url),
:style => "vertical-align: middle",
:class => "delete")
s << link_to(image_tag('delete.png'), url,
:remote => true, :method => 'post', :style => "vertical-align: middle", :class => "delete")
end
content << content_tag('li', s)
end

View File

@@ -18,4 +18,15 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module WorkflowsHelper
def field_required?(field)
field.is_a?(CustomField) ? field.is_required? : %w(project_id tracker_id subject priority_id is_private).include?(field)
end
def field_permission_tag(permissions, status, field)
name = field.is_a?(CustomField) ? field.id.to_s : field
options = [["", ""], [l(:label_readonly), "readonly"]]
options << [l(:label_required), "required"] unless field_required?(field)
select_tag("permissions[#{name}][#{status.id}]", options_for_select(options, permissions[status.id][name]))
end
end

View File

@@ -47,6 +47,9 @@ class Attachment < ActiveRecord::Base
cattr_accessor :storage_path
@@storage_path = Redmine::Configuration['attachments_storage_path'] || File.join(Rails.root, "files")
cattr_accessor :thumbnails_storage_path
@@thumbnails_storage_path = File.join(Rails.root, "tmp", "thumbnails")
before_save :files_to_final_location
after_destroy :delete_from_disk
@@ -124,7 +127,7 @@ class Attachment < ActiveRecord::Base
# Deletes the file from the file system if it's not referenced by other attachments
def delete_from_disk
if Attachment.first(:conditions => ["disk_filename = ? AND id <> ?", disk_filename, id]).nil?
if Attachment.where("disk_filename = ? AND id <> ?", disk_filename, id).empty?
delete_from_disk!
end
end
@@ -134,6 +137,14 @@ class Attachment < ActiveRecord::Base
File.join(self.class.storage_path, disk_filename.to_s)
end
def title
title = filename.to_s
if description.present?
title << " (#{description})"
end
title
end
def increment_download
increment!(:downloads)
end
@@ -151,7 +162,43 @@ class Attachment < ActiveRecord::Base
end
def image?
self.filename =~ /\.(bmp|gif|jpg|jpe|jpeg|png)$/i
!!(self.filename =~ /\.(bmp|gif|jpg|jpe|jpeg|png)$/i)
end
def thumbnailable?
image?
end
# Returns the full path the attachment thumbnail, or nil
# if the thumbnail cannot be generated.
def thumbnail(options={})
if thumbnailable? && readable?
size = options[:size].to_i
if size > 0
# Limit the number of thumbnails per image
size = (size / 50) * 50
# Maximum thumbnail size
size = 800 if size > 800
else
size = Setting.thumbnails_size.to_i
end
size = 100 unless size > 0
target = File.join(self.class.thumbnails_storage_path, "#{id}_#{digest}_#{size}.thumb")
begin
Redmine::Thumbnail.generate(self.diskfile, target, size)
rescue => e
logger.error "An error occured while generating thumbnail for #{disk_filename} to #{target}\nException was: #{e.message}" if logger
return nil
end
end
end
# Deletes all thumbnails
def self.clear_thumbnails
Dir.glob(File.join(thumbnails_storage_path, "*.thumb")).each do |file|
File.delete file
end
end
def is_text?
@@ -176,7 +223,7 @@ class Attachment < ActiveRecord::Base
def self.find_by_token(token)
if token.to_s =~ /^(\d+)\.([0-9a-f]+)$/
attachment_id, attachment_digest = $1, $2
attachment = Attachment.first(:conditions => {:id => attachment_id, :digest => attachment_digest})
attachment = Attachment.where(:id => attachment_id, :digest => attachment_digest).first
if attachment && attachment.container.nil?
attachment
end
@@ -201,8 +248,7 @@ class Attachment < ActiveRecord::Base
end
def self.prune(age=1.day)
attachments = Attachment.all(:conditions => ["created_on < ? AND (container_type IS NULL OR container_type = '')", Time.now - age])
attachments.each(&:destroy)
Attachment.where("created_on < ? AND (container_type IS NULL OR container_type = '')", Time.now - age).destroy_all
end
private

View File

@@ -18,6 +18,7 @@
# Generic exception for when the AuthSource can not be reached
# (eg. can not connect to the LDAP)
class AuthSourceException < Exception; end
class AuthSourceTimeoutException < AuthSourceException; end
class AuthSource < ActiveRecord::Base
include Redmine::SubclassFactory
@@ -58,7 +59,7 @@ class AuthSource < ActiveRecord::Base
# Try to authenticate a user not yet registered against available sources
def self.authenticate(login, password)
AuthSource.find(:all, :conditions => ["onthefly_register=?", true]).each do |source|
AuthSource.where(:onthefly_register => true).all.each do |source|
begin
logger.debug "Authenticating '#{login}' against '#{source.name}'" if logger && logger.debug?
attrs = source.authenticate(login, password)

View File

@@ -18,6 +18,7 @@
require 'iconv'
require 'net/ldap'
require 'net/ldap/dn'
require 'timeout'
class AuthSourceLdap < AuthSource
validates_presence_of :host, :port, :attr_login
@@ -25,18 +26,11 @@ class AuthSourceLdap < AuthSource
validates_length_of :account, :account_password, :base_dn, :filter, :maximum => 255, :allow_blank => true
validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30, :allow_nil => true
validates_numericality_of :port, :only_integer => true
validates_numericality_of :timeout, :only_integer => true, :allow_blank => true
validate :validate_filter
before_validation :strip_ldap_attributes
def self.human_attribute_name(attribute_key_name, *args)
attr_name = attribute_key_name.to_s
if attr_name == "filter"
attr_name = "ldap_filter"
end
super(attr_name, *args)
end
def initialize(attributes=nil, *args)
super
self.port = 389 if self.port == 0
@@ -44,22 +38,26 @@ class AuthSourceLdap < AuthSource
def authenticate(login, password)
return nil if login.blank? || password.blank?
attrs = get_user_dn(login, password)
if attrs && attrs[:dn] && authenticate_dn(attrs[:dn], password)
logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
return attrs.except(:dn)
with_timeout do
attrs = get_user_dn(login, password)
if attrs && attrs[:dn] && authenticate_dn(attrs[:dn], password)
logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
return attrs.except(:dn)
end
end
rescue Net::LDAP::LdapError => e
rescue Net::LDAP::LdapError => e
raise AuthSourceException.new(e.message)
end
# test the connection to the LDAP
def test_connection
ldap_con = initialize_ldap_con(self.account, self.account_password)
ldap_con.open { }
rescue Net::LDAP::LdapError => e
raise "LdapError: " + e.message
with_timeout do
ldap_con = initialize_ldap_con(self.account, self.account_password)
ldap_con.open { }
end
rescue Net::LDAP::LdapError => e
raise AuthSourceException.new(e.message)
end
def auth_method_name
@@ -68,6 +66,16 @@ class AuthSourceLdap < AuthSource
private
def with_timeout(&block)
timeout = self.timeout
timeout = 20 unless timeout && timeout > 0
Timeout.timeout(timeout) do
return yield
end
rescue Timeout::Error => e
raise AuthSourceTimeoutException.new(e.message)
end
def ldap_filter
if filter.present?
Net::LDAP::Filter.construct(filter)

View File

@@ -21,26 +21,37 @@ class Board < ActiveRecord::Base
has_many :topics, :class_name => 'Message', :conditions => "#{Message.table_name}.parent_id IS NULL", :order => "#{Message.table_name}.created_on DESC"
has_many :messages, :dependent => :destroy, :order => "#{Message.table_name}.created_on DESC"
belongs_to :last_message, :class_name => 'Message', :foreign_key => :last_message_id
acts_as_list :scope => :project_id
acts_as_tree :dependent => :nullify
acts_as_list :scope => '(project_id = #{project_id} AND parent_id #{parent_id ? "= #{parent_id}" : "IS NULL"})'
acts_as_watchable
validates_presence_of :name, :description
validates_length_of :name, :maximum => 30
validates_length_of :description, :maximum => 255
validate :validate_board
scope :visible, lambda {|*args| { :include => :project,
:conditions => Project.allowed_to_condition(args.shift || User.current, :view_messages, *args) } }
safe_attributes 'name', 'description', 'move_to'
safe_attributes 'name', 'description', 'parent_id', 'move_to'
def visible?(user=User.current)
!user.nil? && user.allowed_to?(:view_messages, project)
end
def reload(*args)
@valid_parents = nil
super
end
def to_s
name
end
def valid_parents
@valid_parents ||= project.boards - self_and_descendants
end
def reset_counters!
self.class.reset_counters!(id)
end
@@ -53,4 +64,26 @@ class Board < ActiveRecord::Base
" last_message_id = (SELECT MAX(id) FROM #{Message.table_name} WHERE board_id=#{board_id})",
["id = ?", board_id])
end
def self.board_tree(boards, parent_id=nil, level=0)
tree = []
boards.select {|board| board.parent_id == parent_id}.sort_by(&:position).each do |board|
tree << [board, level]
tree += board_tree(boards, board.id, level+1)
end
if block_given?
tree.each do |board, level|
yield board, level
end
end
tree
end
protected
def validate_board
if parent_id && parent_id_changed?
errors.add(:parent_id, :invalid) unless valid_parents.include?(parent)
end
end
end

View File

@@ -162,7 +162,7 @@ class Changeset < ActiveRecord::Base
tag = "#{repository.identifier}|#{tag}"
end
if ref_project && project && ref_project != project
tag = "#{project.identifier}:#{tag}"
tag = "#{project.identifier}:#{tag}"
end
tag
end
@@ -176,18 +176,12 @@ 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.where(["id < ? AND repository_id = ?", id, repository_id]).order('id DESC').first
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.where(["id > ? AND repository_id = ?", id, repository_id]).order('id ASC').first
end
# Creates a new Change from it's common parameters

View File

@@ -30,11 +30,6 @@ class CustomField < ActiveRecord::Base
validate :validate_custom_field
before_validation :set_searchable
def initialize(attributes=nil, *args)
super
self.possible_values ||= []
end
def set_searchable
# make sure these fields are not searchable
self.searchable = false if %w(int float date bool).include?(field_format)
@@ -97,7 +92,7 @@ class CustomField < ActiveRecord::Base
value.force_encoding('UTF-8') if value.respond_to?(:force_encoding)
end
end
values
values || []
end
end
@@ -131,16 +126,26 @@ class CustomField < ActiveRecord::Base
casted
end
def value_from_keyword(keyword, customized)
possible_values_options = possible_values_options(customized)
if possible_values_options.present?
keyword = keyword.to_s.downcase
possible_values_options.detect {|text, id| text.downcase == keyword}.try(:last)
else
keyword
end
end
# Returns a ORDER BY clause that can used to sort customized
# objects by their value of the custom field.
# Returns false, if the custom field can not be used for sorting.
# Returns nil if the custom field can not be used for sorting.
def order_statement
return nil if multiple?
case field_format
when 'string', 'text', 'list', 'date', 'bool'
# COALESCE is here to make sure that blank and NULL values are sorted equally
"COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" +
" WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
" WHERE cv_sort.customized_type='#{self.class.customized_class.base_class.name}'" +
" AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
" AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
when 'int', 'float'
@@ -148,18 +153,74 @@ class CustomField < ActiveRecord::Base
# Postgresql will raise an error if a value can not be casted!
# CustomValue validations should ensure that it doesn't occur
"(SELECT CAST(cv_sort.value AS decimal(60,3)) FROM #{CustomValue.table_name} cv_sort" +
" WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
" WHERE cv_sort.customized_type='#{self.class.customized_class.base_class.name}'" +
" AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
" AND cv_sort.custom_field_id=#{id} AND cv_sort.value <> '' AND cv_sort.value IS NOT NULL LIMIT 1)"
when 'user', 'version'
value_class.fields_for_order_statement(value_join_alias)
else
nil
end
end
# Returns a GROUP BY clause that can used to group by custom value
# Returns nil if the custom field can not be used for grouping.
def group_statement
return nil if multiple?
case field_format
when 'list', 'date', 'bool', 'int'
order_statement
when 'user', 'version'
"COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" +
" WHERE cv_sort.customized_type='#{self.class.customized_class.base_class.name}'" +
" AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
" AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
else
nil
end
end
def join_for_order_statement
case field_format
when 'user', 'version'
"LEFT OUTER JOIN #{CustomValue.table_name} #{join_alias}" +
" ON #{join_alias}.customized_type = '#{self.class.customized_class.base_class.name}'" +
" AND #{join_alias}.customized_id = #{self.class.customized_class.table_name}.id" +
" AND #{join_alias}.custom_field_id = #{id}" +
" AND #{join_alias}.value <> ''" +
" AND #{join_alias}.id = (SELECT max(#{join_alias}_2.id) FROM #{CustomValue.table_name} #{join_alias}_2" +
" WHERE #{join_alias}_2.customized_type = #{join_alias}.customized_type" +
" AND #{join_alias}_2.customized_id = #{join_alias}.customized_id" +
" AND #{join_alias}_2.custom_field_id = #{join_alias}.custom_field_id)" +
" LEFT OUTER JOIN #{value_class.table_name} #{value_join_alias}" +
" ON CAST(#{join_alias}.value as decimal(60,0)) = #{value_join_alias}.id"
else
nil
end
end
def join_alias
"cf_#{id}"
end
def value_join_alias
join_alias + "_" + field_format
end
def <=>(field)
position <=> field.position
end
# Returns the class that values represent
def value_class
case field_format
when 'user', 'version'
field_format.classify.constantize
else
nil
end
end
def self.customized_class
self.name =~ /^(.+)CustomField$/
begin; $1.constantize; rescue nil; end
@@ -200,6 +261,10 @@ class CustomField < ActiveRecord::Base
validate_field_value(value).empty?
end
def format_in?(*args)
args.include?(field_format)
end
protected
# Returns the error message for the given value regarding its format

View File

@@ -31,4 +31,10 @@ class DocumentCategory < Enumeration
def transfer_relations(to)
documents.update_all("category_id = #{to.id}")
end
def self.default
d = super
d = first if d.nil?
d
end
end

View File

@@ -35,19 +35,19 @@ class Enumeration < ActiveRecord::Base
validates_uniqueness_of :name, :scope => [:type, :project_id]
validates_length_of :name, :maximum => 30
scope :shared, :conditions => { :project_id => nil }
scope :active, :conditions => { :active => true }
scope :named, lambda {|arg| { :conditions => ["LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip]}}
scope :shared, where(:project_id => nil)
scope :active, where(:active => true)
scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)}
def self.default
# Creates a fake default scope so Enumeration.default will check
# it's type. STI subclasses will automatically add their own
# types to the finder.
if self.descends_from_active_record?
find(:first, :conditions => { :is_default => true, :type => 'Enumeration' })
where(:is_default => true, :type => 'Enumeration').first
else
# STI classes are
find(:first, :conditions => { :is_default => true })
where(:is_default => true).first
end
end
@@ -58,7 +58,7 @@ class Enumeration < ActiveRecord::Base
def check_default
if is_default? && is_default_changed?
Enumeration.update_all("is_default = #{connection.quoted_false}", {:type => type})
Enumeration.update_all({:is_default => false}, {:type => type})
end
end

View File

@@ -16,6 +16,8 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class Group < Principal
include Redmine::SafeAttributes
has_and_belongs_to_many :users, :after_add => :user_added,
:after_remove => :user_removed
@@ -27,11 +29,25 @@ class Group < Principal
before_destroy :remove_references_before_destroy
scope :sorted, order("#{table_name}.lastname ASC")
safe_attributes 'name',
'user_ids',
'custom_field_values',
'custom_fields',
:if => lambda {|group, user| user.admin?}
def to_s
lastname.to_s
end
alias :name :to_s
def name
lastname
end
def name=(arg)
self.lastname = arg
end
def user_added(user)
members.each do |member|

View File

@@ -58,7 +58,7 @@ class Issue < ActiveRecord::Base
validates_length_of :subject, :maximum => 255
validates_inclusion_of :done_ratio, :in => 0..100
validates_numericality_of :estimated_hours, :allow_nil => true
validate :validate_issue
validate :validate_issue, :validate_required_fields
scope :visible,
lambda {|*args| { :include => :project,
@@ -70,7 +70,6 @@ class Issue < ActiveRecord::Base
}
scope :recently_updated, :order => "#{Issue.table_name}.updated_on DESC"
scope :with_limit, lambda { |limit| { :limit => limit} }
scope :on_active_project, :include => [:status, :project, :tracker],
:conditions => ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"]
@@ -78,6 +77,8 @@ class Issue < ActiveRecord::Base
before_save :close_duplicates, :update_done_ratio_from_issue_status, :force_updated_on_change
after_save {|issue| issue.send :after_project_change if !issue.id_changed? && issue.project_id_changed?}
after_save :reschedule_following_issues, :update_nested_set_attributes, :update_parent_attributes, :create_journal
# Should be after_create but would be called before previous after_save callbacks
after_save :after_create_from_copy
after_destroy :update_parent_attributes
# Returns a SQL conditions string used to find all issues visible by the specified user
@@ -146,6 +147,12 @@ class Issue < ActiveRecord::Base
super
end
def reload(*args)
@workflow_rule_by_attribute = nil
@assignable_versions = nil
super
end
# Overrides Redmine::Acts::Customizable::InstanceMethods#available_custom_fields
def available_custom_fields
(project && tracker) ? (project.all_issue_custom_fields & tracker.custom_fields.all) : []
@@ -164,6 +171,7 @@ class Issue < ActiveRecord::Base
end
end
@copied_from = issue
@copy_options = options
self
end
@@ -208,7 +216,9 @@ class Issue < ActiveRecord::Base
def status_id=(sid)
self.status = nil
write_attribute(:status_id, sid)
result = write_attribute(:status_id, sid)
@workflow_rule_by_attribute = nil
result
end
def priority_id=(pid)
@@ -230,6 +240,7 @@ class Issue < ActiveRecord::Base
self.tracker = nil
result = write_attribute(:tracker_id, tid)
@custom_field_values = nil
@workflow_rule_by_attribute = nil
result
end
@@ -244,6 +255,8 @@ class Issue < ActiveRecord::Base
write_attribute(:project_id, project ? project.id : nil)
association_instance_set('project', project)
if project_was && project && project_was != project
@assignable_versions = nil
unless keep_tracker || project.trackers.include?(tracker)
self.tracker = project.trackers.first
end
@@ -336,6 +349,13 @@ class Issue < ActiveRecord::Base
:if => lambda {|issue, user| (issue.new_record? || user.allowed_to?(:edit_issues, issue.project)) &&
user.allowed_to?(:manage_subtasks, issue.project)}
def safe_attribute_names(user=nil)
names = super
names -= disabled_core_fields
names -= read_only_attribute_names(user)
names
end
# Safely sets attributes
# Should be called from controllers instead of #attributes=
# attr_accessible is too rough because we still want things like
@@ -343,27 +363,28 @@ class Issue < ActiveRecord::Base
def safe_attributes=(attrs, user=User.current)
return unless attrs.is_a?(Hash)
# User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
attrs = delete_unsafe_attributes(attrs, user)
return if attrs.empty?
attrs = attrs.dup
# Project and Tracker must be set before since new_statuses_allowed_to depends on it.
if p = attrs.delete('project_id')
if (p = attrs.delete('project_id')) && safe_attribute?('project_id')
if allowed_target_projects(user).collect(&:id).include?(p.to_i)
self.project_id = p
end
end
if t = attrs.delete('tracker_id')
if (t = attrs.delete('tracker_id')) && safe_attribute?('tracker_id')
self.tracker_id = t
end
if attrs['status_id']
unless new_statuses_allowed_to(user).collect(&:id).include?(attrs['status_id'].to_i)
attrs.delete('status_id')
if (s = attrs.delete('status_id')) && safe_attribute?('status_id')
if new_statuses_allowed_to(user).collect(&:id).include?(s.to_i)
self.status_id = s
end
end
attrs = delete_unsafe_attributes(attrs, user)
return if attrs.empty?
unless leaf?
attrs.reject! {|k,v| %w(priority_id done_ratio start_date due_date estimated_hours).include?(k)}
end
@@ -372,10 +393,92 @@ class Issue < ActiveRecord::Base
attrs.delete('parent_issue_id') unless Issue.visible(user).exists?(attrs['parent_issue_id'].to_i)
end
if attrs['custom_field_values'].present?
attrs['custom_field_values'] = attrs['custom_field_values'].reject {|k, v| read_only_attribute_names(user).include? k.to_s}
end
if attrs['custom_fields'].present?
attrs['custom_fields'] = attrs['custom_fields'].reject {|c| read_only_attribute_names(user).include? c['id'].to_s}
end
# mass-assignment security bypass
assign_attributes attrs, :without_protection => true
end
def disabled_core_fields
tracker ? tracker.disabled_core_fields : []
end
# Returns the custom_field_values that can be edited by the given user
def editable_custom_field_values(user=nil)
custom_field_values.reject do |value|
read_only_attribute_names(user).include?(value.custom_field_id.to_s)
end
end
# Returns the names of attributes that are read-only for user or the current user
# For users with multiple roles, the read-only fields are the intersection of
# read-only fields of each role
# The result is an array of strings where sustom fields are represented with their ids
#
# Examples:
# issue.read_only_attribute_names # => ['due_date', '2']
# issue.read_only_attribute_names(user) # => []
def read_only_attribute_names(user=nil)
workflow_rule_by_attribute(user).reject {|attr, rule| rule != 'readonly'}.keys
end
# Returns the names of required attributes for user or the current user
# For users with multiple roles, the required fields are the intersection of
# required fields of each role
# The result is an array of strings where sustom fields are represented with their ids
#
# Examples:
# issue.required_attribute_names # => ['due_date', '2']
# issue.required_attribute_names(user) # => []
def required_attribute_names(user=nil)
workflow_rule_by_attribute(user).reject {|attr, rule| rule != 'required'}.keys
end
# Returns true if the attribute is required for user
def required_attribute?(name, user=nil)
required_attribute_names(user).include?(name.to_s)
end
# Returns a hash of the workflow rule by attribute for the given user
#
# Examples:
# issue.workflow_rule_by_attribute # => {'due_date' => 'required', 'start_date' => 'readonly'}
def workflow_rule_by_attribute(user=nil)
return @workflow_rule_by_attribute if @workflow_rule_by_attribute && user.nil?
user_real = user || User.current
roles = user_real.admin ? Role.all : user_real.roles_for_project(project)
return {} if roles.empty?
result = {}
workflow_permissions = WorkflowPermission.where(:tracker_id => tracker_id, :old_status_id => status_id, :role_id => roles.map(&:id)).all
if workflow_permissions.any?
workflow_rules = workflow_permissions.inject({}) do |h, wp|
h[wp.field_name] ||= []
h[wp.field_name] << wp.rule
h
end
workflow_rules.each do |attr, rules|
next if rules.size < roles.size
uniq_rules = rules.uniq
if uniq_rules.size == 1
result[attr] = uniq_rules.first
else
result[attr] = 'required'
end
end
end
@workflow_rule_by_attribute = result if user.nil?
result
end
private :workflow_rule_by_attribute
def done_ratio
if Issue.use_status_for_done_ratio? && status && status.default_done_ratio
status.default_done_ratio
@@ -437,6 +540,25 @@ class Issue < ActiveRecord::Base
end
end
# Validates the issue against additional workflow requirements
def validate_required_fields
user = new_record? ? author : current_journal.try(:user)
required_attribute_names(user).each do |attribute|
if attribute =~ /^\d+$/
attribute = attribute.to_i
v = custom_field_values.detect {|v| v.custom_field_id == attribute }
if v && v.value.blank?
errors.add :base, v.custom_field.name + ' ' + l('activerecord.errors.messages.blank')
end
else
if respond_to?(attribute) && send(attribute).blank?
errors.add attribute, :blank
end
end
end
end
# Set the done_ratio using the status if that setting is set. This will keep the done_ratios
# even if the user turns off the setting later
def update_done_ratio_from_issue_status
@@ -462,10 +584,19 @@ class Issue < ActiveRecord::Base
if new_record?
nil
else
journals.first(:order => "#{Journal.table_name}.id DESC").try(:id)
journals.maximum(:id)
end
end
# Returns a scope for journals that have an id greater than journal_id
def journals_after(journal_id)
scope = journals.reorder("#{Journal.table_name}.id ASC")
if journal_id.present?
scope = scope.where("#{Journal.table_name}.id > ?", journal_id.to_i)
end
scope
end
# Return true if the issue is closed, otherwise false
def closed?
self.status.is_closed?
@@ -522,7 +653,21 @@ class Issue < ActiveRecord::Base
# Versions that the issue can be assigned to
def assignable_versions
@assignable_versions ||= (project.shared_versions.open + [Version.find_by_id(fixed_version_id_was)]).compact.uniq.sort
return @assignable_versions if @assignable_versions
versions = project.shared_versions.open.all
if fixed_version
if fixed_version_id_changed?
# nothing to do
elsif project_id_changed?
if project.shared_versions.include?(fixed_version)
versions << fixed_version
end
else
versions << fixed_version
end
end
@assignable_versions = versions.uniq.sort
end
# Returns true if this issue is blocked by another issue that is still open
@@ -702,7 +847,7 @@ class Issue < ActiveRecord::Base
# Returns a string of css classes that apply to the issue
def css_classes
s = "issue status-#{status.position} priority-#{priority.position}"
s = "issue status-#{status_id} priority-#{priority_id}"
s << ' closed' if closed?
s << ' overdue' if overdue?
s << ' child' if child?
@@ -858,6 +1003,30 @@ class Issue < ActiveRecord::Base
end
end
# Copies subtasks from the copied issue
def after_create_from_copy
return unless copy?
unless @copied_from.leaf? || @copy_options[:subtasks] == false || @subtasks_copied
@copied_from.children.each do |child|
unless child.visible?
# Do not copy subtasks that are not visible to avoid potential disclosure of private data
logger.error "Subtask ##{child.id} was not copied during ##{@copied_from.id} copy because it is not visible to the current user" if logger
next
end
copy = Issue.new.copy_from(child, @copy_options)
copy.author = author
copy.project = project
copy.parent_issue_id = id
# Children subtasks are copied recursively
unless copy.save
logger.error "Could not copy subtask ##{child.id} while copying ##{@copied_from.id} to ##{id} due to validation errors: #{copy.errors.full_messages.join(', ')}" if logger
end
end
@subtasks_copied = true
end
end
def update_nested_set_attributes
if root_id.nil?
# issue was just created

View File

@@ -17,10 +17,10 @@
class IssueStatus < ActiveRecord::Base
before_destroy :check_integrity
has_many :workflows, :foreign_key => "old_status_id"
has_many :workflows, :class_name => 'WorkflowTransition', :foreign_key => "old_status_id"
acts_as_list
before_destroy :delete_workflows
before_destroy :delete_workflow_rules
after_save :update_default
validates_presence_of :name
@@ -28,23 +28,23 @@ class IssueStatus < ActiveRecord::Base
validates_length_of :name, :maximum => 30
validates_inclusion_of :default_done_ratio, :in => 0..100, :allow_nil => true
scope :named, lambda {|arg| { :conditions => ["LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip]}}
scope :sorted, order("#{table_name}.position ASC")
scope :named, lambda {|arg| where(["LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip])}
def update_default
IssueStatus.update_all("is_default=#{connection.quoted_false}", ['id <> ?', id]) if self.is_default?
IssueStatus.update_all({:is_default => false}, ['id <> ?', id]) if self.is_default?
end
# Returns the default status for new issues
def self.default
find(:first, :conditions =>["is_default=?", true])
where(:is_default => true).first
end
# Update all the +Issues+ setting their done_ratio to the value of their +IssueStatus+
def self.update_issue_done_ratios
if Issue.use_status_for_done_ratio?
IssueStatus.find(:all, :conditions => ["default_done_ratio >= 0"]).each do |status|
Issue.update_all(["done_ratio = ?", status.default_done_ratio],
["status_id = ?", status.id])
IssueStatus.where("default_done_ratio >= 0").all.each do |status|
Issue.update_all({:done_ratio => status.default_done_ratio}, {:status_id => status.id})
end
end
@@ -61,7 +61,7 @@ class IssueStatus < ActiveRecord::Base
w.tracker_id == tracker.id &&
((!w.author && !w.assignee) || (author && w.author) || (assignee && w.assignee))
end
transitions.collect{|w| w.new_status}.compact.sort
transitions.map(&:new_status).compact.sort
else
[]
end
@@ -75,12 +75,12 @@ class IssueStatus < ActiveRecord::Base
conditions << " OR author = :true" if author
conditions << " OR assignee = :true" if assignee
workflows.find(:all,
:include => :new_status,
:conditions => ["role_id IN (:role_ids) AND tracker_id = :tracker_id AND (#{conditions})",
workflows.
includes(:new_status).
where(["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
]).all.
map(&:new_status).compact.sort
else
[]
end
@@ -92,13 +92,14 @@ class IssueStatus < ActiveRecord::Base
def to_s; name end
private
private
def check_integrity
raise "Can't delete status" if Issue.find(:first, :conditions => ["status_id=?", self.id])
raise "Can't delete status" if Issue.where(:status_id => id).any?
end
# Deletes associated workflows
def delete_workflows
Workflow.delete_all(["old_status_id = :id OR new_status_id = :id", {:id => id}])
def delete_workflow_rules
WorkflowRule.delete_all(["old_status_id = :id OR new_status_id = :id", {:id => id}])
end
end

View File

@@ -342,8 +342,8 @@ class MailHandler < ActionMailer::Base
# Returns a Hash of issue custom field values extracted from keywords in the email body
def custom_field_values_from_keywords(customized)
customized.custom_field_values.inject({}) do |h, v|
if value = get_keyword(v.custom_field.name, :override => true)
h[v.custom_field.id.to_s] = value
if keyword = get_keyword(v.custom_field.name, :override => true)
h[v.custom_field.id.to_s] = v.custom_field.value_from_keyword(keyword, customized)
end
h
end
@@ -468,7 +468,7 @@ class MailHandler < ActionMailer::Base
}
end
if assignee.nil?
assignee ||= assignable.detect {|a| a.is_a?(Group) && a.name.downcase == keyword}
assignee ||= assignable.detect {|a| a.name.downcase == keyword}
end
assignee
end

View File

@@ -327,22 +327,31 @@ class Mailer < ActionMailer::Base
# * :days => how many days in the future to remind about (defaults to 7)
# * :tracker => id of tracker for filtering issues (defaults to all trackers)
# * :project => id or identifier of project to process (defaults to all projects)
# * :users => array of user ids who should be reminded
# * :users => array of user/group ids who should be reminded
def self.reminders(options={})
days = options[:days] || 7
project = options[:project] ? Project.find(options[:project]) : nil
tracker = options[:tracker] ? Tracker.find(options[:tracker]) : nil
user_ids = options[:users]
scope = Issue.open.scoped(:conditions => ["#{Issue.table_name}.assigned_to_id IS NOT NULL" +
scope = Issue.open.where("#{Issue.table_name}.assigned_to_id IS NOT NULL" +
" AND #{Project.table_name}.status = #{Project::STATUS_ACTIVE}" +
" AND #{Issue.table_name}.due_date <= ?", days.day.from_now.to_date]
" AND #{Issue.table_name}.due_date <= ?", days.day.from_now.to_date
)
scope = scope.scoped(:conditions => {:assigned_to_id => user_ids}) if user_ids.present?
scope = scope.scoped(:conditions => {:project_id => project.id}) if project
scope = scope.scoped(:conditions => {:tracker_id => tracker.id}) if tracker
scope = scope.where(:assigned_to_id => user_ids) if user_ids.present?
scope = scope.where(:project_id => project.id) if project
scope = scope.where(:tracker_id => tracker.id) if tracker
issues_by_assignee = scope.includes(:status, :assigned_to, :project, :tracker).all.group_by(&:assigned_to)
issues_by_assignee.keys.each do |assignee|
if assignee.is_a?(Group)
assignee.users.each do |user|
issues_by_assignee[user] ||= []
issues_by_assignee[user] += issues_by_assignee[assignee]
end
end
end
issues_by_assignee = scope.all(:include => [:status, :assigned_to, :project, :tracker]).group_by(&:assigned_to)
issues_by_assignee.each do |assignee, issues|
reminder(assignee, issues, days).deliver if assignee.is_a?(User) && assignee.active?
end

View File

@@ -41,9 +41,9 @@ class Message < ActiveRecord::Base
validates_length_of :subject, :maximum => 255
validate :cannot_reply_to_locked_topic, :on => :create
after_create :add_author_as_watcher, :update_parent_last_reply
after_create :add_author_as_watcher, :reset_counters!
after_update :update_messages_board
after_destroy :reset_board_counters
after_destroy :reset_counters!
scope :visible, lambda {|*args| { :include => {:board => :project},
:conditions => Project.allowed_to_condition(args.shift || User.current, :view_messages, *args) } }
@@ -63,13 +63,6 @@ class Message < ActiveRecord::Base
errors.add :base, 'Topic is locked' if root.locked? && self != root
end
def update_parent_last_reply
if parent
parent.reload.update_attribute(:last_reply_id, self.id)
end
board.reset_counters!
end
def update_messages_board
if board_id_changed?
Message.update_all("board_id = #{board_id}", ["id = ? OR parent_id = ?", root.id, root.id])
@@ -78,7 +71,10 @@ class Message < ActiveRecord::Base
end
end
def reset_board_counters
def reset_counters!
if parent && parent.id
Message.update_all({:last_reply_id => parent.children.maximum(:id)}, {:id => parent.id})
end
board.reset_counters!
end

View File

@@ -19,7 +19,7 @@ class Principal < ActiveRecord::Base
self.table_name = "#{table_name_prefix}users#{table_name_suffix}"
has_many :members, :foreign_key => 'user_id', :dependent => :destroy
has_many :memberships, :class_name => 'Member', :foreign_key => 'user_id', :include => [ :project, :roles ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name"
has_many :memberships, :class_name => 'Member', :foreign_key => 'user_id', :include => [ :project, :roles ], :conditions => "#{Project.table_name}.status<>#{Project::STATUS_ARCHIVED}", :order => "#{Project.table_name}.name"
has_many :projects, :through => :memberships
has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify

View File

@@ -20,6 +20,7 @@ class Project < ActiveRecord::Base
# Project statuses
STATUS_ACTIVE = 1
STATUS_CLOSED = 5
STATUS_ARCHIVED = 9
# Maximum length for project identifiers
@@ -80,6 +81,7 @@ class Project < ActiveRecord::Base
# reserved words
validates_exclusion_of :identifier, :in => %w( new )
after_save :update_position_under_parent, :if => Proc.new {|project| project.name_changed?}
before_destroy :delete_all_members
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] } }
@@ -121,7 +123,7 @@ class Project < ActiveRecord::Base
self.enabled_module_names = Setting.default_projects_modules
end
if !initialized.key?('trackers') && !initialized.key?('tracker_ids')
self.trackers = Tracker.all
self.trackers = Tracker.sorted.all
end
end
@@ -161,12 +163,11 @@ class Project < ActiveRecord::Base
# * :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={})
base_statement = "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"
if perm = Redmine::AccessControl.permission(permission)
unless perm.project_module.nil?
# If the permission belongs to a project module, make sure the module is enabled
base_statement << " AND #{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name='#{perm.project_module}')"
end
perm = Redmine::AccessControl.permission(permission)
base_statement = (perm && perm.read? ? "#{Project.table_name}.status <> #{Project::STATUS_ARCHIVED}" : "#{Project.table_name}.status = #{Project::STATUS_ACTIVE}")
if perm && perm.project_module
# If the permission belongs to a project module, make sure the module is enabled
base_statement << " AND #{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name='#{perm.project_module}')"
end
if options[:project]
project_statement = "#{Project.table_name}.id = #{options[:project].id}"
@@ -186,7 +187,7 @@ class Project < ActiveRecord::Base
end
if user.logged?
user.projects_by_role.each do |role, projects|
if role.allowed_to?(permission)
if role.allowed_to?(permission) && projects.any?
statement_by_role[role] = "#{Project.table_name}.id IN (#{projects.collect(&:id).join(',')})"
end
end
@@ -325,6 +326,14 @@ class Project < ActiveRecord::Base
update_attribute :status, STATUS_ACTIVE
end
def close
self_and_descendants.status(STATUS_ACTIVE).update_all :status => STATUS_CLOSED
end
def reopen
self_and_descendants.status(STATUS_CLOSED).update_all :status => STATUS_ACTIVE
end
# Returns an array of projects the project can be moved to
# by the current user
def allowed_parents
@@ -375,22 +384,7 @@ class Project < ActiveRecord::Base
# Nothing to do
true
elsif p.nil? || (p.active? && move_possible?(p))
# Insert the project so that target's children or root projects stay alphabetically sorted
sibs = (p.nil? ? self.class.roots : p.children)
to_be_inserted_before = sibs.detect {|c| c.name.to_s.downcase > name.to_s.downcase }
if to_be_inserted_before
move_to_left_of(to_be_inserted_before)
elsif p.nil?
if sibs.empty?
# move_to_root adds the project in first (ie. left) position
move_to_root
else
move_to_right_of(sibs.last) unless self == sibs.last
end
else
# move_to_child_of adds the project in last (ie.right) position
move_to_child_of(p)
end
set_or_update_position_under(p)
Issue.update_versions_from_hierarchy_change(self)
true
else
@@ -404,7 +398,7 @@ class Project < ActiveRecord::Base
@rolled_up_trackers ||=
Tracker.find(:all, :joins => :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],
:conditions => ["#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status <> #{STATUS_ARCHIVED}", lft, rgt],
:order => "#{Tracker.table_name}.position")
end
@@ -423,20 +417,20 @@ class Project < ActiveRecord::Base
def rolled_up_versions
@rolled_up_versions ||=
Version.scoped(:include => :project,
:conditions => ["#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status = #{STATUS_ACTIVE}", lft, rgt])
:conditions => ["#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status <> #{STATUS_ARCHIVED}", lft, rgt])
end
# Returns a scope of the Versions used by the project
def shared_versions
if new_record?
Version.scoped(:include => :project,
:conditions => "#{Project.table_name}.status = #{Project::STATUS_ACTIVE} AND #{Version.table_name}.sharing = 'system'")
:conditions => "#{Project.table_name}.status <> #{Project::STATUS_ARCHIVED} AND #{Version.table_name}.sharing = 'system'")
else
@shared_versions ||= begin
r = root? ? self : root
Version.scoped(:include => :project,
:conditions => "#{Project.table_name}.id = #{id}" +
" OR (#{Project.table_name}.status = #{Project::STATUS_ACTIVE} AND (" +
" OR (#{Project.table_name}.status <> #{Project::STATUS_ARCHIVED} 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 < #{lft} AND #{Project.table_name}.rgt > #{rgt} AND #{Version.table_name}.sharing IN ('hierarchy', 'descendants'))" +
@@ -515,6 +509,13 @@ class Project < ActiveRecord::Base
s << ' root' if root?
s << ' child' if child?
s << (leaf? ? ' leaf' : ' parent')
unless active?
if archived?
s << ' archived'
else
s << ' closed'
end
end
s
end
@@ -558,11 +559,20 @@ class Project < ActiveRecord::Base
end
end
# Return true if this project is allowed to do the specified action.
# Return true if this project allows to do the specified action.
# action can be:
# * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
# * a permission Symbol (eg. :edit_project)
def allows_to?(action)
if archived?
# No action allowed on archived projects
return false
end
unless active? || Redmine::AccessControl.read_action?(action)
# No write action allowed on closed projects
return false
end
# No action allowed on disabled modules
if action.is_a? Hash
allowed_actions.include? "#{action[:controller]}/#{action[:action]}"
else
@@ -753,27 +763,30 @@ 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.
issues_map = {}
# Store status and reopen locked/closed versions
version_statuses = versions.reject(&:open?).map {|version| [version, version.status]}
version_statuses.each do |version, status|
version.update_attribute :status, 'open'
end
# Get issues sorted by root_id, lft so that parent issues
# get copied before their children
project.issues.find(:all, :order => 'root_id, lft').each do |issue|
new_issue = Issue.new
new_issue.copy_from(issue)
new_issue.copy_from(issue, :subtasks => false)
new_issue.project = self
# Reassign fixed_versions by name, since names are unique per
# project and the versions for self are not yet saved
if issue.fixed_version
new_issue.fixed_version = self.versions.select {|v| v.name == issue.fixed_version.name}.first
# Reassign fixed_versions by name, since names are unique per project
if issue.fixed_version && issue.fixed_version.project == project
new_issue.fixed_version = self.versions.detect {|v| v.name == issue.fixed_version.name}
end
# Reassign the category by name, since names are unique per
# project and the categories for self are not yet saved
# Reassign the category by name, since names are unique per project
if issue.category
new_issue.category = self.issue_categories.select {|c| c.name == issue.category.name}.first
new_issue.category = self.issue_categories.detect {|c| c.name == issue.category.name}
end
# Parent issue
if issue.parent_id
@@ -790,6 +803,11 @@ class Project < ActiveRecord::Base
end
end
# Restore locked/closed version statuses
version_statuses.each do |version, status|
version.update_attribute :status, status
end
# Relations after in case issues related each other
project.issues.each do |issue|
new_issue = issues_map[issue.id]
@@ -919,4 +937,28 @@ class Project < ActiveRecord::Base
end
update_attribute :status, STATUS_ARCHIVED
end
def update_position_under_parent
set_or_update_position_under(parent)
end
# Inserts/moves the project so that target's children or root projects stay alphabetically sorted
def set_or_update_position_under(target_parent)
sibs = (target_parent.nil? ? self.class.roots : target_parent.children)
to_be_inserted_before = sibs.sort_by {|c| c.name.to_s.downcase}.detect {|c| c.name.to_s.downcase > name.to_s.downcase }
if to_be_inserted_before
move_to_left_of(to_be_inserted_before)
elsif target_parent.nil?
if sibs.empty?
# move_to_root adds the project in first (ie. left) position
move_to_root
else
move_to_right_of(sibs.last) unless self == sibs.last
end
else
# move_to_child_of adds the project in last (ie.right) position
move_to_child_of(target_parent)
end
end
end

View File

@@ -57,10 +57,7 @@ class QueryCustomFieldColumn < QueryColumn
def initialize(custom_field)
self.name = "cf_#{custom_field.id}".to_sym
self.sortable = custom_field.order_statement || false
if %w(list date bool int).include?(custom_field.field_format) && !custom_field.multiple?
self.groupable = custom_field.order_statement
end
self.groupable ||= false
self.groupable = custom_field.group_statement || false
@cf = custom_field
end
@@ -144,7 +141,7 @@ class Query < ActiveRecord::Base
QueryColumn.new(:assigned_to, :sortable => lambda {User.fields_for_order_statement}, :groupable => true),
QueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on", :default_order => 'desc'),
QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name", :groupable => true),
QueryColumn.new(:fixed_version, :sortable => ["#{Version.table_name}.effective_date", "#{Version.table_name}.name"], :default_order => 'desc', :groupable => true),
QueryColumn.new(:fixed_version, :sortable => lambda {Version.fields_for_order_statement}, :groupable => true),
QueryColumn.new(:start_date, :sortable => "#{Issue.table_name}.start_date"),
QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date"),
QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours"),
@@ -213,11 +210,18 @@ class Query < ActiveRecord::Base
is_public && !@is_for_all && user.allowed_to?(:manage_public_queries, project)
end
def trackers
@trackers ||= project.nil? ? Tracker.find(:all, :order => 'position') : project.rolled_up_trackers
end
# Returns a hash of localized labels for all filter operators
def self.operators_labels
operators.inject({}) {|h, operator| h[operator.first] = l(operator.last); h}
end
def available_filters
return @available_filters if @available_filters
trackers = project.nil? ? Tracker.find(:all, :order => 'position') : project.rolled_up_trackers
@available_filters = { "status_id" => { :type => :list_status, :order => 1, :values => IssueStatus.find(:all, :order => 'position').collect{|s| [s.name, s.id.to_s] } },
"tracker_id" => { :type => :list, :order => 2, :values => trackers.collect{|s| [s.name, s.id.to_s] } },
"priority_id" => { :type => :list, :order => 3, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] } },
@@ -300,9 +304,34 @@ class Query < ActiveRecord::Base
end
add_custom_fields_filters(IssueCustomField.find(:all, :conditions => {:is_filter => true, :is_for_all => true}))
end
add_associations_custom_fields_filters :project, :author, :assigned_to, :fixed_version
if User.current.allowed_to?(:set_issues_private, nil, :global => true) ||
User.current.allowed_to?(:set_own_issues_private, nil, :global => true)
@available_filters["is_private"] = { :type => :list, :order => 15, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]] }
end
Tracker.disabled_core_fields(trackers).each {|field|
@available_filters.delete field
}
@available_filters.each do |field, options|
options[:name] ||= l("field_#{field}".gsub(/_id$/, ''))
end
@available_filters
end
# Returns a representation of the available filters for JSON serialization
def available_filters_as_json
json = {}
available_filters.each do |field, options|
json[field] = options.slice(:type, :name, :values).stringify_keys
end
json
end
def add_filter(field, operator, values)
# values must be an array
return unless values.nil? || values.is_a?(Array)
@@ -380,6 +409,17 @@ class Query < ActiveRecord::Base
:caption => :label_spent_time
)
end
if User.current.allowed_to?(:set_issues_private, nil, :global => true) ||
User.current.allowed_to?(:set_own_issues_private, nil, :global => true)
@available_columns << QueryColumn.new(:is_private, :sortable => "#{Issue.table_name}.is_private")
end
disabled_fields = Tracker.disabled_core_fields(trackers).map {|field| field.sub(/_id$/, '')}
@available_columns.reject! {|column|
disabled_fields.include?(column.name.to_s)
}
@available_columns
end
@@ -534,7 +574,7 @@ class Query < ActiveRecord::Base
end
end
if field =~ /^cf_(\d+)$/
if field =~ /cf_(\d+)$/
# custom field
filters_clauses << sql_for_custom_field(field, operator, v, $1)
elsif respond_to?("sql_for_#{field}_field")
@@ -584,13 +624,11 @@ class Query < ActiveRecord::Base
def issues(options={})
order_option = [group_by_sort_order, options[:order]].reject {|s| s.blank?}.join(',')
order_option = nil if order_option.blank?
joins = (order_option && order_option.include?('authors')) ? "LEFT OUTER JOIN users authors ON authors.id = #{Issue.table_name}.author_id" : nil
issues = Issue.visible.scoped(:conditions => options[:conditions]).find :all, :include => ([:status, :project] + (options[:include] || [])).uniq,
:conditions => statement,
:order => order_option,
:joins => joins,
:joins => joins_for_order_statement(order_option),
:limit => options[:limit],
:offset => options[:offset]
@@ -606,13 +644,11 @@ class Query < ActiveRecord::Base
def issue_ids(options={})
order_option = [group_by_sort_order, options[:order]].reject {|s| s.blank?}.join(',')
order_option = nil if order_option.blank?
joins = (order_option && order_option.include?('authors')) ? "LEFT OUTER JOIN users authors ON authors.id = #{Issue.table_name}.author_id" : nil
Issue.visible.scoped(:conditions => options[:conditions]).scoped(:include => ([:status, :project] + (options[:include] || [])).uniq,
:conditions => statement,
:order => order_option,
:joins => joins,
:joins => joins_for_order_statement(order_option),
:limit => options[:limit],
:offset => options[:offset]).find_ids
rescue ::ActiveRecord::StatementInvalid => e
@@ -686,13 +722,21 @@ class Query < ActiveRecord::Base
end
end
def sql_for_is_private_field(field, operator, value)
op = (operator == "=" ? 'IN' : 'NOT IN')
va = value.map {|v| v == '0' ? connection.quoted_false : connection.quoted_true}.uniq.join(',')
"#{Issue.table_name}.is_private #{op} (#{va})"
end
private
def sql_for_custom_field(field, operator, value, custom_field_id)
db_table = CustomValue.table_name
db_field = 'value'
filter = @available_filters[field]
if filter && filter[:format] == 'user'
return nil unless filter
if filter[:format] == 'user'
if value.delete('me')
value.push User.current.id.to_s
end
@@ -703,7 +747,15 @@ class Query < ActiveRecord::Base
operator = '='
not_in = 'NOT'
end
"#{Issue.table_name}.id #{not_in} IN (SELECT #{Issue.table_name}.id FROM #{Issue.table_name} LEFT OUTER JOIN #{db_table} ON #{db_table}.customized_type='Issue' AND #{db_table}.customized_id=#{Issue.table_name}.id AND #{db_table}.custom_field_id=#{custom_field_id} WHERE " +
customized_key = "id"
customized_class = Issue
if field =~ /^(.+)\.cf_/
assoc = $1
customized_key = "#{assoc}_id"
customized_class = Issue.reflect_on_association(assoc.to_sym).klass.base_class rescue nil
raise "Unknown Issue association #{assoc}" unless customized_class
end
"#{Issue.table_name}.#{customized_key} #{not_in} IN (SELECT #{customized_class.table_name}.id FROM #{customized_class.table_name} LEFT OUTER JOIN #{db_table} ON #{db_table}.customized_type='#{customized_class}' AND #{db_table}.customized_id=#{customized_class.table_name}.id AND #{db_table}.custom_field_id=#{custom_field_id} WHERE " +
sql_for_field(field, operator, value, db_table, db_field, true) + ')'
end
@@ -812,7 +864,8 @@ class Query < ActiveRecord::Base
return sql
end
def add_custom_fields_filters(custom_fields)
def add_custom_fields_filters(custom_fields, assoc=nil)
return unless custom_fields.present?
@available_filters ||= {}
custom_fields.select(&:is_filter?).each do |field|
@@ -839,7 +892,25 @@ class Query < ActiveRecord::Base
else
options = { :type => :string, :order => 20 }
end
@available_filters["cf_#{field.id}"] = options.merge({ :name => field.name, :format => field.field_format })
filter_id = "cf_#{field.id}"
filter_name = field.name
if assoc.present?
filter_id = "#{assoc}.#{filter_id}"
filter_name = l("label_attribute_of_#{assoc}", :name => filter_name)
end
@available_filters[filter_id] = options.merge({ :name => filter_name, :format => field.field_format })
end
end
def add_associations_custom_fields_filters(*associations)
fields_by_class = CustomField.where(:is_filter => true).group_by(&:class)
associations.each do |assoc|
association_klass = Issue.reflect_on_association(assoc).klass
fields_by_class.each do |field_class, fields|
if field_class.customized_class <= association_klass
add_custom_fields_filters(fields, assoc)
end
end
end
end
@@ -868,4 +939,24 @@ class Query < ActiveRecord::Base
def relative_date_clause(table, field, days_from, days_to)
date_clause(table, field, (days_from ? Date.today + days_from : nil), (days_to ? Date.today + days_to : nil))
end
# Additional joins required for the given sort options
def joins_for_order_statement(order_options)
joins = []
if order_options
if order_options.include?('authors')
joins << "LEFT OUTER JOIN #{User.table_name} authors ON authors.id = #{Issue.table_name}.author_id"
end
order_options.scan(/cf_\d+/).uniq.each do |name|
column = available_columns.detect {|c| c.name.to_s == name}
join = column && column.custom_field.join_for_order_statement
if join
joins << join
end
end
end
joins.any? ? joins.join(' ') : nil
end
end

View File

@@ -19,6 +19,10 @@ class ScmFetchError < Exception; end
class Repository < ActiveRecord::Base
include Redmine::Ciphering
include Redmine::SafeAttributes
# Maximum length for repository identifiers
IDENTIFIER_MAX_LENGTH = 255
belongs_to :project
has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
@@ -33,7 +37,7 @@ class Repository < ActiveRecord::Base
before_destroy :clear_changesets
validates_length_of :password, :maximum => 255, :allow_nil => true
validates_length_of :identifier, :maximum => 255, :allow_blank => true
validates_length_of :identifier, :maximum => IDENTIFIER_MAX_LENGTH, :allow_blank => true
validates_presence_of :identifier, :unless => Proc.new { |r| r.is_default? || r.set_as_default? }
validates_uniqueness_of :identifier, :scope => :project_id, :allow_blank => true
validates_exclusion_of :identifier, :in => %w(show entry raw changes annotate diff show stats graph)
@@ -42,6 +46,16 @@ class Repository < ActiveRecord::Base
# Checks if the SCM is enabled when creating a repository
validate :repo_create_validation, :on => :create
safe_attributes 'identifier',
'login',
'password',
'path_encoding',
'log_encoding',
'is_default'
safe_attributes 'url',
:if => lambda {|repository, user| repository.new_record?}
def repo_create_validation
unless Setting.enabled_scm.include?(self.class.name.demodulize)
errors.add(:type, :invalid)
@@ -103,6 +117,14 @@ class Repository < ActiveRecord::Base
end
end
def identifier=(identifier)
super unless identifier_frozen?
end
def identifier_frozen?
errors[:identifier].blank? && !(new_record? || identifier.blank?)
end
def identifier_param
if is_default?
nil
@@ -167,7 +189,9 @@ class Repository < ActiveRecord::Base
end
def entries(path=nil, identifier=nil)
scm.entries(path, identifier)
entries = scm.entries(path, identifier)
load_entries_changesets(entries)
entries
end
def branches
@@ -380,6 +404,16 @@ class Repository < ActiveRecord::Base
end
end
def load_entries_changesets(entries)
if entries
entries.each do |entry|
if entry.lastrev && entry.lastrev.identifier
entry.changeset = find_changeset_by_name(entry.lastrev.identifier)
end
end
end
end
private
# Deletes repository data

View File

@@ -63,6 +63,8 @@ class Repository::Bazaar < Repository
end
end
end
load_entries_changesets(entries)
entries
end
def fetch_changesets

View File

@@ -21,6 +21,9 @@ require 'digest/sha1'
class Repository::Cvs < Repository
validates_presence_of :url, :root_url, :log_encoding
safe_attributes 'root_url',
:if => lambda {|repository, user| repository.new_record?}
def self.human_attribute_name(attribute_key_name, *args)
attr_name = attribute_key_name.to_s
if attr_name == "root_url"
@@ -66,6 +69,7 @@ class Repository::Cvs < Repository
end
end
end
load_entries_changesets(entries)
entries
end

View File

@@ -66,6 +66,7 @@ class Repository::Darcs < Repository
end
end
end
load_entries_changesets(entries)
entries
end

View File

@@ -44,10 +44,6 @@ class Repository::Filesystem < Repository
false
end
def entries(path=nil, identifier=nil)
scm.entries(path, identifier)
end
def fetch_changesets
nil
end

View File

@@ -87,16 +87,16 @@ class Repository::Git < Repository
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}%"])
if name.present?
changesets.where(:revision => name.to_s).first ||
changesets.where('scmid LIKE ?', "#{name}%").first
end
end
def entries(path=nil, identifier=nil)
scm.entries(path,
identifier,
options = {:report_last_commit => extra_report_last_commit})
entries = scm.entries(path, identifier, :report_last_commit => extra_report_last_commit)
load_entries_changesets(entries)
entries
end
# With SCMs that have a sequential commit numbering,

View File

@@ -76,12 +76,12 @@ class Repository::Mercurial < Repository
return nil if name.blank?
s = name.to_s
if /[^\d]/ =~ s or s.size > 8
e = changesets.find(:first, :conditions => ['scmid = ?', s])
cs = changesets.where(:scmid => s).first
else
e = changesets.find(:first, :conditions => ['revision = ?', s])
cs = changesets.where(:revision => s).first
end
return e if e
changesets.find(:first, :conditions => ['scmid LIKE ?', "#{s}%"]) # last ditch
return cs if cs
changesets.where('scmid LIKE ?', "#{s}%").first
end
# Returns the latest changesets for +path+; sorted by revision number

View File

@@ -40,7 +40,12 @@ class Repository::Subversion < Repository
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) : []
if revisions
identifiers = revisions.collect(&:identifier).compact
changesets.where(:revision => identifiers).reorder("committed_on DESC").includes(:repository, :user).all
else
[]
end
end
# Returns a path relative to the url of the repository
@@ -81,6 +86,24 @@ class Repository::Subversion < Repository
end
end
protected
def load_entries_changesets(entries)
return unless entries
entries_with_identifier = entries.select {|entry| entry.lastrev && entry.lastrev.identifier.present?}
identifiers = entries_with_identifier.map {|entry| entry.lastrev.identifier}.compact.uniq
if identifiers.any?
changesets_by_identifier = changesets.where(:revision => identifiers).includes(:user, :repository).all.group_by(&:revision)
entries_with_identifier.each do |entry|
if m = changesets_by_identifier[entry.lastrev.identifier]
entry.changeset = m.first
end
end
end
end
private
# Returns the relative url of the repository

View File

@@ -16,6 +16,19 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class Role < ActiveRecord::Base
# Custom coder for the permissions attribute that should be an
# array of symbols. Rails 3 uses Psych which can be *unbelievably*
# slow on some platforms (eg. mingw32).
class PermissionsAttributeCoder
def self.load(str)
str.to_s.scan(/:([a-z0-9_]+)/).flatten.map(&:to_sym)
end
def self.dump(value)
YAML.dump(value)
end
end
# Built-in roles
BUILTIN_NON_MEMBER = 1
BUILTIN_ANONYMOUS = 2
@@ -26,17 +39,17 @@ class Role < ActiveRecord::Base
['own', :label_issues_visibility_own]
]
scope :sorted, {:order => 'builtin, position'}
scope :givable, { :conditions => "builtin = 0", :order => 'position' }
scope :sorted, order("#{table_name}.builtin ASC, #{table_name}.position ASC")
scope :givable, order("#{table_name}.position ASC").where(:builtin => 0)
scope :builtin, lambda { |*args|
compare = 'not' if args.first == true
{ :conditions => "#{compare} builtin = 0" }
compare = (args.first == true ? 'not' : '')
where("#{compare} builtin = 0")
}
before_destroy :check_deletable
has_many :workflows, :dependent => :delete_all do
has_many :workflow_rules, :dependent => :delete_all do
def copy(source_role)
Workflow.copy(nil, source_role, nil, proxy_association.owner)
WorkflowRule.copy(nil, source_role, nil, proxy_association.owner)
end
end
@@ -44,7 +57,7 @@ class Role < ActiveRecord::Base
has_many :members, :through => :member_roles
acts_as_list
serialize :permissions, Array
serialize :permissions, ::Role::PermissionsAttributeCoder
attr_protected :builtin
validates_presence_of :name
@@ -54,8 +67,13 @@ class Role < ActiveRecord::Base
:in => ISSUES_VISIBILITY_OPTIONS.collect(&:first),
:if => lambda {|role| role.respond_to?(:issues_visibility)}
def permissions
read_attribute(:permissions) || []
# Copies attributes from another role, arg can be an id or a Role
def copy_from(arg, options={})
return unless arg.present?
role = arg.is_a?(Role) ? arg : Role.find_by_id(arg.to_s)
self.attributes = role.attributes.dup.except("id", "name", "position", "builtin", "permissions")
self.permissions = role.permissions.dup
self
end
def permissions=(perms)
@@ -87,7 +105,15 @@ class Role < ActiveRecord::Base
end
def <=>(role)
role ? position <=> role.position : -1
if role
if builtin == role.builtin
position <=> role.position
else
builtin <=> role.builtin
end
else
-1
end
end
def to_s
@@ -134,7 +160,7 @@ class Role < ActiveRecord::Base
# Find all the roles that can be given to a project member
def self.find_all_givable
find(:all, :conditions => {:builtin => 0}, :order => 'position')
Role.givable.all
end
# Return the builtin 'non member' role. If the role doesn't exist,
@@ -165,7 +191,7 @@ private
end
def self.find_or_create_system_role(builtin, name)
role = first(:conditions => {:builtin => builtin})
role = where(:builtin => builtin).first
if role.nil?
role = create(:name => name, :position => 0) do |r|
r.builtin = builtin

View File

@@ -16,11 +16,18 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class Tracker < ActiveRecord::Base
CORE_FIELDS_UNDISABLABLE = %w(project_id tracker_id subject description priority_id is_private).freeze
# Fields that can be disabled
# Other (future) fields should be appended, not inserted!
CORE_FIELDS = %w(assigned_to_id category_id fixed_version_id parent_issue_id start_date due_date estimated_hours done_ratio).freeze
CORE_FIELDS_ALL = (CORE_FIELDS_UNDISABLABLE + CORE_FIELDS).freeze
before_destroy :check_integrity
has_many :issues
has_many :workflows, :dependent => :delete_all do
has_many :workflow_rules, :dependent => :delete_all do
def copy(source_tracker)
Workflow.copy(source_tracker, nil, proxy_association.owner, nil)
WorkflowRule.copy(source_tracker, nil, proxy_association.owner, nil)
end
end
@@ -28,11 +35,14 @@ class Tracker < ActiveRecord::Base
has_and_belongs_to_many :custom_fields, :class_name => 'IssueCustomField', :join_table => "#{table_name_prefix}custom_fields_trackers#{table_name_suffix}", :association_foreign_key => 'custom_field_id'
acts_as_list
attr_protected :field_bits
validates_presence_of :name
validates_uniqueness_of :name
validates_length_of :name, :maximum => 30
scope :named, lambda {|arg| { :conditions => ["LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip]}}
scope :sorted, order("#{table_name}.position ASC")
scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)}
def to_s; name end
@@ -40,10 +50,6 @@ class Tracker < ActiveRecord::Base
position <=> tracker.position
end
def self.all
find(:all, :order => 'position')
end
# Returns an array of IssueStatus that are used
# in the tracker's workflows
def issue_statuses
@@ -53,16 +59,57 @@ class Tracker < ActiveRecord::Base
return []
end
ids = Workflow.
connection.select_rows("SELECT DISTINCT old_status_id, new_status_id FROM #{Workflow.table_name} WHERE tracker_id = #{id}").
ids = WorkflowTransition.
connection.select_rows("SELECT DISTINCT old_status_id, new_status_id FROM #{WorkflowTransition.table_name} WHERE tracker_id = #{id} AND type = 'WorkflowTransition'").
flatten.
uniq
@issue_statuses = IssueStatus.find_all_by_id(ids).sort
end
def disabled_core_fields
i = -1
@disabled_core_fields ||= CORE_FIELDS.select { i += 1; (fields_bits || 0) & (2 ** i) != 0}
end
def core_fields
CORE_FIELDS - disabled_core_fields
end
def core_fields=(fields)
raise ArgumentError.new("Tracker.core_fields takes an array") unless fields.is_a?(Array)
bits = 0
CORE_FIELDS.each_with_index do |field, i|
unless fields.include?(field)
bits |= 2 ** i
end
end
self.fields_bits = bits
@disabled_core_fields = nil
core_fields
end
# Returns the fields that are disabled for all the given trackers
def self.disabled_core_fields(trackers)
if trackers.present?
trackers.uniq.map(&:disabled_core_fields).reduce(:&)
else
[]
end
end
# Returns the fields that are enabled for one tracker at least
def self.core_fields(trackers)
if trackers.present?
trackers.uniq.map(&:core_fields).reduce(:|)
else
CORE_FIELDS.dup
end
end
private
def check_integrity
raise "Can't delete tracker" if Issue.find(:first, :conditions => ["tracker_id=?", self.id])
raise Exception.new("Can't delete tracker") if Issue.where(:tracker_id => self.id).any?
end
end

View File

@@ -52,8 +52,6 @@ class User < Principal
has_one :api_token, :class_name => 'Token', :conditions => "action='api'"
belongs_to :auth_source
# Active non-anonymous users scope
scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
scope :logged, :conditions => "#{User.table_name}.status <> #{STATUS_ANONYMOUS}"
scope :status, lambda {|arg| arg.blank? ? {} : {:conditions => {:status => arg.to_i}} }
@@ -394,7 +392,7 @@ class User < Principal
def roles_for_project(project)
roles = []
# No role on archived projects
return roles unless project && project.active?
return roles if project.nil? || project.archived?
if logged?
# Find project membership
membership = memberships.detect {|m| m.project_id == project.id}
@@ -420,10 +418,13 @@ class User < Principal
def projects_by_role
return @projects_by_role if @projects_by_role
@projects_by_role = Hash.new {|h,k| h[k]=[]}
@projects_by_role = Hash.new([])
memberships.each do |membership|
membership.roles.each do |role|
@projects_by_role[role] << membership.project if membership.project
if membership.project
membership.roles.each do |role|
@projects_by_role[role] = [] unless @projects_by_role.key?(role)
@projects_by_role[role] << membership.project
end
end
end
@projects_by_role.each do |role, projects|
@@ -455,9 +456,6 @@ class User < Principal
# or falls back to Non Member / Anonymous permissions depending if the user is logged
def allowed_to?(action, context, options={}, &block)
if context && context.is_a?(Project)
# No action allowed on archived projects
return false unless context.active?
# No action allowed on disabled modules
return false unless context.allows_to?(action)
# Admin users are authorized for anything else
return true if admin?
@@ -662,6 +660,10 @@ class AnonymousUser < User
def time_zone; nil end
def rss_key; nil end
def pref
UserPreference.new(:user => self)
end
# Anonymous user can not be destroyed
def destroy
false

View File

@@ -162,13 +162,13 @@ class Version < ActiveRecord::Base
"#{project} - #{name}"
end
# Versions are sorted by effective_date and "Project Name - Version name"
# Those with no effective_date are at the end, sorted by "Project Name - Version name"
# Versions are sorted by effective_date and name
# Those with no effective_date are at the end, sorted by name
def <=>(version)
if self.effective_date
if version.effective_date
if self.effective_date == version.effective_date
"#{self.project.name} - #{self.name}" <=> "#{version.project.name} - #{version.name}"
name == version.name ? id <=> version.id : name <=> version.name
else
self.effective_date <=> version.effective_date
end
@@ -179,11 +179,18 @@ class Version < ActiveRecord::Base
if version.effective_date
1
else
"#{self.project.name} - #{self.name}" <=> "#{version.project.name} - #{version.name}"
name == version.name ? id <=> version.id : name <=> version.name
end
end
end
def self.fields_for_order_statement(table=nil)
table ||= table_name
["(CASE WHEN #{table}.effective_date IS NULL THEN 1 ELSE 0 END)", "#{table}.effective_date", "#{table}.name", "#{table}.id"]
end
scope :sorted, order(fields_for_order_statement)
# Returns the sharings that +user+ can set the version to
def allowed_sharings(user = User.current)
VERSION_SHARINGS.select do |s|

View File

@@ -0,0 +1,45 @@
# Redmine - project management software
# Copyright (C) 2006-2012 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 WorkflowPermission < WorkflowRule
validates_inclusion_of :rule, :in => %w(readonly required)
validate :validate_field_name
# Replaces the workflow permissions for the given tracker and role
#
# Example:
# WorkflowPermission.replace_permissions role, tracker, {'due_date' => {'1' => 'readonly', '2' => 'required'}}
def self.replace_permissions(tracker, role, permissions)
destroy_all(:tracker_id => tracker.id, :role_id => role.id)
permissions.each { |field, rule_by_status_id|
rule_by_status_id.each { |status_id, rule|
if rule.present?
WorkflowPermission.create(:role_id => role.id, :tracker_id => tracker.id, :old_status_id => status_id, :field_name => field, :rule => rule)
end
}
}
end
protected
def validate_field_name
unless Tracker::CORE_FIELDS_ALL.include?(field_name) || field_name.to_s.match(/^\d+$/)
errors.add :field_name, :invalid
end
end
end

View File

@@ -15,31 +15,15 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class Workflow < ActiveRecord::Base
class WorkflowRule < ActiveRecord::Base
self.table_name = "#{table_name_prefix}workflows#{table_name_suffix}"
belongs_to :role
belongs_to :tracker
belongs_to :old_status, :class_name => 'IssueStatus', :foreign_key => 'old_status_id'
belongs_to :new_status, :class_name => 'IssueStatus', :foreign_key => 'new_status_id'
validates_presence_of :role, :old_status, :new_status
# Returns workflow transitions count by tracker and role
def self.count_by_tracker_and_role
counts = connection.select_all("SELECT role_id, tracker_id, count(id) AS c FROM #{Workflow.table_name} GROUP BY role_id, tracker_id")
roles = Role.find(:all, :order => 'builtin, position')
trackers = Tracker.find(:all, :order => 'position')
result = []
trackers.each do |tracker|
t = []
roles.each do |role|
row = counts.detect {|c| c['role_id'].to_s == role.id.to_s && c['tracker_id'].to_s == tracker.id.to_s}
t << [role, (row.nil? ? 0 : row['c'].to_i)]
end
result << [tracker, t]
end
result
end
validates_presence_of :role, :tracker, :old_status
# Copies workflows from source to targets
def self.copy(source_tracker, source_role, target_trackers, target_roles)
@@ -50,7 +34,7 @@ class Workflow < ActiveRecord::Base
target_trackers = [target_trackers].flatten.compact
target_roles = [target_roles].flatten.compact
target_trackers = Tracker.all if target_trackers.empty?
target_trackers = Tracker.sorted.all if target_trackers.empty?
target_roles = Role.all if target_roles.empty?
target_trackers.each do |target_tracker|
@@ -78,9 +62,9 @@ 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" +
" FROM #{Workflow.table_name}" +
connection.insert "INSERT INTO #{WorkflowRule.table_name} (tracker_id, role_id, old_status_id, new_status_id, author, assignee, field_name, rule, type)" +
" SELECT #{target_tracker.id}, #{target_role.id}, old_status_id, new_status_id, author, assignee, field_name, rule, type" +
" FROM #{WorkflowRule.table_name}" +
" WHERE tracker_id = #{source_tracker.id} AND role_id = #{source_role.id}"
end
true

View File

@@ -0,0 +1,39 @@
# Redmine - project management software
# Copyright (C) 2006-2012 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 WorkflowTransition < WorkflowRule
validates_presence_of :new_status
# Returns workflow transitions count by tracker and role
def self.count_by_tracker_and_role
counts = connection.select_all("SELECT role_id, tracker_id, count(id) AS c FROM #{table_name} WHERE type = 'WorkflowTransition' GROUP BY role_id, tracker_id")
roles = Role.sorted.all
trackers = Tracker.sorted.all
result = []
trackers.each do |tracker|
t = []
roles.each do |role|
row = counts.detect {|c| c['role_id'].to_s == role.id.to_s && c['tracker_id'].to_s == tracker.id.to_s}
t << [role, (row.nil? ? 0 : row['c'].to_i)]
end
result << [tracker, t]
end
result
end
end

View File

@@ -1,6 +1,6 @@
<%= call_hook :view_account_login_top %>
<div id="login-form">
<%= form_tag({:action=> "login"}) do %>
<%= form_tag(signin_path) do %>
<%= back_url_hidden_field_tag %>
<table>
<tr>
@@ -28,7 +28,7 @@
<tr>
<td align="left">
<% if Setting.lost_password? %>
<%= link_to l(:label_password_lost), :controller => 'account', :action => 'lost_password' %>
<%= link_to l(:label_password_lost), lost_password_path %>
<% end %>
</td>
<td align="right">
@@ -36,7 +36,7 @@
</td>
</tr>
</table>
<%= javascript_tag "Form.Element.focus('username');" %>
<%= javascript_tag "$('#username').focus();" %>
<% end %>
</div>
<%= call_hook :view_account_login_bottom %>

View File

@@ -1,11 +1,11 @@
<h2><%=l(:label_password_lost)%></h2>
<div class="box">
<%= form_tag({:action=> "lost_password"}, :class => "tabular") do %>
<p><label for="mail"><%=l(:field_mail)%> <span class="required">*</span></label>
<%= text_field_tag 'mail', nil, :size => 40 %>
<%= submit_tag l(:button_submit) %></p>
<%= form_tag(lost_password_path) do %>
<div class="box tabular">
<p>
<label for="mail"><%=l(:field_mail)%> <span class="required">*</span></label>
<%= text_field_tag 'mail', nil, :size => 40 %>
<%= submit_tag l(:button_submit) %>
</p>
</div>
<% end %>
</div>

View File

@@ -2,14 +2,19 @@
<%= error_messages_for 'user' %>
<%= form_tag({:token => @token.value}) do %>
<div class="box tabular">
<p><label for="new_password"><%=l(:field_new_password)%> <span class="required">*</span></label>
<%= password_field_tag 'new_password', nil, :size => 25 %>
<em class="info"><%= l(:text_caracters_minimum, :count => Setting.password_min_length) %></em></p>
<%= form_tag(lost_password_path) do %>
<%= hidden_field_tag 'token', @token.value %>
<div class="box tabular">
<p>
<label for="new_password"><%=l(:field_new_password)%> <span class="required">*</span></label>
<%= password_field_tag 'new_password', nil, :size => 25 %>
<em class="info"><%= l(:text_caracters_minimum, :count => Setting.password_min_length) %></em>
</p>
<p><label for="new_password_confirmation"><%=l(:field_password_confirmation)%> <span class="required">*</span></label>
<%= password_field_tag 'new_password_confirmation', nil, :size => 25 %></p>
</div>
<p><%= submit_tag l(:button_save) %></p>
<p>
<label for="new_password_confirmation"><%=l(:field_password_confirmation)%> <span class="required">*</span></label>
<%= password_field_tag 'new_password_confirmation', nil, :size => 25 %>
</p>
</div>
<p><%= submit_tag l(:button_save) %></p>
<% end %>

View File

@@ -1,6 +1,6 @@
<h2><%=l(:label_register)%> <%=link_to l(:label_login_with_open_id_option), signin_url if Setting.openid? %></h2>
<%= labelled_form_for @user, :url => {:action => 'register'} do |f| %>
<%= labelled_form_for @user, :url => register_path do |f| %>
<%= error_messages_for 'user' %>
<div class="box tabular">

View File

@@ -1,5 +1,3 @@
<div id="admin-menu">
<ul>
<%= render_menu :admin_menu %>
</ul>
<%= render_menu :admin_menu %>
</div>

View File

@@ -27,12 +27,12 @@
<tbody>
<% project_tree(@projects) do |project, level| %>
<tr class="<%= cycle("odd", "even") %> <%= project.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>">
<td class="name"><span><%= link_to_project(project, {:action => 'settings'}, :title => project.short_description) %></span></td>
<td class="name"><span><%= link_to_project(project, {:action => (project.active? ? 'settings' : 'show')}, :title => project.short_description) %></span></td>
<td align="center"><%= checked_image project.is_public? %></td>
<td align="center"><%= format_date(project.created_on) %></td>
<td class="buttons">
<%= link_to(l(:button_archive), { :controller => 'projects', :action => 'archive', :id => project, :status => params[:status] }, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-lock') if project.active? %>
<%= link_to(l(:button_unarchive), { :controller => 'projects', :action => 'unarchive', :id => project, :status => params[:status] }, :method => :post, :class => 'icon icon-unlock') if !project.active? && (project.parent.nil? || project.parent.active?) %>
<%= link_to(l(:button_archive), { :controller => 'projects', :action => 'archive', :id => project, :status => params[:status] }, :data => {:confirm => l(:text_are_you_sure)}, :method => :post, :class => 'icon icon-lock') unless project.archived? %>
<%= link_to(l(:button_unarchive), { :controller => 'projects', :action => 'unarchive', :id => project, :status => params[:status] }, :method => :post, :class => 'icon icon-unlock') if project.archived? && (project.parent.nil? || !project.parent.archived?) %>
<%= link_to(l(:button_copy), { :controller => 'projects', :action => 'copy', :id => project }, :class => 'icon icon-copy') %>
<%= link_to(l(:button_delete), project_path(project), :method => :delete, :class => 'icon icon-del') %>
</td>

View File

@@ -8,7 +8,7 @@
<% end %>
<span id="attachments_fields">
<span>
<%= file_field_tag 'attachments[1][file]', :size => 30, :id => nil, :class => 'file',
<%= file_field_tag 'attachments[1][file]', :id => nil, :class => 'file',
:onchange => "checkFileSize(this, #{Setting.attachment_max_size.to_i.kilobytes}, '#{escape_javascript(l(:error_attachment_too_big, :max_size => number_to_human_size(Setting.attachment_max_size.to_i.kilobytes)))}');" -%>
<%= text_field_tag 'attachments[1][description]', '', :id => nil, :class => 'description', :maxlength => 255, :placeholder => l(:label_optional_description) %>
<%= link_to_function(image_tag('delete.png'), 'removeFileField(this)', :title => (l(:button_delete))) %>

View File

@@ -10,7 +10,7 @@
<span class="size">(<%= number_to_human_size attachment.filesize %>)</span>
<% if options[:deletable] %>
<%= link_to image_tag('delete.png'), attachment_path(attachment),
:confirm => l(:text_are_you_sure),
:data => {:confirm => l(:text_are_you_sure)},
:method => :delete,
:class => 'delete',
:title => l(:button_delete) %>
@@ -20,4 +20,14 @@
<% end %>
</p>
<% end %>
<% if defined?(thumbnails) && thumbnails %>
<% images = attachments.select(&:thumbnailable?) %>
<% if images.any? %>
<div class="thumbnails">
<% images.each do |attachment| %>
<div><%= thumbnail_tag(attachment) %></div>
<% end %>
</div>
<% end %>
<% end %>
</div>

View File

@@ -23,9 +23,12 @@
<p><label for="auth_source_base_dn"><%=l(:field_base_dn)%> <span class="required">*</span></label>
<%= text_field 'auth_source', 'base_dn', :size => 60 %></p>
<p><label for="auth_source_custom_filter"><%=l(:field_ldap_filter)%></label>
<p><label for="auth_source_custom_filter"><%=l(:field_auth_source_ldap_filter)%></label>
<%= text_field 'auth_source', 'filter', :size => 60 %></p>
<p><label for="auth_source_timeout"><%=l(:field_timeout)%></label>
<%= text_field 'auth_source', 'timeout', :size => 4 %></p>
<p><label for="auth_source_onthefly_register"><%=l(:field_onthefly)%></label>
<%= check_box 'auth_source', 'onthefly_register' %></p>
</div>

View File

@@ -21,11 +21,7 @@
<td align="center"><%= h source.users.count %></td>
<td class="buttons">
<%= link_to l(:button_test), {:action => 'test_connection', :id => source}, :class => 'icon icon-test' %>
<%= link_to l(:button_delete), { :action => 'destroy', :id => source },
:method => :delete,
:confirm => l(:text_are_you_sure),
:class => 'icon icon-del',
:disabled => source.users.any? %>
<%= delete_link auth_source_path(source) %>
</td>
</tr>
<% end %>

View File

@@ -1,9 +1,7 @@
<ul>
<% if @issues.any? -%>
<% @issues.each do |issue| -%>
<%= content_tag 'li', h("#{issue.tracker} ##{issue.id}: #{issue.subject}"), :id => issue.id %>
<% end -%>
<% else -%>
<%= content_tag("li", l(:label_none), :style => 'display:none') %>
<% end -%>
</ul>
<%= raw @issues.map {|issue| {
'id' => issue.id,
'label' => "#{issue.tracker} ##{issue.id}: #{truncate issue.subject.to_s, :length => 60}",
'value' => issue.id
}
}.to_json
%>

View File

@@ -3,4 +3,7 @@
<div class="box tabular">
<p><%= f.text_field :name, :required => true %></p>
<p><%= f.text_field :description, :required => true, :size => 80 %></p>
<% if @board.valid_parents.any? %>
<p><%= f.select :parent_id, boards_options_for_select(@board.valid_parents), :include_blank => true, :label => :field_board_parent %></p>
<% end %>
</div>

View File

@@ -8,21 +8,19 @@
<th><%= l(:label_message_last) %></th>
</tr></thead>
<tbody>
<% for board in @boards %>
<% Board.board_tree(@boards) do |board, level| %>
<tr class="<%= cycle 'odd', 'even' %>">
<td>
<td style="padding-left: <%= level * 18 %>px;">
<%= link_to h(board.name), {:action => 'show', :id => board}, :class => "board" %><br />
<%=h board.description %>
</td>
<td align="center"><%= board.topics_count %></td>
<td align="center"><%= board.messages_count %></td>
<td>
<small>
<td class="topic-count"><%= board.topics_count %></td>
<td class="message-count"><%= board.messages_count %></td>
<td class="last-message">
<% if board.last_message %>
<%= authoring board.last_message.created_on, board.last_message.author %><br />
<%= link_to_message board.last_message %>
<% end %>
</small>
</td>
</tr>
<% end %>

View File

@@ -1,10 +1,10 @@
<%= breadcrumb link_to(l(:label_board_plural), project_boards_path(@project)) %>
<%= board_breadcrumb(@board) %>
<div class="contextual">
<%= link_to_if_authorized l(:label_message_new),
{:controller => 'messages', :action => 'new', :board_id => @board},
:class => 'icon icon-add',
:onclick => 'Element.show("add-message"); Form.Element.focus("message_subject"); return false;' %>
:onclick => 'showAndScrollTo("add-message", "message_subject"); return false;' %>
<%= watcher_tag(@board, User.current) %>
</div>
@@ -14,14 +14,8 @@
<%= form_for @message, :url => {:controller => 'messages', :action => 'new', :board_id => @board}, :html => {:multipart => true, :id => 'message-form'} do |f| %>
<%= render :partial => 'messages/form', :locals => {:f => f} %>
<p><%= submit_tag l(:button_create) %>
<%= link_to_remote l(:label_preview),
{ :url => { :controller => 'messages', :action => 'preview', :board_id => @board },
:method => 'post',
:update => 'preview',
:with => "Form.serialize('message-form')",
:complete => "Element.scrollTo('preview')"
}, :accesskey => accesskey(:preview) %> |
<%= link_to l(:button_cancel), "#", :onclick => 'Element.hide("add-message")' %></p>
<%= preview_link({:controller => 'messages', :action => 'preview', :board_id => @board}, 'message-form') %> |
<%= link_to l(:button_cancel), "#", :onclick => '$("#add-message").hide(); return false;' %></p>
<% end %>
<div id="preview" class="wiki"></div>
<% end %>
@@ -43,9 +37,9 @@
<% @topics.each do |topic| %>
<tr class="message <%= cycle 'odd', 'even' %> <%= topic.sticky? ? 'sticky' : '' %> <%= topic.locked? ? 'locked' : '' %>">
<td class="subject"><%= link_to h(topic.subject), { :controller => 'messages', :action => 'show', :board_id => @board, :id => topic } %></td>
<td class="author" align="center"><%= link_to_user(topic.author) %></td>
<td class="created_on" align="center"><%= format_time(topic.created_on) %></td>
<td class="replies" align="center"><%= topic.replies_count %></td>
<td class="author"><%= link_to_user(topic.author) %></td>
<td class="created_on"><%= format_time(topic.created_on) %></td>
<td class="reply-count"><%= topic.replies_count %></td>
<td class="last_message">
<% if topic.last_reply %>
<%= authoring topic.last_reply.created_on, topic.last_reply.author %><br />
@@ -69,5 +63,4 @@
<% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@project}: #{@board}") %>
<%= stylesheet_link_tag 'scm' %>
<% end %>

View File

@@ -20,7 +20,7 @@
<%= 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_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' %>
</p>
<% end %>

View File

@@ -16,8 +16,8 @@
</div>
<script>
Event.observe(window, 'load', function() { displayTabsButtons(); });
Event.observe(window, 'resize', function() { displayTabsButtons(); });
$(document).ready(displayTabsButtons);
$(window).resize(displayTabsButtons);
</script>
<% tabs.each do |tab| -%>

View File

@@ -5,7 +5,7 @@
<li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue},
:class => 'icon-edit', :disabled => !@can[:edit] %></li>
<% else %>
<li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id)},
<li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issue_ids},
:class => 'icon-edit', :disabled => !@can[:edit] %></li>
<% end %>
@@ -14,7 +14,7 @@
<a href="#" class="submenu"><%= l(:field_status) %></a>
<ul>
<% @allowed_statuses.each do |s| -%>
<li><%= context_menu_link h(s.name), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {:status_id => s}, :back_url => @back}, :method => :post,
<li><%= context_menu_link h(s.name), {:controller => 'issues', :action => 'bulk_update', :ids => @issue_ids, :issue => {:status_id => s}, :back_url => @back}, :method => :post,
:selected => (@issue && s == @issue.status), :disabled => !@can[:update] %></li>
<% end -%>
</ul>
@@ -26,74 +26,77 @@
<a href="#" class="submenu"><%= l(:field_tracker) %></a>
<ul>
<% @trackers.each do |t| -%>
<li><%= context_menu_link h(t.name), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'tracker_id' => t}, :back_url => @back}, :method => :post,
<li><%= context_menu_link h(t.name), {:controller => 'issues', :action => 'bulk_update', :ids => @issue_ids, :issue => {'tracker_id' => t}, :back_url => @back}, :method => :post,
:selected => (@issue && t == @issue.tracker), :disabled => !@can[:edit] %></li>
<% end -%>
</ul>
</li>
<% end %>
<% if @safe_attributes.include?('priority_id') -%>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_priority) %></a>
<ul>
<% @priorities.each do |p| -%>
<li><%= context_menu_link h(p.name), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'priority_id' => p}, :back_url => @back}, :method => :post,
<li><%= context_menu_link h(p.name), {:controller => 'issues', :action => 'bulk_update', :ids => @issue_ids, :issue => {'priority_id' => p}, :back_url => @back}, :method => :post,
:selected => (@issue && p == @issue.priority), :disabled => (!@can[:edit] || @issues.detect {|i| !i.leaf?}) %></li>
<% end -%>
</ul>
</li>
<% end %>
<% #TODO: allow editing versions when multiple projects %>
<% unless @project.nil? || @project.shared_versions.open.empty? -%>
<% if @safe_attributes.include?('fixed_version_id') && @versions.any? -%>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_fixed_version) %></a>
<ul>
<% @project.shared_versions.open.sort.each do |v| -%>
<li><%= context_menu_link format_version_name(v), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'fixed_version_id' => v}, :back_url => @back}, :method => :post,
<% @versions.sort.each do |v| -%>
<li><%= context_menu_link format_version_name(v), {:controller => 'issues', :action => 'bulk_update', :ids => @issue_ids, :issue => {'fixed_version_id' => v}, :back_url => @back}, :method => :post,
:selected => (@issue && v == @issue.fixed_version), :disabled => !@can[:update] %></li>
<% end -%>
<li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'fixed_version_id' => 'none'}, :back_url => @back}, :method => :post,
<li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_update', :ids => @issue_ids, :issue => {'fixed_version_id' => 'none'}, :back_url => @back}, :method => :post,
:selected => (@issue && @issue.fixed_version.nil?), :disabled => !@can[:update] %></li>
</ul>
</li>
<% end %>
<% if @assignables.present? -%>
<% if @safe_attributes.include?('assigned_to_id') && @assignables.present? -%>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_assigned_to) %></a>
<ul>
<% if @assignables.include?(User.current) %>
<li><%= context_menu_link "<< #{l(:label_me)} >>", {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'assigned_to_id' => User.current}, :back_url => @back}, :method => :post,
<li><%= context_menu_link "<< #{l(:label_me)} >>", {:controller => 'issues', :action => 'bulk_update', :ids => @issue_ids, :issue => {'assigned_to_id' => User.current}, :back_url => @back}, :method => :post,
:disabled => !@can[:update] %></li>
<% end %>
<% @assignables.each do |u| -%>
<li><%= context_menu_link h(u.name), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'assigned_to_id' => u}, :back_url => @back}, :method => :post,
<li><%= context_menu_link h(u.name), {:controller => 'issues', :action => 'bulk_update', :ids => @issue_ids, :issue => {'assigned_to_id' => u}, :back_url => @back}, :method => :post,
:selected => (@issue && u == @issue.assigned_to), :disabled => !@can[:update] %></li>
<% end -%>
<li><%= context_menu_link l(:label_nobody), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'assigned_to_id' => 'none'}, :back_url => @back}, :method => :post,
<li><%= context_menu_link l(:label_nobody), {:controller => 'issues', :action => 'bulk_update', :ids => @issue_ids, :issue => {'assigned_to_id' => 'none'}, :back_url => @back}, :method => :post,
:selected => (@issue && @issue.assigned_to.nil?), :disabled => !@can[:update] %></li>
</ul>
</li>
<% end %>
<% unless @project.nil? || @project.issue_categories.empty? -%>
<% if @safe_attributes.include?('category_id') && @project && @project.issue_categories.any? -%>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_category) %></a>
<ul>
<% @project.issue_categories.each do |u| -%>
<li><%= context_menu_link h(u.name), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'category_id' => u}, :back_url => @back}, :method => :post,
<li><%= context_menu_link h(u.name), {:controller => 'issues', :action => 'bulk_update', :ids => @issue_ids, :issue => {'category_id' => u}, :back_url => @back}, :method => :post,
:selected => (@issue && u == @issue.category), :disabled => !@can[:update] %></li>
<% end -%>
<li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'category_id' => 'none'}, :back_url => @back}, :method => :post,
<li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_update', :ids => @issue_ids, :issue => {'category_id' => 'none'}, :back_url => @back}, :method => :post,
:selected => (@issue && @issue.category.nil?), :disabled => !@can[:update] %></li>
</ul>
</li>
<% end -%>
<% if Issue.use_field_for_done_ratio? %>
<% if @safe_attributes.include?('done_ratio') && Issue.use_field_for_done_ratio? %>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_done_ratio) %></a>
<ul>
<% (0..10).map{|x|x*10}.each do |p| -%>
<li><%= context_menu_link "#{p}%", {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'done_ratio' => p}, :back_url => @back}, :method => :post,
<li><%= context_menu_link "#{p}%", {:controller => 'issues', :action => 'bulk_update', :ids => @issue_ids, :issue => {'done_ratio' => p}, :back_url => @back}, :method => :post,
:selected => (@issue && p == @issue.done_ratio), :disabled => (!@can[:edit] || @issues.detect {|i| !i.leaf?}) %></li>
<% end -%>
</ul>
@@ -128,11 +131,11 @@
<li><%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue},
:class => 'icon-copy', :disabled => !@can[:copy] %></li>
<% else %>
<li><%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :copy => '1'},
<li><%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'bulk_edit', :ids => @issue_ids, :copy => '1'},
:class => 'icon-copy', :disabled => !@can[:move] %></li>
<% end %>
<li><%= context_menu_link l(:button_delete), issues_path(:ids => @issues.collect(&:id), :back_url => @back),
:method => :delete, :confirm => issues_destroy_confirmation_message(@issues), :class => 'icon-del', :disabled => !@can[:delete] %></li>
<li><%= context_menu_link l(:button_delete), issues_path(:ids => @issue_ids, :back_url => @back),
:method => :delete, :data => {:confirm => issues_destroy_confirmation_message(@issues)}, :class => 'icon-del', :disabled => !@can[:delete] %></li>
<%= call_hook(:view_issues_context_menu_end, {:issues => @issues, :can => @can, :back => @back }) %>
</ul>

View File

@@ -28,6 +28,6 @@
<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] %>
:method => :delete, :data => {:confirm => l(:text_time_entries_destroy_confirmation)}, :class => 'icon-del', :disabled => !@can[:delete] %>
</li>
</ul>

View File

@@ -1,86 +1,31 @@
<%= error_messages_for 'custom_field' %>
<script type="text/javascript">
//<![CDATA[
function toggle_custom_field_format() {
format = $("custom_field_field_format");
p_length = $("custom_field_min_length");
p_regexp = $("custom_field_regexp");
p_values = $("custom_field_possible_values");
p_searchable = $("custom_field_searchable");
p_default = $("custom_field_default_value");
p_multiple = $("custom_field_multiple");
p_default.setAttribute('type','text');
Element.show(p_default.parentNode);
switch (format.value) {
case "list":
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_multiple.parentNode);
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_multiple.parentNode);
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_multiple.parentNode);
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_multiple.parentNode);
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);
Element.show(p_multiple.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_multiple.parentNode);
break;
}
}
//]]>
</script>
<div class="box tabular">
<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();",
:disabled => !@custom_field.new_record? %></p>
<p><%= f.select :field_format, custom_field_formats_for_select(@custom_field), {}, :disabled => !@custom_field.new_record? %></p>
<% if @custom_field.format_in? 'list', 'user', 'version' %>
<p><%= f.check_box :multiple, :disabled => @custom_field.multiple && !@custom_field.new_record? %></p>
<% end %>
<% unless @custom_field.format_in? 'list', 'bool', 'date', 'user', 'version' %>
<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>
<% end %>
<% if @custom_field.format_in? 'list' %>
<p>
<%= f.text_area :possible_values, :value => @custom_field.possible_values.to_a.join("\n"), :rows => 15 %>
<em class="info"><%= l(:text_custom_field_possible_values_info) %></em>
</p>
<% end %>
<% unless @custom_field.format_in? 'user', 'version' %>
<p><%= @custom_field.field_format == 'bool' ? f.check_box(:default_value) : f.text_field(:default_value) %></p>
<% end %>
<%= call_hook(:view_custom_fields_form_upper_box, :custom_field => @custom_field, :form => f) %>
</div>
@@ -89,7 +34,7 @@ function toggle_custom_field_format() {
when "IssueCustomField" %>
<fieldset><legend><%=l(:label_tracker_plural)%></legend>
<% Tracker.all.each do |tracker| %>
<% Tracker.sorted.all.each do |tracker| %>
<%= check_box_tag "custom_field[tracker_ids][]",
tracker.id,
(@custom_field.trackers.include? tracker),
@@ -110,11 +55,21 @@ when "IssueCustomField" %>
<p><%= f.check_box :is_required %></p>
<p><%= f.check_box :visible %></p>
<p><%= f.check_box :editable %></p>
<p><%= f.check_box :is_filter %></p>
<% when "ProjectCustomField" %>
<p><%= f.check_box :is_required %></p>
<p><%= f.check_box :visible %></p>
<p><%= f.check_box :searchable %></p>
<p><%= f.check_box :is_filter %></p>
<% when "VersionCustomField" %>
<p><%= f.check_box :is_required %></p>
<p><%= f.check_box :is_filter %></p>
<% when "GroupCustomField" %>
<p><%= f.check_box :is_required %></p>
<p><%= f.check_box :is_filter %></p>
<% when "TimeEntryCustomField" %>
<p><%= f.check_box :is_required %></p>
@@ -125,4 +80,3 @@ when "IssueCustomField" %>
<% end %>
<%= call_hook(:"view_custom_fields_form_#{@custom_field.type.to_s.underscore}", :custom_field => @custom_field, :form => f) %>
</div>
<%= javascript_tag "toggle_custom_field_format();" %>

View File

@@ -22,10 +22,7 @@
<% end %>
<td align="center" style="width:15%;"><%= reorder_links('custom_field', {:action => 'update', :id => custom_field}, :put) %></td>
<td class="buttons">
<%= link_to(l(:button_delete), custom_field_path(custom_field),
:method => :delete,
:confirm => l(:text_are_you_sure),
:class => 'icon icon-del') %>
<%= delete_link custom_field_path(custom_field) %>
</td>
</tr>
<% end; reset_cycle %>

View File

@@ -2,7 +2,7 @@
&#187; <%= link_to l(@custom_field.type_name), :controller => 'custom_fields', :action => 'index', :tab => @custom_field.class.name %>
&#187; <%=h @custom_field.name %></h2>
<%= labelled_form_for :custom_field, @custom_field, :url => custom_field_path(@custom_field), :html => {:method => :put} do |f| %>
<%= labelled_form_for :custom_field, @custom_field, :url => custom_field_path(@custom_field), :html => {:method => :put, :id => 'custom_field_form'} do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<%= submit_tag l(:button_save) %>
<% end %>

View File

@@ -2,8 +2,18 @@
&#187; <%= link_to l(@custom_field.type_name), :controller => 'custom_fields', :action => 'index', :tab => @custom_field.class.name %>
&#187; <%= l(:label_custom_field_new) %></h2>
<%= labelled_form_for :custom_field, @custom_field, :url => custom_fields_path do |f| %>
<%= labelled_form_for :custom_field, @custom_field, :url => custom_fields_path, :html => {:id => 'custom_field_form'} do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<%= hidden_field_tag 'type', @custom_field.type %>
<%= submit_tag l(:button_save) %>
<% end %>
<%= javascript_tag do %>
$('#custom_field_field_format').change(function(){
$.ajax({
url: '<%= new_custom_field_path(:format => 'js') %>',
type: 'get',
data: $('#custom_field_form').serialize()
});
});
<% end %>

View File

@@ -0,0 +1 @@
$('#content').html('<%= escape_javascript(render :template => 'custom_fields/new', :layout => nil, :formats => [:html]) %>')

View File

@@ -1,6 +1,6 @@
<div class="contextual">
<%= link_to l(:label_document_new), new_project_document_path(@project), :class => 'icon icon-add',
:onclick => 'Element.show("add-document"); Form.Element.focus("document_title"); return false;' if User.current.allowed_to?(:manage_documents, @project) %>
:onclick => 'showAndScrollTo("add-document", "document_title"); return false;' if User.current.allowed_to?(:manage_documents, @project) %>
</div>
<div id="add-document" style="display:none;">
@@ -9,7 +9,7 @@
<%= render :partial => 'form', :locals => {:f => f} %>
<p>
<%= submit_tag l(:button_create) %>
<%= link_to l(:button_cancel), "#", :onclick => 'Element.hide("add-document")' %>
<%= link_to l(:button_cancel), "#", :onclick => '$("#add-document").hide(); return false;' %>
</p>
<% end %>
</div>

View File

@@ -1,7 +1,7 @@
<div class="contextual">
<% if User.current.allowed_to?(:manage_documents, @project) %>
<%= link_to l(:button_edit), edit_document_path(@document), :class => 'icon icon-edit', :accesskey => accesskey(:edit) %>
<%= link_to l(:button_delete), document_path(@document), :confirm => l(:text_are_you_sure), :method => :delete, :class => 'icon icon-del' %>
<%= delete_link document_path(@document) %>
<% end %>
</div>
@@ -17,7 +17,7 @@
<%= link_to_attachments @document %>
<% if authorize_for('documents', 'add_attachment') %>
<p><%= link_to l(:label_attachment_new), {}, :onclick => "Element.show('add_attachment_form'); Element.hide(this); Element.scrollTo('add_attachment_form'); return false;",
<p><%= link_to l(:label_attachment_new), {}, :onclick => "$('#add_attachment_form').show(); return false;",
:id => 'attach_files_link' %></p>
<%= form_tag({ :controller => 'documents', :action => 'add_attachment', :id => @document }, :multipart => true, :id => "add_attachment_form", :style => "display:none;") do %>
<div class="box">
@@ -28,7 +28,3 @@
<% end %>
<% html_title @document.title -%>
<% content_for :header_tags do %>
<%= stylesheet_link_tag 'scm' %>
<% end %>

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